However, with such a mass of people involved, it is very important to test everything thoroughly. When developing Ansible content such as playbooks, roles, or collections, we highly recommend that you test everything in a test environment before rolling out to production. The purpose of this testing is to make sure everything works as it should, to avoid unpleasant surprises on "production" systems.
Testing automation content is a tricky thing because it requires deploying a dedicated testing infrastructure and setting up testing conditions to ensure that the tests themselves are relevant. Molecule is a comprehensive testing framework that helps you develop and test Ansible roles so you can focus on developing automation without being distracted by managing your test infrastructure.
This is how it is declared in the project documentation :
"Molecule is designed to help develop and test Ansible roles, and fosters an approach that results in well-written roles that are well written, easy to understand and maintain."
Molecule allows you to test a role on multiple target instances to test it across a variety of operating systems and virtualization environments. Without it, for each of these combinations, you would have to create and maintain a separate test environment, set up connections to test instances, and roll them back to their original state before each test. Molecule does it all for you, in an automated and reproducible way.
In this two-part series, we'll show you how to use Molecule to develop and test Ansible roles. In the first part, we will look at installing and configuring Molecule, in the second, developing roles with it.
If the role is part of a collection, use this approach to develop and unit test the role. In the next article, we'll show you how to use Molecule to run integrated tests on collections.
Molecule uses drivers to deliver target instances across a variety of technologies, including Linux containers, virtual machines, and cloud providers. By default, it comes with three drivers preinstalled: Docker and Podman for containers, and a Delegated driver for creating custom integrations. Drivers for other providers are provided by the project development community.
In this post we will be using the Podman driverto develop and test a new role using Linux containers. Podman is a lightweight container engine for Linux, it doesn't need a running daemon, and it allows rootless containers to run, which is good for security.
Using Molecule with the Podman driver, we will develop and test a new Ansible role from scratch that deploys a web application based on an Apache web server and should run on Red Hat Enterprise Linux (RHEL) 8 or Ubuntu 20.04.
We are analyzing a typical scenario when a role should work on different versions of the operating system. Using Podman and Linux containers, we can create multiple instances to test the role on different OS versions. Because of their lightness, containers allow you to quickly iterate through the functionality of a role during development. Using containers to test roles is applicable in this situation, since the role only configures running Linux instances. For testing on other target systems or cloud infrastructures, you can use the delegated driver or other community-provided drivers.
What do we need
The examples in this article need a physical or virtual Linux machine with Python 3 and Podman installed (we're using RHEL 8.2). Also Podman had to be configured to run rootless containers. Installing Podman is beyond the scope of this article, see the official documentation for related information . Installing Podman on RHEL 8 is also covered in the RHEL 8 container documentation .
Let's get started
Molecule is designed as a Python package and is therefore installed via pip. The first step is to create a dedicated Python environment and install our Molecule into it:
$ mkdir molecule-blog
$ cd molecule-blog
$ python3 -m venv molecule-venv
$ source molecule-venv/bin/activate
(molecule-venv) $ pip install "molecule[lint]"
Note that we are installing Molecule with the "lint" option so that pip will also supply the "yamllint" and "ansible-lint" tools, which will allow us to use Molecule to statically analyze the role code against Ansible coding standards.
The installation downloads all the necessary dependencies from the Internet, including Ansible. Now let's see what we have installed:
$ molecule --version
molecule 3.0.4
ansible==2.9.10 python==3.6
Well, it's time to use the "molecule" command to initialize the new Ansible role.
Initializing a new Ansible role
Generally speaking, when developing a new Ansible role, it is initialized with the "ansible-galaxy role init" command, but we will use the "molecule" command instead. This will give us the same role structure as with the "ansible-galaxy" command, as well as a basic piece of code for running Molecule tests.
By default, Molecule uses the Docker driver to run tests. Since we want to use podman instead, when initializing the role with the "molecule" command, we must specify the appropriate driver using the "--driver-name = podman" option.
Switch back to the "molecule-blog" directory and initialize the new role "mywebapp" with the following command:
$ molecule init role mywebapp --driver-name=podman
--> Initializing new role mywebapp...
Initialized role in /home/ricardo/molecule-blog/mywebapp successfully.
Molecule creates our role structure in the "mywebapp" folder. Switch to this folder and see what is there:
$ cd mywebapp
$ tree
.
βββ defaults
β βββ main.yml
βββ files
βββ handlers
β βββ main.yml
βββ meta
β βββ main.yml
βββ molecule
β βββ default
β βββ converge.yml
β βββ INSTALL.rst
β βββ molecule.yml
β βββ verify.yml
βββ README.md
βββ tasks
β βββ main.yml
βββ templates
βββ tests
β βββ inventory
β βββ test.yml
βββ vars
βββ main.yml
10 directories, 12 files
Molecule puts its configuration files in the "molecule" subdirectory. When initializing a new role, there is only one script called "default". Later, you can add your scripts here to test various conditions. In this article, we will only use the "default" script.
Let's check the basic configuration in the file "molecule / default / molecule.yml":
$ cat molecule/default/molecule.yml
---
dependency:
name: galaxy
driver:
name: podman
platforms:
- name: instance
image: docker.io/pycontribs/centos:7
pre_build_image: true
provisioner:
name: ansible
verifier:
name: ansible
As we requested, this file states that the Podman driver is used for tests. This is where the default platform for the test instance is set, via the "docker.io/pycontribs/centos:7" container image, which we will change later.
Unlike Molecule v2, Molecule v3 does not define a default linter. Therefore, let's open the configuration file "molecule / default / molecule.yml" and add the lint configuration at the end:
$ vi molecule/default/molecule.yml
...
verifier:
name: ansible
lint: |
set -e
yamllint .
ansible-lint .
Save and close the file, and run the "molecule lint" command from the root folder of our project to run the linter throughout the project:
$ molecule lint
We get a few errors in the output, since the file "meta / main.yml" does not contain a number of required values. Let's fix this: edit the "meta / main.yml" file, add "author", "company", "license", "platforms" and remove the empty line at the end. For brevity, we will dispense with comments, and then our "meta / main.yaml" will look like this:
$ vi meta/main.yml
galaxy_info:
author: Ricardo Gerardi
description: Mywebapp role deploys a sample web app
company: Red Hat
license: MIT
min_ansible_version: 2.9
platforms:
- name: rhel
versions:
- 8
- name: ubuntu
versions:
- 20.04
galaxy_tags: []
dependencies: []
Let's run the linter on the project again and make sure that there are no more errors.
$ molecule lint
--> Test matrix
βββ default
βββ dependency
βββ lint
--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'lint'
--> Executing: set -e
yamllint .
ansible-lint .
So, our role is initialized and the basic configuration of the molecule is also in place. Now let's create a test instance.
Create a test instance
By default, Molecule defines only one instance, which is called "instance" and is created from the "Centos: 7" image. Our role, if you remember, should work on RHEL 8 and Ubuntu 20.04. Also, because it runs the Apache web server as a system service, we need a container image with "systemd" enabled.
Red Hat has an official Universal Base Image for RHEL 8 with "systemd" enabled:
β’ registry.access.redhat.com/ubi8/ubi-init
There is no official "systemd" image for Ubuntu, so we'll use the image maintained by Jeff Geerling (Jeff Geerling) from the Ansible community:
β’ geerlingguy / docker-ubuntu2004-ansible
To get instances with "systemd", let's edit the configuration file "molecule / default / molecule.yml" by removing the "centos: 7" instance from it and adding two new instances:
$ vi molecule/default/molecule.yml
---
dependency:
name: galaxy
driver:
name: podman
platforms:
- name: rhel8
image: registry.access.redhat.com/ubi8/ubi-init
tmpfs:
- /run
- /tmp
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:ro
capabilities:
- SYS_ADMIN
command: "/usr/sbin/init"
pre_build_image: true
- name: ubuntu
image: geerlingguy/docker-ubuntu2004-ansible
tmpfs:
- /run
- /tmp
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:ro
capabilities:
- SYS_ADMIN
command: "/lib/systemd/systemd"
pre_build_image: true
provisioner:
name: ansible
verifier:
name: ansible
lint: |
set -e
yamllint .
ansible-lint .
With these parameters we mount for each instance the temporary filesystem "/ run" and "/ tmp", as well as the volume "cgroup". In addition, we enable the "SYS_ADMIN" function, which is required to run containers with Systemd.
If you do everything smartly and run this example on a RHEL 8 machine with SELinux enabled, then you also need to set the boolean parameter "container_manage_cgroup" to true so that containers can run Systemd (see the RHEL 8 documentation for more details ):
sudo setsebool -P container_manage_cgroup 1
Molecule uses the Ansible Playbook to initialize these instances. Let's change and add the initialization parameters by modifying the "provisioner" dictionary in the "molecule / default / molecule.yml" configuration file.
It accepts the same configuration options as specified in the "ansible.cfg" configuration file. For example, let's update the provisioner configuration by adding a "defaults" section. Set the Python interpreter to "auto_silent" to disable warnings. Let's include the "profile_tasks", "timer" and "yaml" callback plugins so that the profiler information is included in the Playbook output. Finally, add the "ssh_connection" section and disable SSH pipelining as it does not work with Podman:
provisioner:
name: ansible
config_options:
defaults:
interpreter_python: auto_silent
callback_whitelist: profile_tasks, timer, yaml
ssh_connection:
pipelining: false
Let's save this file and create an instance with the "molecule create" command from the root directory of our role:
$ molecule create
Molecule will execute an init playbook and create both of our instances. Let's check them with the "molecule list" command:
$ molecule list
Instance Name Driver Name Provisioner Name Scenario Name Created Converged
--------------- ------------- ------------------ --------------- --------- -----------
rhel8 podman ansible default true false
ubuntu podman ansible default true false
Let's also check that both containers are running in Podman:
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2e2f14eaa37b docker.io/geerlingguy/docker-ubuntu2004-ansible:latest /lib/systemd/syst... About a minute ago Up About a minute ago ubuntu
2ce0a0ea8692 registry.access.redhat.com/ubi8/ubi-init:latest /usr/sbin/init About a minute ago Up About a minute ago rhel8
When developing a role, Molecule uses the running instances to test it. If the test fails or some error leads to irreversible changes, because of which everything has to start over, you can kill these instances at any time with the command "molecule destroy" and recreate them with the command "molecule create".
Conclusion
If you are impatient and want to dig deeper into the topic of developing and testing roles, or the topic of Ansible automation, then we recommend the following resources:
- Ansible Whitepaper - Achieving Rolling Updates & Continuous Deployment
- Ansible Whitepaper - Ansible in Depth
- Getting Started Guide - Molecule documentation
- ansible-community/molecule: Molecule aids in the development and testing of Ansible roles
- Roles in the official Ansible documentation
- Galaxy Developer Guide