Getting started

A first Droid project

Let's see how to use Droid to set-up a web server. To begin we will create a new Composer project and install Droid along with a number of standard Droid commands:-

$ composer init -n
$ composer require --no-dev droid/droid-standard

Next we will create a new Droid project by creating a droid.yml file with the following contents:-

project:
    name: "My first Droid project"

targets:
    make-webserver:
        tasks:
            -
              name: "Install Apache2"
              command: "apt-get:install"
              sudo: true
              arguments:
                  package: "apache2"

And now we may execute the tasks of the make-webserver Target:-

$ vendor/bin/droid make-webserver

Upon which we should see:-

Droid: Running target `make-webserver`
Task `Install Apache2`: apt-get:install package=apache2  locally
localhost apt-get install -y apache2
... (much output) ...
localhost  * Starting web server apache2
...
Result: 0
--------------------------------------------
$

We have installed the Apache 2 package and its dependencies by running apt-get install -y apache2 on the local host. We did this by writing a droid.yml Yaml configuration file which declares a Droid project and a Target which we named make-webserver. A Target is like a recipe or a script and defines a number of steps that Droid must take to achieve a desired outcome. We have defined one Task, which we named "Install Apache2", to invoke the apt-get:install Droid command. We can see that apt-get:install takes one argument, package, by invoking the Droid help command:-

$ vendor/bin/droid help apt-get:install
Usage:
  apt-get:install <package>

Arguments:
  package               Name of the package(s) to install

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Help:
  Installs package(s) through apt-get on Debian based systems

Finally, we declared that we desire the command to be executed with elevated privileges by setting sudo: true. The output generated during execution of the make-webserver Target begins by displaying the name of the Target and then information about and output from each of the Tasks defined by the Target.

Now suppose we want to install Apache 2 on two remote machines: let us see how to use Droid Inventory to manage remote installation.

Inventory

Droid is able to execute Commands on remote hosts with a little extra configuration, but first it needs us to prepare the hosts by installing PHP, setting-up SSH public key authentication and granting password-less sudo privileges for the commands we wish to execute. Please see how to enable remote command execution for more on this.

Let's assume that our remote hosts are suitably prepared; we will need to provide Droid with the name of a user account on the hosts and paths to private ssh key files. We will add two top-level directives, groups and hosts, to our droid.yml:-

groups:
    webservers:
        hosts:
            - web-01
            - web-02
hosts:
    web-01:
        public_ip: "198.51.100.5"
        username: "c3po"
        keyfile: "/path/to/c3po_id_rsa"
    web-02:
        public_ip: "198.51.100.6"
        username: "r2d2"
        keyfile: "/path/to/r2d2_id_rsa"

We have declared two hosts which we named web-01 and web-02 and we grouped them under the name webservers. Now we may declare that our make-webserver Target should execute remotely at these hosts:-

targets:
    make-webserver:
        hosts: webservers
        tasks:
            -
              ...

We invoke Droid as before:-

$ vendor/bin/droid make-webserver

and we shall see:-

Droid: Running target `make-webserver`
Task `Install Apache2`: apt-get:install on webservers
Host web-01: package=apache2
Host web-02: package=apache2
web-01 Begin droid enablement.
web-01 Finished droid enablement. Success.
web-02 Begin droid enablement.
web-02 Finished droid enablement. Success.
web-01 apt-get install -y apache2
web-02 apt-get install -y apache2
... (much output) ...
web-01  * Starting web server apache2
... (much output) ...
web-02  * Starting web server apache2
...
Result: 0
--------------------------------------------
$

We see that Droid has installed Apache 2 on the two hosts in our Inventory. In order to complete each Task, Droid on our local machine will invoke the corresponding Droid command on the hosts in our Inventory. Droid will first make sure that it is installed, at the same version number and with the same available commands, on those hosts.

Going further: configure Apache

Let's go further and set-up two Apache virtual hosts on both of our web servers. For each virtual host we will install a virtual host config file, create a document root directory, deploy an index page and enable the virtual host. Finally, we will trigger Apache to reload its configuration.

We begin by creating a directory for the file assets in our Droid project:-

