Using Molecule to Test Ansible Roles
Gain Confidence in your Ansible Roles by Writing Tests
Prerequisites
If you wish to use Molecule for local development, and use a Docker container to test your roles on; you will of course need docker installed locally. I am using docker-for-mac; which works really well.
Setting up your environment
I like to use python’s virtualenv to separate concerns and keep each working environment I may be using, isolated.
$ virtualenv --no-site-packages venv
$ source venv/bin/activate
Once activated; install molecule
$ pip install molecule
This should also install Ansible; and various other dependancies.
You might need the Docker driver for Molecule also, so install that too.
$ pip install molecule[docker]
Use Molecule to create a new role
$ molecule init role -d docker -r ansible.role.example
This will create a generic role structure from ansible-galaxy
as well as an additional molecule
directory, from which all the tests and setup for molecule will be kept.
Although I have specified the docker driver here using -d docker
it is in fact the default driver, if non were specified.
If you look inside the molecule directory you will see it has given you a Dockerfile.j2
from which molecule will use to spin up a new docker container to test your task from.
As molecule uses ansible-galaxy to create the templated role, there are metadata checks in place. Within meta/main.yml
there are 5 sections that need to be filled in
- author
- description
- company
- license
- platform
Otherwise the molecule lint
sequence will fail. Alternatively you can rename the yml file to something else for now.
$ mv meta/main.yml meta/main
Running a Molecule test
Before writing anything, run a molecule test and see what the output is.
$ cd ansible.role.example
$ molecule test
--> Validating schema /Users/keirwhitlock/development/ansible.role.example/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix└── default
├── lint
├── dependency
├── cleanup
├── destroy
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
├── cleanup
└── destroy--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in /Users/keirwhitlock/development/ansible.role.example/...
Lint completed successfully.
I’ve cut this short to save screen space; but you should of seen molecule spin up a container, run its test(s) and then delete the container again.
Now that you have molecule up and running and a base role created, you can no go ahead and start adding your tasks as you normally would have, and you can run molecule test
at anytime to get molecule to spin up a test box and try out your roles tasks.
If you are continuously iterating your development of your role, you might not want the docker container to constantly be created and destroyed each time you run molecule test
. If this is the case you can use molecule converge
instead, which will leave your container running. Once you are done you can issue the molecule destroy
command to delete it.
NB: The molecule.yml
is used to setup the environment for molecule to use, and the default image for docker to spin up is centos:7
; so you will need to edit this if this is incorrect for you.
Add some tasks to your role
Just to illustrate how you might use molecule for your development. I’m going to add a basic task to my ansible.role.example
role.
So within tasks/main.yml
I’m going to add a task to install httpd
.
---
# tasks file for ansible.role.example- name: install apache package
package:
name: httpd
state: present
Run a converge to test
$ molecule converge
and you will see that molecule is using our role on the docker test box, and installing httpd
as intended.
--> Action: 'converge'PLAY [Converge] ****************************************************************TASK [Gathering Facts] *********************************************************
ok: [instance]TASK [ansible.role.example : install apache package] ***************************
changed: [instance]PLAY RECAP *********************************************************************
instance : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
But how can we be extra certain the package is installed correctly?
Writing Tests
Molecule out the box uses testinfra to allow users to write python tests to check the test systems they are applying their Ansible roles to, for correctness.
Within your default scenario, within the molecule directory there is a python script where you can add your tests.
molecule/default/tests/test_default.py
Inside you will see that there is already a example test there for you, which is checking for the existence of the /etc/hosts
file. This is the 1 test you may have noticed in the molecule test
output earlier.
Testinfra comes out the box with various modules that can be used for testing your Ansible with. As we have just installed the httpd
package, lets test that this exists. Testinfra has a package class for this very thing.
Adding a simple test for this is simply
def test_httpd_installed(host):
assert host.package("httpd").is_installed
Now lets run the verify
command to run our tests and hopefully things should pass.
Good! Now we have 2 tests that are passing. Just for illustration I’ll show you a snippet of the output when the tests do not pass.
Here I have changed the test to look for apache
instead of httpd
and as a result our test has failed.
Conclusion
So as you can see from my quick example; you can use Molecule to help you in the process of developing an Ansible role. Spinning up a new container to test your role’s tasks against, and even the ability writing additional tests, (which can be as simple or as complex as you feel you need), in order to assure yourself that your role is configuring your systems as you expect.
Next time, I’m going to take the process one step further, and have Gitlab-CI detect updates to the role repositories and conduct a test run for us.