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 it out to production. The purpose of this testing is to make sure everything is working 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 test 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 documentation project:
“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 use the driver Podman to 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 must run 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 useful 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
For the examples in this article, you need a physical or virtual Linux machine with Python 3 and Podman installed (we are using RHEL 8.2). Also Podman had to be configured to run rootless containers. Installing Podman is outside the scope of this article, see the official for related information. documentation… Installing Podman on RHEL 8 is also covered in container documentation RHEL 8.
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 that will allow us to use Molecule to statically analyze the role code against Ansible coding standards.
The installation downloads all the required 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. Here, the default platform for the test instance is set, via the container image “docker.io/pycontribs/centos:7”, 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 it: 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:
There is no official “systemd” image for Ubuntu, so we’ll use the image maintained by 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 a temporary file system “/ 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, (for more details, see p. documentation RHEL 8):
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 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”.
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