Buy

Installing Composer & the script Module

With our project cloned, the next step is obvious: use Composer to install our dependencies! Actually, we used the Composer module earlier from the command line. Google again for "Ansible Modules" and find the "All Modules" page. I'll find the composer module and open that in a new tab.

This module is really easy... except for one problem. Under Requirements, it says that Composer already needs to be installed. We have not done that yet... and, unfortunately, it can't be installed with apt-get.

Installing Composer Programmatically?

So how do you install it? Check out https://getcomposer.org and click "Download".

Normally, we just paste these lines into our terminal and celebrate! But... there's this problematic fine print at the bottom:

Do not redistribute the install code. It will change for every version of the install.

Huh. Composer includes a bit of built-in security: a sha hash to make sure that the installer hasn't been tampered with. If we tried to use these 4 commands in Ansible, it would work... for awhile. But next time the installer is updated, and that sha changed... it would stop working.

What to do? Check out that how to install Composer programmatically link. Eureka: a shell script that will safely download the latest version of Composer. The end result is a composer.phar file wherever we run this script from.

Installing Composer with script

Our mission is clear: somehow, execute this shell script via Ansible. But before we do that, near the top, add one new task: Install low-level utilities:

109 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 6
tasks:
... lines 8 - 24
- name: Install low-level utilities
... lines 26 - 109

Here, use the apt module and the with_items syntax to install zip and unzip:

109 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 6
tasks:
... lines 8 - 24
- name: Install low-level utilities
become: true
apt:
name: "{{ item }}"
with_items:
- zip
- unzip
... lines 32 - 109

Without these, Composer will run really slowly and you'll blame Jordi when you should be thanking him.

Now, back to our main job: how can we execute a script on a host? Why, with... the script module of course!

Runs a local script on a remote node after transferring it

Neato! We just point it at a local script, and it takes care of the rest. Go copy the script and, in our ansible directory, create a new scripts directory and a new file called install_composer.sh. Paste the code there:

17 lines ansible/scripts/install_composer.sh
#!/bin/sh
EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig)
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');")
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
then
>&2 echo 'ERROR: Invalid installer signature'
rm composer-setup.php
exit 1
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
exit $RESULT

Back in the playbook, at the bottom, create a new task: Download Composer:

109 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 6
tasks:
... lines 8 - 96
- name: Download Composer
... lines 98 - 109

Use the script module. Then, the easiest way to use this is to literally put the script filename on the same line as the module name: script: scripts/install_composer.sh:

109 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 6
tasks:
... lines 8 - 96
- name: Download Composer
script: scripts/install_composer.sh
... lines 99 - 109

Actually, every module can be used with a one-line syntax like this... but since line breaks are pretty cheap these days, I usually organize things a bit more.

Thanks to this task, we'll have a new composer.phar file in our home directory, which is where this task - well, all tasks - are running. But that's not enough: we need to move this to /usr/local/bin/composer.

Moving Composer Globally

Create another task: Move Composer globally. This time, use become: true and use the command module:

109 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 6
tasks:
... lines 8 - 99
- name: Move Composer globally
become: true
... lines 102 - 109

In your browser, go find the command module. Like with script, command has a short syntax. We'll say: command: mv composer.phar to /usr/local/bin/composer:

109 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 6
tasks:
... lines 8 - 99
- name: Move Composer globally
become: true
command: mv composer.phar /usr/local/bin/composer
... lines 103 - 109

If you're a little surprised that I'm using the command module instead of some built-in file or move module... me too! In general, you should always look for a built-in module first: they're always more powerful than using command. But sometimes, like with moving files, command is the right tool for the job.

Add one more task to make sure the file is executable: "Set Permissions on Composer" with become: true:

109 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 6
tasks:
... lines 8 - 103
- name: Set permissions on Composer
become: true
... lines 106 - 109

Remember, Ansible is all about state. The job of the file module isn't really to create files or symlinks. Instead, it's to make sure that they exist and have the right permissions. In this case, we're going to take advantage of the mode option to guarantee that the file is executable.

In the playbook, use the file module, set path to /usr/local/bin/composer and mode to "a+x" to guarantee that all users have executable permission:

109 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 6
tasks:
... lines 8 - 103
- name: Set permissions on Composer
become: true
file:
path: /usr/local/bin/composer
mode: "a+x"

Oh, and make sure the file you created is install_composer.sh.

Time to give this a try. Find your main machine's terminal and run the playbook!

ansible-playbook ansible/playbook.yml -i ansible/hosts.ini

Back on the VM, I'm in my home directory. And right now, it's empty. But if you're really fast, you can see the installation script doing its work. There it is: composer-setup.phar, composer-temp.phar, composer.phar and then it's gone once our task moves it. Yes!

And finally, we can type composer. Let's install some dependencies already!

Leave a comment!

  • 2017-05-17 weaverryan

    Hey jian su!

    There's no native rollback in Ansible. As you know, the purpose of the tasks/playbook is to put your server in a specific "state". If you don't have any tasks related to Composer suddenly, then your playbook basically doesn't have any opinion about the "state" of Composer. It does nothing :). If you *did* really care about the state or something, then you should actually add a task to make sure it's *not* installed. I'm not suggesting you do this - because you probably don't care enough - but that's the official answer :). For example, the apt module allows you to set the state to absent... which you would use if you actually wanted to make sure that something was *not* installed.

    And of course, you can always start from a fresh server. But any specific rollbacks are up to you (and kind of need to be - Ansible wouldn't natively know how to rollback the Composer installation anyways).

    Cheers!

  • 2017-05-17 jian su

    Hi guys:

    Is there way we can roll back the task. Say I don't need composer, and can I roll back to previous state. I tried to delete the task and run again, composer still there/: