fab-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Fab-user] [PATCH] Fix #7: Implement "something like roles."


From: Jeff Forcier
Subject: Re: [Fab-user] [PATCH] Fix #7: Implement "something like roles."
Date: Thu, 6 Nov 2008 15:48:56 -0500

One potential problem with this approach is that it makes it very easy
to make mistakes; if the only differentiation between
strings-as-rolenames and strings-as-hostnames is whether you use
(string) varargs or a list argument, I can see a lot of people (yours
truly possibly included :)) accidentally doing one when they mean the
other.

Making matters worse is that such a mistake would be very hard to
catch and provide a meaningful error message for, which IMHO is the
only saving grace for potentially confusing features -- if you can
catch the obvious mistake easily and thus not waste as much of the
user's time, that's usually Good Enough.

I'd personally prefer to do what I think Cap does, and provide both
@hosts and @roles to lessen the ambiguity (where @roles would do
exactly what your patch does -- just lets you refer to the name of a
config variable which is a list of hostnames). I do not recall what
Cap does to allow mixing of roles and explicit server names (or IF it
does so) but it'd definitely be good to allow mixing the two if at all
possible.

-Jeff

On Thu, Nov 6, 2008 at 3:32 PM, Christian Vest Hansen
<address@hidden> wrote:
> Actually, what this gives is a way to simulate roles.
>
> We allow fab_hosts (and other places) to be a string rather
> than a list of hosts. This string will then be used to
> look up a variable that contains the real list of hosts to
> connect to.
>
> So we can have fabfiles like this:
>
> config.servers = ['localhost', '127.0.0.1']
> def prod():
>  config.servers = ['...', '...']
> @hosts('servers')
> def deploy():
>  run('stuff on $(fab_host)')
>
> Here, we are allowed to define as many variables with lists
> of hosts as we want, and we can change them any time we want.
> ---
>  TODO                         |    3 ---
>  doc/samples/roles/fabfile.py |   14 ++++++--------
>  fabric.py                    |   18 +++++++++++++-----
>  3 files changed, 19 insertions(+), 16 deletions(-)
>
> diff --git a/TODO b/TODO
> index 183a74d..ea4969e 100644
> --- a/TODO
> +++ b/TODO
> @@ -1,7 +1,4 @@
>  1   Ctrl-C dosn't quite work... especially not with the tail example
> -7   need something like roles in Capistrano
> -    - I'm thinking the ability to work with variable environments as
> first class
> -      objects
>  9   make parallel submode work
>  11  add built-in help for built-in variables
>     - and modes and submodes
> diff --git a/doc/samples/roles/fabfile.py b/doc/samples/roles/fabfile.py
> index 169fa8a..fd54c53 100644
> --- a/doc/samples/roles/fabfile.py
> +++ b/doc/samples/roles/fabfile.py
> @@ -1,27 +1,25 @@
>
>  # Our server roles:
> -rdbms = ['127.0.0.1']
> -httpd = ['localhost']
> +config.rdbms = ['127.0.0.1']
> +config.httpd = ['localhost']
>
>  def production():
>     # this would set `rdbms` and `httpd` to prod. values.
>     # for now we just switch them around in order to observe the effect
> -    global httpd, rdbms
> -    rdbms, httpd = httpd, rdbms
> -    # ... except, this dosn't actually work :(
> +    config.rdbms, config.httpd = config.httpd, config.rdbms
>
>  def build():
>     local('echo Building project')
>
> address@hidden(*rdbms)
> address@hidden('rdbms')
>  def prepare_db():
>     run("echo Preparing database for deployment")
>
> address@hidden(*httpd)
> address@hidden('httpd')
>  def prepare_web():
>     run("echo Preparing web servers for deployment")
>
>  @depends(prepare_db, prepare_web)
> address@hidden(*httpd)
> address@hidden('httpd')
>  def deploy():
>     run("echo Doing final deployment things to $(fab_host)")
> diff --git a/fabric.py b/fabric.py
> index 952f94b..c1ae55a 100644
> --- a/fabric.py
> +++ b/fabric.py
> @@ -1250,21 +1250,29 @@ def _args_hash(args):
>     return hash(tuple(sorted(args.items())))
>
>  def _execute_at_target(command, args):
> -    ENV['fab_local_mode'] = getattr(command, 'mode', ENV['fab_mode'])
> -    ENV['fab_local_hosts'] = getattr(command, 'hosts', ENV.get('fab_hosts'))
> +    mode = ENV['fab_local_mode'] = getattr(command, 'mode', ENV['fab_mode'])
> +    hosts = ENV['fab_local_hosts'] = getattr(
> +        command, 'hosts', ENV.get('fab_hosts'))
> +    if hosts and len(hosts) == 1 and isinstance(hosts[0], basestring):
> +        # we allow the hosts to be a string-alias for another variable
> +        # that will contain the real array of hosts to connect to.
> +        host_alias = _lazy_format(hosts[0])
> +        hosts = ENV.get(host_alias)
> +        print "Resoling host alias", host_alias, "to", (', '.join(hosts))
> +        ENV['fab_local_hosts'] = hosts
>     # Determine whether we need to connect for this command, do so if so
>     if _needs_connect(command):
>         _check_fab_hosts()
>         _connect()
> -    if ENV['fab_local_mode'] in ('rolling', 'fanout'):
> +    if mode in ('rolling', 'fanout'):
>         print("Warning: The 'rolling' and 'fanout' fab_modes are " +
>               "deprecated.\n   Use 'broad' and 'deep' instead.")
>         ENV['fab_local_mode'] = 'broad'
>     # Run command once, with each operation running once per host.
> -    if ENV['fab_local_mode'] == 'broad':
> +    if mode == 'broad':
>         command(**(args or {}))
>     # Run entire command once per host.
> -    elif ENV['fab_local_mode'] == 'deep':
> +    elif mode == 'deep':
>         # Gracefully handle local-only commands
>         if CONNECTIONS:
>             for host_conn in CONNECTIONS:
> --
> 1.6.0.1
>
> _______________________________________________
> Fab-user mailing list
> address@hidden
> http://lists.nongnu.org/mailman/listinfo/fab-user
>
>




reply via email to

[Prev in Thread] Current Thread [Next in Thread]