$ mkdir assets

We create a basic HTML index page, assets/index.html, with the following content:-

<html>
<head><title>Coming soon!</title></head>
<body><p>Coming soon!</p></body>
</html>

and a template for the virtual host config file, assets/vhost.conf.template, with the following content:-

# {{ item.name }}
<VirtualHost *:80>
  ServerName "{{ item.name }}"
  DocumentRoot "{{ item.docroot }}"
  <Directory "{{ item.docroot }}">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Require all granted
  </Directory>
  ErrorLog "/var/log/apache2/{{ item.name }}_error.log"
  CustomLog "/var/log/apache2/{{ item.name }}_access.log" combined
</VirtualHost>

This vhost config template contains placeholders {{ item.name }} and {{ item.docroot }} which will be replaced with concrete values. We will add to our droid.yml a Target variable in which to declare the concrete values:-

targets:
    make-webserver:
        hosts: webservers
        variables:
            websites:
                -
                  name: "one.example.com"
                  docroot: "/var/www/one.example.com"
                -
                  name: "another.example.com"
                  docroot: "/var/www/another.example.com"
        tasks:
            -
              ...

We add a Task to the make-webserver Target to write the vhost config files to our web server hosts:-

        tasks:
            -
              ...
            -
              name: "Copy site config to sites-available"
              command: "fs:copy"
              sudo: true
              arguments:
                  src: "@assets/vhost.conf.template"
                  dest: "/etc/apache2/sites-available/{{ item.name }}.conf"
              with_items: websites

Here we use the fs:copy command to copy the content of our template to /etc/apache2/sites-available. The @ character at the beginning of the src argument indicates that Droid should substitute the file path with the contents of that file and, because the file is a template, it should replace placeholders in the content with concrete values. The with_items directive instructs Droid to perform a Task for each item in the list of items declared at the named variable: our Target variable websites in this case. Droid makes available a special variable named item containing the values of the current item and this is how Droid is able to replace the {{ item.name }} and {{ item.docroot }} placeholders.

Next we will add tasks to create the document root directories and assign ownership and permissions:-

        tasks:
            -
              ...
            -
              name: "Create the document root directory"
              command: "fs:mkdir"
              sudo: true
              arguments:
                  directory: "{{ item.docroot }}"
              with_items: websites
            -
              name: "Change ownership of the document root directory"
              command: "fs:chown"
              sudo: true
              arguments:
                  file: "{{ item.docroot }}"
                  user: "root"
                  group: "www-data"
              with_items: websites
            -
              name: "Change permissions of the document root directory"
              command: "fs:chmod"
              sudo: true
              arguments:
                  filename: "{{ item.docroot }}"
                  mode: "2750"
              with_items: websites

We deploy our HTML index page:-

        tasks:
            -
              ...
            -
              name: "Copy a placeholder index page to the document root directory"
              command: "fs:copy"
              sudo: true
              arguments:
                  src: "@assets/index.html"
                  dest: "{{ item.docroot} }}/index.html"
              with_items: websites

The @ symbol at the beginning of the src argument instructs Droid to substitute the file path with the content of that file without replacing placeholders in the content.

Our final Task is to enable our Apache virtual hosts and trigger a configuration reload:-

        tasks:
            -
              ...
            -
              name: "Enable the vhost configuration"
              command: "apache:ensite"
              sudo: true
              arguments:
                  site-name: "{{ item.name }}"
              with_items: websites
              trigger: "Reload the Apache2 service"
        triggers:
            -
              name: "Reload the Apache2 service"
              command: "service:reload"
              sudo: true
              arguments:
                  name: "apache2"

Some Droid commands are able to report when they have made a change of some kind. Tasks which invoke such commands can declare a trigger which is a special Task that will be run after all other Tasks have completed and only when the triggering command reports that a change was made. In this example the service:reload command is executed only when a virtual host was not already enabled.

Our project directory now looks like this:-

assets/index.html
assets/vhost.conf.template
composer.json
composer.lock
droid.yml
vendor/

We may now execute our Target exactly as we did before:-

$ vendor/bin/droid make-webserver

Further reading