An Advanced Method to Define Tasks in Fabric

One of the best things about Fabric is its simplicity. For example, in order to execute a command on a remote host, just define a task:

Then, call it with a fab command.

When I started to use Fabric, its simplicity of just listing commands helped me to easily transfer shell scripts or procedure manuals that I used to write.

In addition to the method to define a task with a task decorator, the document also provides a method to use Task subclasses. Let’s borrow an example from the document. If you define a subclass: 

It is equivalent to below which is defined with a decorator.

Internally, the task decorator also creates a Task subclass called WrappedCallableTask. If you just look at the above example, the decorator method seems shorter and simpler. However, the subclass method will be useful when you want to: 

  • share a task across multiple services
  • create a number of programmed tasks
  • effectively switch between environments

Let’s look at how these functions work.

Sharing Tasks Across Multiple Services

When sharing middleware or application frameworks within a team, you may often want to use a task used for one service in other services. At Nulab, we are operating four services, Backlog, Cacoo, Typetalk and Nulab Account, and there are some tasks we want to share in those services.

In such cases, you can create a library with defined task classes.

Then, in every service, define the task like this:

You will be able to repeat the task without copying and pasting it. If one service has specific processing, you can avoid writing duplicates by simply extending a class.

This is an advantage of object-oriented programming.

fabfile Gets Messy with Start/Stop Tasks

One of the major usages of Fabric is to start, stop and restart middleware. These tasks are simple yet they can sometimes complicate a fabfile if there are too many of them, e.g.

There are only three tasks defined in this example. However, if you also have other types of middleware like memcached and tomcat, the fabfile will get messy with a number of similar tasks. As a result, you may accidentally overwrite a task or forget to copy and paste it when adding a new task.

In such a case, you can use a utility method that auto-generates tasks. In the following example, a wrapper function to generate a Task class is obtained by calling a task decorator as a function.

Now, you can just call “create_tasks” method as follows, and stop/start/restart tasks will be auto-generated.

They are now recognized as tasks.

Switching Tasks for Each Environment

You may often use multiple environments like production and staging environments in a single service. In such cases, the following technique is commonly used.

As shown above, a task to switch environments is called first and then followed by the task you actually want to execute.  In a “switch” task, you can change environment specific values like host names defined in env.roles or other variables like AWS region that you have set for your own purpose.

If operations performed in “sometask” are the same in both environments, you probably will not encounter any difficulties. If there is a difference in operations between staging and production environments, you can usually use “if condition” to switch the operations like this.

 However, the presence of similar processing in each task will make a program complex and difficult to read if:

  • there are many tasks that need switching for each environment, or
  • you want to always perform the same operation in specific environments.

If you create FactoryTask to wrap function calling, you will have the advantage of being able to:

  • avoid writing “if-condition” in all tasks
  • gather all the operations that are frequently used in a specific environment in FactoryTask.

To call the above:

In this case, to illustrate the difference, I used different arguments for production and staging and left the execution log with a logger for production.

As mentioned earlier, the method to subclass Task will allow you to make a program easy to understand without copying and pasting it, thus making it handy to: 

  • share a task across multiple services,
  • create a number of programmed tasks
  • effectively switch between environments.

All the sample codes provided in this post are available at:

All the tasks provided in this post can be executed after installing the required tools (Vagrant, Ansible and Fabric) listed in the above link and run the following commands. Try them and see how it works for yourself!

Another advantage of Fabric is its capacity for these advanced functions in spite of its simplicity. It’s wonderful to have tools like Vagrant and Ansible which enable us to easily share a testing environment for this kind of tools.

Work better, together.

Collaboration tools for modern teams

View Products