Operations vs. Tasks

In Aegir5, the low-level building blocks consist of Operation and Task entities. These can be associated with a variety of Aegir components, such as Deployment Targets, Projects, and Deployments. Operations are generally auto-created along with their higher level component, and in turn auto-create the Task entities from which they are composed.

Operations

Operations represent the actions that a user might want to trigger. Operations are configured to contain one or more Tasks. Each Task is a standalone fieldable entity. As a result each Task is responsible for gathering whatever data it needs to run its back-end tasks. The Operation includes these Tasks as inline entities.

When we trigger an Operation, we are presented with a form that includes all the fields from the various Tasks associated with that Operation. These values, across all of the related Tasks, are marshalled and sent to the back-end.

Logs are kept at the Operation level and so the output from the back-end gets piped back into a field on the Operation. In addition to the terminal output from the back-end, we also include the return code of the operation, so that we can record whether it succeeded or not.

Technically, an Operation is a bundle of a custom Entity Type which contains a lot of the logic to:

Tasks

Tasks represent relatively low-level reusable functionality. For example, we might have a Task for the Ansible back-end that is responsible for writing an Nginx vhost. We might then bundle that Task, along with others, in order to compose an “Install Drupal” Operation. Since the need to write a vhost is pretty common for web applications, that same Task might also be bundled into an “Install Joomla” Operation.

Another type of task is what we call an “action” task. These do not get bundled into Operations, but rather happen within a given page request. For example, instead of explicitly triggering a “password reset” task, as we do in Aegir 3, we can have a task dispatched when we click a “Log into site” button. This task runs a drush uli (via the Celery task queue), and relays the result directly back to the front-end, almost immediately. This can in turn redirect us directly to that URL. The result is that we are logged into the site without having to wait for a fully logged Operation to run.

From a technical perspective, a Task is a bundle of a custom Entity Type which contains the logic to:

Task Executors

Currently our backend dispatcherd assumes a single executable will be used for all Operations it receives. Operation::dispatch always queues a task called dispatcherd.ansible, which in turn presumes that ansible-playbook is the command to run, using the ansible Roles based on the Tasks included.

Shortly, we will refactor Operations and Tasks to:

  1. Allow for alternate Task Executors, initially a trivial bash command mechanism.
  2. Enable Tasks targeting the Ansible executor to specify ansible tasks within arbitrary roles, to allow for more flexibility and re-use of Ansible roles.

Composing Operations and Tasks

In the same way that Operations are made up of Tasks via inline entities, other entities such as Projects and Deployments are themselves made up of Operations embedded as inline entities. When we create a Project entity, we instantiate all of its Operations and related Tasks. Among other things, this allows us to reuse Tasks between Operations.

For example, when taking a backup, we will want to record the file path to the tarball. This data then becomes available to the restore task that may come later.

This case also illustrates the need for the back-end to be able to write data to the front-end. This is another function of relayd in the queue architecture. In addition to writing logs on Operation entities, it can write data to any field within any entity on the front-end.

This architecture largely mirrors how Aegir 3 works. However, it provides a more flexible, robust and scalable solution.

API Module

NB A lot of the baseline custom functionality used by Operations and Tasks is abstracted into the Aegir API module and associated classes. The business logic is often encapsulated into these classes which Operations and Tasks trivially subclass to provide the specific classes and annotations to hook into Drupal.