“On 2017-05-01 I’ve updated this post to the current situation. Some things where outdated and where removed.”
In some earlier posts I’ve described how you can use Test Kitchen for testing Ansible Roles (This one and the one extending it.). Test Kitchen was created for testing Chef Cookbooks and like Chef, Test Kitchen is a Ruby application. On this page we describe an other tool for the same purpose. This tool is what you might see as a Python clone of Test Kitchen, but more specific to Ansible: Molecule (Github)
Molecule isn’t that old, only few years and when I browse the internet it is not yet really known in the community. Unlike Test kitchen with the many different drivers, Molecule supports several backends, like Vagrant, Docker, and OpenStack. With Molecule you can make use of Serverspec (Like Test Kitchen), but you can also make use of ‘Testinfra’. Testinfra is like Serverspec a tool for writing unit tests, but it is written in Python.
Lets dive into Molecule and create some tests for Molecule. On this page, we make use of the Docker backend and if you following this page please install docker.
Installing Molecule is really simple:
pip install molecule docker
Voila, it is installed. With the installation of Molecule, Testinfra is installed to. We had to provide the docker module as well, otherwise molecule doesn’t know how to connect to the docker daemon. We can configure a Ansible Role. I used my ‘zabbix-agent’ role as test case for the Test Kitchen setup, so I will use it again for Molecule.
When you haven’t created an Ansible role yet, instead of using the ansible-galaxy command you can use the following command:
molecule init --driver docker --role role_name
This will create just like the ‘ansible-galaxy’ some default directories and files, but also gives us a starting point with a few extra files for testing this role with Molecule.
When you already have a working module and want to make use of Molecule, please execute the following command:
molecule init --driver docker
This will install several files specific for Molecule. No worries, we can recreate these files manually. Lets do that in a role and see what the files do.
File: <root>/molecule.yml
--- ansible: playbook: playbook.yml driver: docker docker: containers: - name: zabbix-01 ansible_groups: - group1 image: debian image_version: latest privileged: True verifier: name: testinfra
This is the configuration file for Molecule. We specify which playbook Molecule will execute, in this case playbook.yml.
We specify that we want to make use of the Docker driver and that we have a docker container configuration. In this case, we only have 1 docker container specified. We use a Debian docker container with the ‘latest’ tag. We name the container ‘test-01’ and is in the group ‘group1’. And at last we configure molecule to use testinfra as the testtool.
File: <root>/playbook.yml
--- - hosts: all roles: - role: ansible-zabbix-agent
The playbook that is executed in the Docker container. This is a very basic one, we only have to specify the correct .
File: <root>/tests/inventory
localhost [group1] zabbix-01 ansible_connection=docker
The Ansible inventory file. Should be a known file to you 😉
File: <root>/tests/test_default.py
from testinfra.utils.ansible_runner import AnsibleRunner testinfra_hosts = AnsibleRunner('.molecule/ansible_inventory').get_hosts('all') def test_hosts_file(File): hosts = File('/etc/hosts') assert hosts.user == 'root' assert hosts.group == 'root'
(Edit 2016-09-14: As of release 1.9, the first 2 lines should be present in the TestInfra script.)
This is the test infra python file, containing the tests. After the init command, we have 1 test that will check if there is a hosts file, and the user and group of the file belongs to user ‘root’. We discuss this file later on by adding some more tests.
File: <root>/tests/test.yml
--- - hosts: localhost remote_user: root roles: - zabbix-agent-role
Now we have discussed the files.
We add some infra test checks in the ‘test_default.py’ file. We add the following 2 tests:
def test_zabbix_package(Package): zabbixagent = Package('zabbix-agent') assert zabbixagent.is_installed assert zabbixagent.version.startswith("1:3.0") def test_zabbixagent_running_and_enabled(Service): zabbixagent = Service("zabbix-agent") # assert zabbixagent.is_running assert zabbixagent.is_enabled
These are 2 Python function which are executed with Testinfra. With the first function, we validate if the package ‘zabbix-agent’ is installed. Also we check if the version starts with: 1:3.0. If you have some experience with testing Python code, this might be familiar to you. Test Infra uses ‘PyTest‘ to execute the tests and validate the.
The 2nd function we validate the ‘zabbix-agent’ service. We make sure the service is enabled. As you see, I’ve commented the check if the service is running. When it is enabled, I get this error message:
Failed to get D-Bus connection: Unknown error -1
Strange, because I’ve configured the privileged mode on the docker container. So maybe this is a bug or misconfiguration on my part, but for now I leave it commented and need to find a solution for this.
Within the molecule.yml we have to update the docker container configuration by adding the following property for all the docker containers:
required: True
Now we added it, we have to do a “molecule destroy” and start again. The container will be recreated and we won’t get an error message about the dbus.
Now we are ready to move on (I’m well aware that these 2 tests that I added will not be enough, I’ll add these myself later on).
Molecule has several subcommands, let run molecule -h and see what is available:
No handlers could be found for logger "vagrant" Usage: molecule [-hv] &amp;lt;command&amp;gt; [&amp;lt;args&amp;gt;...] Commands: check check playbook syntax create create instances converge create and provision instances idempotence converge and check the output for changes test run a full test cycle: destroy, create, converge, idempotency-check, verify and destroy instances verify create, provision and test instances destroy destroy instances status show status of instances list show available platforms, providers login connects to instance via SSH init creates the directory structure and files for a new Ansible role compatible with molecule Options: -h --help shows this screen -v --version shows the version
We first start with the ‘check’ command:
[vagrant@localhost ansible-zabbix-agent]$ molecule check No handlers could be found for logger "vagrant" playbook: playbook.yml [vagrant@localhost ansible-zabbix-agent]$ echo $? 0
Seems very well, the check commands validate if the playbook.yml doesn’t have any problems/syntax errors.
We can continue with the next command: create.
[vagrant@localhost ansible-zabbix-agent]$ molecule create No handlers could be found for logger "vagrant" Building ansible compatible image ... Step 1 : FROM debian:latest ---&amp;gt; 1b088884749b Step 2 : RUN bash -c 'if [ -x "$(command -v apt-get)" ]; then apt-get update &amp;amp;&amp;amp; apt-get install -y python sudo; fi' ---&amp;gt; Using cache ---&amp;gt; 8ef54383599a Step 3 : RUN bash -c 'if [ -x "$(command -v yum)" ]; then yum makecache fast &amp;amp;&amp;amp; yum update -y &amp;amp;&amp;amp; yum install -y python sudo; fi' ---&amp;gt; Running in 6d3142fa72aa ... Finished building molecule_local/debian:latest Creating container zabbix-01 with base image debian:latest ... Container created. [vagrant@localhost ansible-zabbix-agent]$
Now we have created a docker container where we can install our Ansible role on to, we do that with the ‘converge’ subcommand.
[vagrant@localhost ansible-zabbix-agent]$ molecule converge No handlers could be found for logger "vagrant" PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [zabbix-01] TASK [ansible-zabbix-agent : Include OS-specific variables] ******************** ok: [zabbix-01] TASK [ansible-zabbix-agent : Install the correct repository] ******************* skipping: [zabbix-01] ... RUNNING HANDLER [ansible-zabbix-agent : restart zabbix-agent] ****************** changed: [zabbix-01] PLAY RECAP ********************************************************************* zabbix-01 : ok=12 changed=7 unreachable=0 failed=0
Nice, the role is installed correctly without any issues on the container. With Test Kitchen we had to use BATS to validate if the Role is idempotent, but luckily molecule has just a simple sub command for it: idempotence
Well, it seems that the Role has passed the idempotence test:
[vagrant@localhost ansible-zabbix-agent]$ molecule idempotence No handlers could be found for logger "vagrant" Idempotence test in progress (can take a few minutes)... Idempotence test passed. [vagrant@localhost ansible-zabbix-agent]$
Testing the role is nicely going on right now, but we are not there yet. Now we need to use the ‘verify’ command to actually validate our role on the Docker container:
[vagrant@localhost ansible-zabbix-agent]$ molecule verify No handlers could be found for logger "vagrant" Trailing whitespace found in ./defaults/main.yml on lines: 35 Trailing newline found at the end of ./handlers/main.yml Trailing whitespace found in ./library/zabbix_host.py on lines: 29 Trailing newline found at the end of ./library/zabbix_hostmacro.py [vagrant@localhost ansible-zabbix-agent]$
Whoops, it seems it has found some issues. Let me fix that first, probably need to run the verify again after fixing it.
[vagrant@localhost ansible-zabbix-agent]$ molecule verify No handlers could be found for logger "vagrant" Executing testinfra tests found in tests/. ============================= test session starts ============================== platform linux2 -- Python 2.7.5, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 rootdir: /git/ansible/ansible-zabbix-agent/tests, inifile: plugins: xdist-1.14, testinfra-1.4.0 collected 3 itemss tests/test_default.py ... =========================== 3 passed in 0.63 seconds =========================== No serverspec tests found in spec/. [vagrant@localhost ansible-zabbix-agent]$
After fixing it, everything seems to work fine. Nice!
Now we are done with the container, so we can execute molecule again, but with the delete sub command and the container will be deleted.
These were the basics for testing an Ansible role with Molecule, Docker and Test Infra. This page uses the ‘Debian’ Docker image, whereas I normally use CentOS for this. I have some issues (Get the same error message when I enable the Test Infra test to validate if the service is running) to make this work on CentOS. So maybe Molecule isn’t mature enough yet, but it is getting there.
I’ll update my Ansible roles so it will use Molecule instead of Test Kitchen (No hard feelings ;-))
About “Failed to get D-Bus connection: Unknown error -1”, it’s probably because you have to run systemd as pid 1 to run the command “systemctl is-active $service”.
It’s systemd, you cannot escape from it
LikeLike
Pingback: Extending Ansible Role testing with Molecule by adding group_vars, dependencies and using travis ci – werner-dijkerman.nl
Pingback: Testing Ansible roles in a cluster setup with Docker and Molecule – werner-dijkerman.nl
Hi,
I get the following message when executing molecule:
I downloaded the ansible-zabbix-agent role from ansible-galaxy
molecule -v
1.8.4
I did the following steps
molecule syntax
–> Checking playbooks syntax …
playbook: playbook.yml
molecule converge
–> Starting Ansible Run …
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
fatal: [zabbix-01]: FAILED! => {“changed”: false, “failed”: true, “msg”: “unsupported parameter for module: gather_timeout”}
NO MORE HOSTS LEFT *************************************************************
to retry, use: –limit @playbook.retry
PLAY RECAP *********************************************************************
zabbix-01 : ok=0 changed=0 unreachable=0 failed=1
-> [1]
Which playbook this error comes from?
Regards,
Kevin
LikeLike
Hi,
I fixed this. It was a ansible installation issue.
Regards,
Kevin
LikeLike
Molecule now supports openstack as a driver, and a couple others in progress.
LikeLike
Yes, you are right. Will update the blog.
LikeLike
Also the syntax has changed:
“molecule init –role foo –driver docker” instead of “molecule init –docker”.
LikeLiked by 1 person
Yes, you are right. I think it is time to create a new blog because a lot of things happened since I wrote this page. 🙂
LikeLike