Automated Ansible testing with Molecule

Infrastructure testing provides some challenges just because of the mere fact you are building machines and not just compiling code. To test Ansible, I used to run Ansible with –syntax-check and –list-tasks. For roles I would run local tests with Vagrant using the tests/ directory in the role. The tests had Ansible test itself with the uri module or other checks. This is ok for simple checks but can be cumbersome and time consuming as it doesn’t catch everything.

Molecule has made this very simple. Install Molecule with pip install molecule. Create your role with molecule init --role-name role --driver-name driver. The current drivers are Docker, OpenStack, Vagrant, EC2, GCE, LXC, LXD, and a few others with Docker being the default. It’s not absolutely required to build the role with molecule init but it adds the molecule dir with default testing and the .yamllint file.

Rather than rewrite their documentation, I’ll just go through what I have set up to test my roles. Currently I’m using the Vagrant driver with a CentOS 7 box ( I don’t do much with non RHEL distros) and the libvirt provider for Vagrant. So here’s what my molecule/molecule.yml file looks like:

---
dependency:
  name: galaxy
driver:
  name: vagrant
  provider:
    name: libvirt
lint:
  name: yamllint
platforms:
  - name: bind
    box: centos/7
provisioner:
  name: ansible
  lint:
    name: ansible-lint
scenario:
  name: default
verifier:
  name: testinfra
  options:
    sudo: True
  lint:
    name: flake8

Molecule also uses testinfra to run automated tests against the Vagrant box. Here’s a simple example to check a firewalld role:

import os

import testinfra.utils.ansible_runner

testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')

def test_firewall_installed(host):
    package = host.package("firewalld")
    assert package.is_installed

def test_firewall_service(host):
    service = host.service("firewalld")
    assert service.is_running
    assert service.is_enabled

def test_firewall_config(host):
    config = host.file("/etc/firewalld/zones/public.xml")
    assert config.exists
    assert config.contains('<service name="http"/>')
    assert config.contains('<service name="https"/>')
    assert config.contains('<service name="ssh"/>')

Testinfra will check that the package firewalld is installed, the service is running and enabled, and that the /etc/firewalld/zones/public.xml file contains the services defined.

To start your molecule testing you can run molecule test.

Here’s a video running this setup against a firewalld role: