[Top][All Lists]

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

Re: @task(hosts=[…]) yields Context but -H yields Connection?

From: Jeff Forcier
Subject: Re: @task(hosts=[…]) yields Context but -H yields Connection?
Date: Fri, 18 Sep 2020 15:52:48 -0400

Hi Daniel,

You're running into a few problems here. Most of them stem from this API needing more work - for example first class CLI and decorator support for Groups. However I'm getting back into Fabric dev after some inactivity and this is at or near the top of my list.

One thing to be aware of here is the defaulting of your hosts value to None - that makes the execution machinery think you haven't defined it at all, which is why it falls back to "must be a local task, here's a Context".

Unfortunately after that, the globals trick won't work (it will only update the value of target_group at runtime, but decorators operate at definition/load time - so by the time your 'pre task' runs the execution machinery has already slurped up the old None value.

And as noted, Groups need to be accepted in more places (this at least ought to be a quick fix so keep an eye out for a feature release sometime).

For the time being the least-awful workaround is to say "thanks but no thanks" to the Fabric-specific CLI bits and treat this like an Invoke + Fabric-the-API problem - which /also/ needs work on my end to be cleaner and scalable, but will at least function:

- Define some module level mapping of env names to Group objects
- Define your task function(s) with an 'env' or similar parameter, taking a name from that mapping
- Write the task(s) to load the given value out of that mapping, and use it to do the necessary work

For example:

envs = {'dev': Group('localhost'), 'prod': Group('www1', 'db1')}

def deploy(ctx, env='dev'):
    targets = envs[env]

$ fab deploy
$ fab deploy --env prod
Thanks & apologies,

On Wed, Sep 16, 2020 at 11:23 AM Daniel Middleton <d@monokal.io> wrote:
Python 3.8.2, Fabric 2.5.0, Paramiko 2.7.2, Invoke 1.4.1


I have a fabfile which needs to handle hosts passed at the command-line (using `-H`) and hosts defined in the fabfile if `-H` was not passed. Here's an example of the issue I'm facing:

target_group = None

def prod(ctx):
    _env_handler(ctx, "prod")

def _env_handler(ctx, env_name):
    global target_group

    if not hasattr(ctx, 'host'):
        target_group = Group("somehost1.tld", "somehost2.tld")

def test(ctx):

If I run `fab prod test`:

<Context: <Config: {'run': {'asynch ...

If I run `fab -H 1,2 test`:

<Connection host=1>
<Connection host=2>

So, passing hosts using the `@task(hosts=[...]` decorator produces a ctx `Context` object, and using `-H` produces a ctx `Connection` object.

I know using a task (`prod(ctx)`) to wrap environment logic may be questionable...but is there a way to ensure the task (`test(ctx)`) always receives a `Connection` object...or am I fundamentally misunderstanding something?


Edit: I've also tried directly passing a host list `(e.g. @task(hosts=["somehost1.tld", "somehost2.tld"]))` with the same result.

Jeff Forcier
Unix sysadmin; Python engineer

reply via email to

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