Molecule provides a clean way of testing Ansible roles which you write. Molecule can spin a docker container and run your Ansible role on it and report back the test results. In this blog I'm going to demonstrate a simple use case of Molecule to test a simple Ansible role on a Docker container.
Now the role is ready and we want to use molecule to test it. Molecule can launch a Vagrant machine, or Docker machine or AWS machine or something else to test it. But in this example we will configure molecule to launch Docker.
We can init molecule by calling "molecule init --provider docker", which creates basic configuration files. I have hosted selectively few files on GitHub, which are generated by this command.
The "molecule.yml" is the main configuration file. In the "molecule.yml" file we insist Molecule to launch Docker
cat molecule.yml
The "playbook.yml" file is the default Ansible playbook, molecule will look for. In case if we have named our playbook differently, then we can specify that in the "molecule.yml" file as
cat playbook.yml
Next we need to write our Testinfra based test case
cat tests/test_default.py
Now it's time to run the tests with molecule, by running
The command "molecule test" has done lot many things
Prerequisites
Install the following on your workstation. I'm using Mac and the below installation commands represent Mac
- Docker
- brew install docker
- Start docker service on Mac
- Ansible
- pip install ansible
- Molecule
- pip install molecule
Sample code
The sample code used for this tutorial are hosted on GitHub at https://github.com/siddeshbg/molecule_tutorial
Here are the files involved
- molecule.yml
- playbook.yml
- roles/base/tasks/main.yml
- tests/test_default.py
How it works?
The unit test cases are written using Python based TestInfra. You don't need to install it separately, since molecule package include this.
Our goal is to test an Ansible role. In this example we want to test the role ...
cat roles/base/tasks/main.yml
This is a simple role where, we want to install the package "aptitude" on the desired machine.
--- - name: Install Aptitude apt: name: aptitude state: present
Now the role is ready and we want to use molecule to test it. Molecule can launch a Vagrant machine, or Docker machine or AWS machine or something else to test it. But in this example we will configure molecule to launch Docker.
We can init molecule by calling "molecule init --provider docker", which creates basic configuration files. I have hosted selectively few files on GitHub, which are generated by this command.
The "molecule.yml" is the main configuration file. In the "molecule.yml" file we insist Molecule to launch Docker
cat molecule.yml
Here we insist molecule to use Docker as driver, use Docker image "ubuntu:latest" and create container out of this image by the name "ansible-siddesh". Also we are using the default verifier Testinfra.
--- dependency: name: galaxy driver: name: docker docker: containers: - name: ansible-siddesh image: ubuntu image_version: latest privileged: true verifier: name: testinfra
The "playbook.yml" file is the default Ansible playbook, molecule will look for. In case if we have named our playbook differently, then we can specify that in the "molecule.yml" file as
Let's look the content of our "playbook.yml"---
ansible:
playbook: myplaybook.yml
cat playbook.yml
This is a simple playbook, which basically calls the role "base", we want to test.
--- - hosts: all roles: - role: base
Next we need to write our Testinfra based test case
cat tests/test_default.py
We have defined 2 tests here. One is to check whether "/etc/hosts" file exists and owned by root and another test is to ensure that aptitude package is installed.import testinfra.utils.ansible_runnertestinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner('.molecule/ansible_inventory').get_hosts('all')def test_hosts_file(File):f = File('/etc/hosts')assert f.existsassert f.user == 'root'assert f.group == 'root'def test_packages(Package):assert Package("aptitude").is_installed
Now it's time to run the tests with molecule, by running
$ molecule test--> Destroying instances...--> Checking playbook's syntax...playbook: playbook.yml--> Creating instances...--> Creating Ansible compatible image of ubuntu:latest ...Creating container ansible-siddesh with base image ubuntu:latest...Container created.--> Starting Ansible Run...PLAY [all] *********************************************************************TASK [Gathering Facts] *********************************************************ok: [ansible-siddesh]TASK [base : Install Aptitude] *************************************************The following additional packages will be installed:
0 upgraded, 38 newly installed, 0 to remove and 26 not upgraded.changed: [ansible-siddesh]PLAY RECAP *********************************************************************ansible-siddesh : ok=2 changed=1 unreachable=0 failed=0--> Idempotence test in progress (can take a few minutes)...--> Starting Ansible Run...Idempotence test passed.--> Executing ansible-lint...--> Executing flake8 on *.py files found in tests/...--> Executing testinfra tests found in tests/...============================= test session starts ==============================platform darwin -- Python 2.7.13, pytest-3.1.3, py-1.4.34, pluggy-0.4.0rootdir: /Users/siddesh.gurusiddappa/work/github/molecule_tutorial, inifile:plugins: testinfra-1.5.5collected 2 itemsstests/test_default.py ..=============================== warnings summary ===============================NoneModule already imported so can not be re-written: testinfra-- Docs: http://doc.pytest.org/en/latest/warnings.html===================== 2 passed, 1 warnings in 0.66 seconds =====================--> Destroying instances...Stopping container ansible-siddesh...Removed container ansible-siddesh.
The command "molecule test" has done lot many things
- It checked the playbook syntax
- It created the docker container using the image "molecule_local/ubuntu:latest"
- Alternatively if you just want the docker instances to be created without doing anything else, you can run the command "molecule create"
- It ran our Ansible role
- Alternatively you can run "molecule converge", which will create docker container and run Ansible role on it.
- Next it tests whether role is idempotent (repeated calling will produce same result)
- Next it runs our tests. As you can see, both of our tests passed.
- Alternatively you can run "molecule verify" after running "molecule converge" to run tests
- Next it destroys the container
- You can run "molecule destroy"
I like to use molecule while developing Ansible roles. Basically it provides me Docker container to test my role. I start by writing a role and then run "molecule converge", which creates a docker container with the role executed and then I login to container using "docker exec -it container-id /bin/bash" and validate my role execution manually.