Martin Reinhardt
Jun 20 · 3 min read

In the DevOps lifecycle, configuration management has always been an integral part of maintaining the desired system state. Many common tools such as Chef, Puppet, Ansible, SaltStack were used for configuration management. But before the configuration can be used in production, it has to be tested because different systems behave differently with the same configuration. So we have to be aware that our desired configuration state goes through every test scenario before it is applied to production. This is where Molecule comes into play.


Molecule has been specially developed for testing Ansible Reels under various operating systems. It uses the Ansible Playbook to determine which Ansible role to test. Molecule uses Serverspec, Testinfra and Goss as Verifier backend. To learn more about Molecule, click here. Molecule supports multiple backends such as Vagrant, Docker and OpenStack. With Molecule you can use Serverspec (Like Test Kitchen) but also ‘Testinfra’. Like Serverspec, Testinfra is a tool for writing unit tests, but it is written in Python.

To get started with Molecule just install it as python library, along with docker:

pip install ansible molecule docker

Voilà, it’s installed. With the installation of Molecule Testinfra is installed. For reference I use my docker-hardening role in the further steps. If you want to create a new Ansible role from scratch Molecule can help you here:

molecule init --driver docker --role role_name

In both examples the source code already contains a folder called molecule. All tests and docker configurations can be found here. The Molecule configuration is located in the molecule/default/molecule.ymlfile file:

driver:
name: docker
lint:
name: yamllint

The interesting part is that we use for this example docker as backend driver and also linting our YAML files with yamllint. To configure docker Molecule will generate an Docker image with the help of a template and start it:

platforms:
- name: instance
image: debian:9
privileged: true
volumes:
- /var/run/docker.sock:/var/run/docker.sock:rw
- /sys/fs/cgroup:/sys/fs/cgroup:ro
command: /sbin/init
capabilities:
- SYS_ADMIN
exposed_ports:
- 80/udp
- 80/tcp
published_ports:
- 0.0.0.0:8888:80/udp
- 0.0.0.0:8888:80/tcp
groups:
- debian

The important thing here is the privileged and capabilities part: To use a systemd unit in Docker you need this. Therefore, we need to adjust the docker configuration to run the system in docker containers. This configuration consists of approving the container core to:

  • allow application access to the calling system process (security_opts)
  • making the container similar to the host (privileged)
  • configuring the Selinux container (capabilities), and writing the container to host memory.

Remember that these docking images may only be used in test environments and not in production.

If you run molecule test, Molecule normally runs in the following sequence:

  • lint
  • destroy
  • dependency
  • syntax
  • create
  • prepare
  • converge
  • idempotence
  • side_effect
  • verify
  • cleanup
  • destroy

Molecule will run a number of tests, either the default values like linting, idempotence etc., all of which are configurable under the configuration file molecule.yml for each scenario.
After completion of the tests, the Molecule destroys the container named instance-to-test-ansible, as described in the configuration.

It’s quite handy, but in case of debugging failing tests it may be annoying to create the setup again. So in this case you can use molecule test --destroy=never, which leads to skipping the last destroy step.

When it comes to the structure of different Molecule “playbooks” I recommend the following structure:

  • create is for docker driver already covered by creating a docker instance
  • prepare should be used for basic setup, e.g preparing the docker instance with certain packages. That’s the reason I mostly name this file alsoprepare.yml
  • converge should mostly just call the appropriate playbook, so it’s a good idea to call it playbook.yml or test.yml.
  • destroy should be used to clean up certain stuff, like a tear down script

But that’s just the basic stuff. You can add test folders under molecule/default and add pytest testcases there. Here testinfra can be used:

import os 
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
def test_docker(host):
daemon = host.service("docker")
assert daemon.is_running
assert daemon.is_enabled

It provides access to the provisioned Ansible machine and allows the verification of the automation scripts, e.g. check if daemon is running on a certain network interface.

I hope you like this little introduction into testing Ansible playbooks with Molecule. You can also use Molecule to engineer your playbooks in TDD-style…

Holisticon Consultants

voices of holistikoenner/innen

Martin Reinhardt

Written by

IT Architect from Hamburg

Holisticon Consultants

voices of holistikoenner/innen

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade