How Ansible, Molecule and Vagrant are revolutionizing testing
Touching finished IT infrastructures or developed IT products again can be associated with a certain risk: Possible errors can be the result if something is changed again afterwards. As updates or corrections always mean a change, it would be helpful at this point to have a tool for testing changes. Because testing is an essential part of every development process, but every beginning can be difficult. Molecule, a tool for testing Ansible roles, can simplify this process. This guide walks through the process of testing roles with Molecule, including setting up test environments, deploying roles, and testing deployed devices.
It covers a specific case of testing roles deploying Docker containers and how to overcome the challenges involved.
By the end of this guide, you should have a solid understanding of how to use Molecule to test Ansible roles and ensure they work as expected before deploying them to production.
Testing Ansible roles with Docker containers
When testing roles that deploy Docker containers, it can be difficult to do this in a containerised environment (Docker in Docker, for example in GitLab CI). To work around this problem, we can test our roles in VMs instead. To manage these VMs, we need a specific Molecule plugin called molecule-vagrant. This plugin completely takes care of setting up isolated environments.
Guide to testing Ansible roles with Molecule and Vagrant
Prerequisites:
Ansible
Vagrant and VirtualBox
Molecule and Molecule-Vagrant plugin
Step 1: Initialising a new scenario with Molecule
$ molecule init scenario -d vagrant
INFO Initialising new scenario default...
INFO Initialised scenario in /home/sebastian/a/molecule/default successfully.
Step 2: Configuring the dependencies
To automate the testing of Ansible roles with Vagrant and Molecule, we first need to configure the VM image for the Vagrant "box" in the molecule.yml file. We will use Debian.
$ git diff 729d4e3d
--- a/molecule/default/molecule.yml
+++ b/molecule/default/molecule.yml
@@ -1,11 +1,16 @@
---
dependency:
name: galaxy
driver:
name: vagrant
platforms:
- name: instance
+ box: debian/bullseye64
provisioner:
name: ansible
verifier:
name: ansible
We can then configure the role's dependencies by creating a requirements.yml file for Molecule. This file should list all dependencies, including any Ansible roles, modules or libraries that need to be installed.
Finally, a prepare.yml file must be created that sets up the dependencies specified in the requirements.yml. This file can include tasks such as installing required packages, configuring the environment or running required scripts. These steps ensure that the role's dependencies are properly configured and ready for testing.
$ git diff ac65d0dc
--- a/molecule/default/molecule.yml
+++ b/molecule/default/molecule.yml
@@ -1,6 +1,7 @@
---
dependency:
name: galaxy
+ requirements-file: requirements.yml
driver:
name: vagrant
--- /dev/null
+++ b/molecule/default/prepare.yml
@@ -0,0 +1,19 @@
+---
+# Add necessary requirements to the test environment
+- name: Prepare
+ hosts: all
+ # These roles need root access
+ become: true
+
+ vars:
+ pip_install_packages:
+ - name: docker
+
+ roles:
+ - geerlingguy.docker
+ - geerlingguy.pip
+
+ pre_tasks:
+ - name: Update apt cache
+ ansible.builtin.apt:
+ update_cache: true
--- /dev/null
+++ b/molecule/default/requirements.yml
@@ -0,0 +1,8 @@
+---
+# Our role needs Docker and pip to be installed
+roles:
+ - src: geerlingguy.docker
+ version: 6.0.4
+
+ - src: geerlingguy.pip
+ version: 2.2.0
Step 3: Testing the role
Once the dependencies of the role are configured, we can run the molecule create command to create and prepare the VM. This command creates an isolated environment to test the role and installs all the dependencies specified in the requirements.yml file.
Once the VM has been created, we can use the molecule login command to log into the VM and manually check the installation of the dependencies. This allows us to verify that everything has been set up correctly before proceeding to the next step.
We then use the molecule converge command to deploy the role in the isolated environment. It is important to remember that the converge.yml is customised to run this playbook as an authorised user (in this case as root). This step ensures that the role is properly installed and configured on the test VM.
$ git diff 74fdd3cb
--- a/molecule/default/converge.yml
+++ b/molecule/default/converge.yml
@@ -1,6 +1,8 @@
---
- name: Converge
hosts: all
+ # Docker access needs root permissions
+ become: true
tasks:
- name: "Include molecule_http_docker_demo"
ansible.builtin.include_role:
--- a/molecule/default/molecule.yml
+++ b/molecule/default/molecule.yml
@@ -12,6 +12,9 @@ platforms:
provisioner:
name: ansible
+ options:
+ # Enable `--diff` mode
+ D: true
verifier:
name: ansible
To ensure that the installation and functionality of the role works as expected, we can define tests in the verify.yml file and then execute them with the molecule verify command. This step gives us the certainty that the role is working correctly.
$ git diff 74fdd3cb
--- a/molecule/default/verify.yml
+++ b/molecule/default/verify.yml
@@ -3,8 +3,36 @@
- name: Verify
hosts: all
+ become: true
gather_facts: false
tasks:
- name: Example assertion
+ - name: Register Docker container status
+ community.docker.docker_container_info:
+ name: nginx
+ register: container_info
+
+ - name: Nginx Docker container assertion
ansible.builtin.assert:
- that: true
+ that:
+ - "container_info.exists"
+ - "container_info.container.State.Status == 'running'"
+ - "container_info.container.State.Running"
+ success_msg: "container OK"
+ fail_msg: "container NOT OK"
+
+ - name: Make a simple HTTP request
+ ansible.builtin.uri:
+ url: http://localhost:80
+ return_content: true
+ register: result
+
+ - name: HTTP request assertion
+ ansible.builtin.assert:
+ that:
+ - "result.status == 200"
+ success_msg: "HTTP request OK"
+ fail_msg: "HTTP request NOT OK"
+
+ - name: Print HTTP response
+ ansible.builtin.debug:
+ msg: "{{ result.content }}"
Finally, we can use the molecule destroy command to delete the VMs that have just been created. This step removes all resources that were used during the test process.
Bonus: Linting the role with Molecule
Molecule also has the ability to check files in the role according to certain rules(linting)! To do this, the configuration must be adjusted as described below:
$ git diff 74fdd3cb
--- a/molecule/default/molecule.yml
+++ b/molecule/default/molecule.yml
@@ -18.3 +18.8 @@ provisioner:
verifier:
name: ansible
+
+lint: |
+ set -e
+ yamllint .
+ ansible-lint
--- a/tasks/main.yml
+++ b/tasks/main.yml
@@ -6,7 +6,7 @@
source: pull
force_source: true
-- name: Start start nginx service via Docker
+- name: Start start nginx service via Docker # noqa args[module]
community.general.docker_container:
name: "{{ nginx_service_name }}"
image: "{{ nginx_image }}"
To execute all steps at once and additionally check for idempotency, only the command molecule test is necessary
A simple but complete demo can be found here:
Molecule Docker Demo (Github, Netresearch)
$ cat .gitlab-ci.yml
---
stages:
- test
test:
stage: test
tags:
- molecule
script:
- molecule test -- --extra-vars "docker_registry_username=${CI_REGISTRY_USER}" --extra-vars "docker_registry_password=${CI_REGISTRY_PASSWORD}"
The advantages of using Molecule to test Ansible roles are manifold. Firstly, it is quick and easy to set up. We also save a lot of time as we don't have to set up isolated environments. We can set up controlled environments in a VM with Vagrant (controlled by the molecule-vagrant plugin) and deploy and test roles with Molecule.
With the help of Molecule and Vagrant, testing with Ansible has never been easier.