4

In my fabric scripts I have the following problem. I have a main task called autodeploy. Within this task I have some tasks that I only want to run once, locally. all remote tasks should run on each of the hosts of the host list.

env.roledefs ={
  'testing': ['t-server-01', 't-server-02']  
  'staging': ['s-server-01', 's-server-02']  
  'live': ['l-server-01', 'l-server-02']  
}

def localtask1():
  # download artifact

def localtask2(): 
  # cleanup locally

def remotetask():
  # deploy artifact to all hosts

def autodeploy():
  localtask1() # run this task only once, locally  

  remotetask() # run this task on all hosts

  localtask2() # run this task only once

The call is the following. I want to pass the role as an attribute.

fab -R test autodeploy
wodow
  • 3,871
  • 5
  • 33
  • 45
Michael Küller
  • 3,982
  • 4
  • 22
  • 42

3 Answers3

6

Use the execute function inside the wrapper function autodeploy, and specify a host list for the remote task.

For the other two you can call them with execute, like for the remote task, or directly. Use the local function inside them and you'll be fine, and not need to have ssh on localhost.

Docs are here for how best ot use the new execute function.

EDIT

Since you mention a different use case in the comments I'll mock up how you'd do that, from bits in the documentation given already, adding the param passing section

code:

#copy above

#redefine this one
def autodeploy(role_from_arg):
    localtask1()
    execute(remotetask, role=role_from_arg)
    localtask2()

#calls like fab autodeploy:testing
Morgan
  • 4,143
  • 27
  • 35
  • I changed the code in the question above, so you can see what I tried. Maybe I missed somethined, because it doesn't seem to work – Michael Küller Sep 19 '12 at 13:18
  • you did it backwards. execute(remotetask) is the one you really needed. I'll edit my answer to make it more clear. – Morgan Sep 19 '12 at 13:55
  • since I want to pass the role as an attribute, I don't see that this solution works for me. If I put @roles('testing') above a task, it will always run this task with the according hostlist – Michael Küller Sep 19 '12 at 14:07
  • mocked up some more, but you should go through the tutorial, and some more of the docs. Jeff (bitprophet) has done a great job writing the up for pretty much every part of the project's functionality. – Morgan Sep 19 '12 at 14:17
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/16882/discussion-between-morgan-and-michael-schmook) – Morgan Sep 19 '12 at 15:03
3

Use the runs_once decorator.

@runs_once
def localtask1():
    local('command')
Rob Seed
  • 464
  • 5
  • 4
  • The prevents `localtask1` from being used more than once, of course, so you can't do `fab localtask1 localtask1` anymore. – wodow Aug 18 '16 at 10:26
2

You can abuse the hosts decorator to force a single task to run once only, by specifying "localhost" as the host.

Example:

@fabric.decorators.hosts("localhost")
def localtask1():
    # download artefact
wodow
  • 3,871
  • 5
  • 33
  • 45
  • 2
    I'm using `@fabric.decorators.hosts("")`, since it matches what's used internally in fabric. The string in this task doesn't matter since it shouldn't be used. If you accidentally put a remote command in this it will fail. – Tim Ludwinski Jan 31 '17 at 16:56
  • @TimLudwinski Interesting! I see the convention in use at https://github.com/fabric/fabric/search?q=local-only&type=Code – wodow Feb 01 '17 at 11:25