saltstack / salt

Software to automate the management and configuration of any infrastructure or application at scale. Get access to the Salt software package repository here:
https://repo.saltproject.io/
Apache License 2.0
14.15k stars 5.47k forks source link

Salt ignores dunder outputter in custom module if a function is decorated #8393

Closed malinoff closed 10 years ago

malinoff commented 10 years ago

I'm not sure, is this a bug or not, but I think salt should handle such things.

$ cat _modules/project.py

# -*- coding: utf-8 -*-
'''
Module to deploy projects.
'''

import logging

log = logging.getLogger(__name__)

__outputter__ = {
    'run': 'highstate'
}

def __virtual__():
    return 'project'

def _parse_params(kwargs):
    '''
    Returns projectID and environment from the salt target
    if runs from the master, takes them from grains if runs locally.
    '''
    if kwargs:
        tgt = kwargs['__pub_tgt']
        components = [x.strip().lstrip('G@').split(':')
                      for x in tgt.split('and') if x]
        params = dict(components)
    else:
        params = __grains__
    return params['project'], params['env']

def _pre_run(frun):
    def wrapped(step, **kwargs):
        if step == 'configure':
            __salt__['saltutil.refresh_pillar']()
        return frun(step, **kwargs)
    return wrapped

@_pre_run
def run(step, **kwargs):
    '''
    Runs specified step in a project's specified environment.
    '''
    proj, env = _parse_params(kwargs)
    ret = __salt__['state.sls']('{0}.{1}'.format(proj, step),  # NOQA
                                **kwargs)
    return ret

I use it this way: salt -C 'G@project:project-id and G@env:live' project.run deploy, salt -C 'G@project:project-id and G@env:live' project.run configure and so on.

I want to get a properly formatted result, so I try to use __outputter__, but it's not working. If I comment _pre_run decorator, it works.

Also, calling salt -C 'G@project:project-id and G@env:live' project.run configure --out=highstate works as expected.

malinoff commented 10 years ago

Alright, the problem is because salt looks for a function name. It finds wrapped instead of run - and there is no wrapped in __outputter__.

To be able to do such things, I have two ways, as I see:

1) Dynamically change function name
def _pre_run(frun):
    def wrapped(step, **kwargs):
        if step == 'configure':
            __salt__['saltutil.refresh_pillar']()
        return frun(step, **kwargs)
    wrapped.func_name = 'run'
    return wrapped
2) Inner function must have the same name as outter
def _pre_run(frun):
    def run(step, **kwargs):
        if step == 'configure':
            __salt__['saltutil.refresh_pillar']()
        return frun(step, **kwargs)
    return run

But if I'll have more functions in my module, both of approaches will fail. Uh, figured out the third way:

def _pre_run(frun):
    def wrapped(step, **kwargs):
        if step == 'configure':
            __salt__['saltutil.refresh_pillar']()
        return frun(step, **kwargs)
    wrapped.func_name = frun.func_name
    return wrapped

But this is ugly.

s0undt3ch commented 10 years ago

Here's how we do it:

https://github.com/saltstack/salt/blob/develop/salt/utils/decorators/__init__.py#L154

malinoff commented 10 years ago

Thanks!