Skip to content

Add a 'role' attribute to env.host_string #824

Closed
wants to merge 1 commit into from

5 participants

@flupke
flupke commented Jan 16, 2013

This allows to know the 'current' role in a task.

Here is an example use case:

from fabric.api import env, sudo
from fabric.decorators import roles

env.dedupe_hosts = False

env.roledefs = {
    'web': ['webhost.example.com'],
    'worker': ['webhost.example.com', 'workers.example.com'],
}

def prod():
    env.prog_name = {
        'web': 'webapp',
        'worker': 'webapp-worker',
    }

def preprod():
    env.prog_name = {
        'web': 'webapp-preprod',
        'worker': 'webapp-worker-preprod',
    }

@roles('web', 'worker')
def restart():
    prog_name = env.prog_name[env.host_string.role]
    sudo('supervisorctl restart %s' % prog_name)

If env.host_string had no 'role' attribute we would have to guess it by searching env.host_string in env.roledefs. It would not work in this case because 'webhost.example.com' is present in both 'web' and 'worker' roles.

In the example above, the restart task will be executed 3 times, with env.host_string.role taking values ['web', 'worker', 'worker'].

I included a test in the commit, let me know if you accept the pull request and I will write documentation too.

@flupke flupke Host strings now have a 'role' attribute.
This allows to know the 'current' role in a task (with
env.host_string.role), i.e. from which role the current host has been
selected.
47650dc
@bitprophet
Fabric member

I appreciate the patch & understand the desire to 'tell users what role(s) is in play' -- but unfortunately I'm not a big fan of using an attribute on the host string (trying to get away from that pattern for the future) and more importantly, I'm not sure this particular approach (re: defining the 'active' role) would be intuitive/accurate for all users/use cases.

Long story short, the way roles are designed & used, there is no truly reliable way to tell what role is "currently" being "run"; the ordering stuff you're keying off of is more an implementation detail than anything :(

I have plans to redo all the hosts/roles stuff in version 2.0 and am hoping to avoid piling more conceptual baggage onto Fab 1.x in the meantime. So again, thanks, but I'm not going to merge this to mainline.

@bitprophet bitprophet closed this Jan 28, 2013
@flupke
flupke commented Jan 28, 2013

Yes I am not a big fan of my implementation either, adding attributes to str is kinda ugly, but since hosts are "flattened down" before tasks execution I can't think of another way.

Still I think it's a really important feature. Here is the code above without it:

from fabric.api import env, sudo, execute
from fabric.decorators import roles

env.dedupe_hosts = False

env.roledefs = {
    'web': ['webhost.example.com'],
    'worker': ['webhost.example.com', 'workers.example.com'],
}

def prod():
    env.prog_name = {
        'web': 'webapp',
        'worker': 'webapp-worker',
    }

def preprod():
    env.prog_name = {
        'web': 'webapp-preprod',
        'worker': 'webapp-worker-preprod',
    }

@roles('web')
def restart_web():
    prog_name = env.prog_name['web']
    sudo('supervisorctl restart %s' % prog_name)

@roles('worker')
def restart_worker():
    prog_name = env.prog_name['worker']
    sudo('supervisorctl restart %s' % prog_name)

def restart():
    execute(restart_web)
    execute(restart_worker)

You can't factorize identical tasks across roles. This can become very ugly in large fabfiles and make them unmaintainable. The alternative is to generate tasks dynamically, but it's even uglier.

@DXist
DXist commented Mar 22, 2013

+1 for current role feature

If task is only for single role you can use decorator like

from fabric.decorators import _wrap_as_new

def role(role):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            env.role = role
            return func(*args, **kwargs)

        wrapped = roles(role)(wrapper)

        return _wrap_as_new(func, wrapped)

    return decorator
@exhuma
exhuma commented Jul 18, 2013

Why not simply using fabric.api.env.roles inside your task? This existed already as early as 1.4.2.

@flupke
flupke commented Jul 18, 2013

@exhuma It works as long as you don't have overlap between your roles. If a host is part of 2 roles, once you're inside the task you can't determine the current role.

@mungayree

flupke, Seem your example of prepod, prod apps do not work ad give attribute errors on latest fabric modules 1.11.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.