I have a monolithic Ansible playbook that contains dozens of different roles, all bundled into the same Git repository. Some of the roles are more generically useful than others, so I thought I would do some refactoring.
I decided to move the role that installs and configures fail2ban to its own repository, and then call that new/refactored role as a dependency in my now-slightly-less-monolithic role.
Of course, I had no idea what I was doing.
The first thing I tried was creating a simple ansible-test playbook with a single test role. Just to make sure it did something, I had it copy a file to the /tmp directory on the target host. In roles/test/meta/main.yml
I added a dependency:
---
dependencies:
- { role: "git+https://git.osric.net/chris/ansible-fail2ban.git" }
...
Then I tried running the playbook:
$ ansible-playbook -i hosts site.yml
Which produced the following error:
ERROR! the role 'git+https://git.osric.net/chris/ansible-fail2ban.git' was not found in /home/chris/ansible-test/roles:/usr/local/etc/ansible/roles:/home/chris/ansible-test/roles:/home/chris/ansible-test
The error appears to have been in '/home/chris/ansible-test/roles/test/meta/main.yml': line 4, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
dependencies:
- { role: "git+https://git.osric.net/chris/ansible-fail2ban.git" }
^ here
This one looks easy to fix. It seems that there is a value started
with a quote, and the YAML parser is expecting to see the line ended
with the same kind of quote. For instance:
when: "ok" in result.stdout
Could be written as:
when: '"ok" in result.stdout'
Or equivalently:
when: "'ok' in result.stdout"
That’s a long and, as it turns out, misleading error. There is no issue with unbalanced quotes. The real error is on the first line:
ERROR! the role 'git+https://git.osric.net/chris/ansible-fail2ban.git' was not found
Isn’t is supposed to automatically install? Isn’t that the magic of dependencies?
Apparently not. The convention, as far as I can tell, is to use a requirements.yml
file that describes where to find the dependencies, e.g.:
---
src: git+https://git.osric.net/chris/ansible-fail2ban.git
...
Then you use ansible-galaxy
to install the dependencies:
$ ansible-galaxy install -r requirements.yml -p roles/
It worked! I now had a new role in roles/ansible-fail2ban
with the expected content.
It turned out that I had left out an important piece of the ansible-fail2ban
role (I forgot to add
, commit
, and push
the defaults/main.yml
into the remote origin).
I tried re-installing the repository using the --force
option:
$ ansible-galaxy install --force -r requirements.yml -p roles/
- ansible-fail2ban is already installed, skipping.
Wait, what? What is --force
actually supposed to do? It turned out, this was a bug in my version of ansible
(and ansible-galaxy
), as described in this bug: https://github.com/ansible/galaxy-issues/issues/249
Before:
$ ansible-galaxy --version
ansible-galaxy 2.3.1.0
After:
$ ansible-galaxy --version
ansible-galaxy 2.4.3.0
I ran the install again:
$ ansible-galaxy install --force -r requirements.yml -p roles/
- changing role ansible-fail2ban from to unspecified
- extracting ansible-fail2ban to /home/chris/ansible-test/roles/ansible-fail2ban
- ansible-fail2ban was installed successfully
It worked! But the first output line about that bit “changing role ansible-fail2ban from to unspecified
” led me to believe that I could change the role destination. I’d rather have roles/fail2ban
than roles/ansible-fail2ban
.
We can specify additional attributes, including name
. See Ansible Galaxy – Installing Multiple Roles from a File for details.
[Pet peeve: documentation that does not include the most verbose & complete example possible. Every single option should be demonstrated. Why not include both? Here is the simplest possible example that works, here is the most complete example possible. This is open source, so I could fix it I suppose….]
I updated my requirements.yml
file:
---
- name: fail2ban
scm: git
src: git+https://git.osric.net/chris/ansible-fail2ban.git
version: master
...
I installed the requirements again:
$ ansible-galaxy install --force -r requirements.yml -p roles/
Which successfully created an additional copy of the role in the specified directory:
$ ls roles | cat
fail2ban
ansible-fail2ban
test
That meant I also needed to update the dependency in roles/test/meta/mail.yml
:
---
dependencies:
- { role: fail2ban }
...
I ran the test playbook again. It worked! Now I just have a couple dozen more roles to refactor.