Getting Started with Ansible Molecule

· by Victor Mendonça · Read in about 7 min · (1331 words) ·

Overview

If you haven’t heard about Ansible Molecule you came to the right place. I will cover what it is, it’s basic usage, and how to install it and get started.

What it is

Ansible Molecule is a project (tool) that can greatly improve your Ansible workflow. It allows you to automate your tasks (which is great for CI) by providing means of running different and idempotent tests against your roles.

And Molecule is based on Ansible, so you are essentially using Ansible to test Ansible. How cool is that!?

What it does

To put it in simple words, Molecule tests your code. With Molecule you can easily run multiple tests against your code and make sure it works before deploying to an environment.

Some of the tests you can run are:

  • Yaml lint
  • Ansible lint
  • Ansible syntax
  • Run the Ansible role against a virtual machine
  • Test idempotence (run the same Ansible role against the same virtual machine for the second time)

Folder Structure

When invoked Molecule creates a single role folder with the usual Ansible structure. Inside this role folder an additional molecule folder is created. This is where the main Molecule configuration lives.

$ tree -d
.
├── defaults
├── files
├── handlers
├── molecule
│   └── default
│       ├── converge.yml
│       ├── molecule.yml
│       └── verify.yml
├── tasks
├── templates
├── tests
└── vars

Test Types and Scenarios

Test scenarios can be configured inside the molecule folder and each scenario should have it’s own folder.

A ‘default’ scenario is created automatically with the following tests enabled (you can change them according to your needs):

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

Drivers

Three different drivers (Vagrant, Docker, and OpenStack) can be used to create virtual machines. These virtual machines are used to test our roles.

On this tutorial we will be using Docker as our driver.

Installing Molecule

Molecule can be installed via pip or with distro packages (depending on your distro). You can mix and match and install Molecule via pip and specific components (like ansible or ansible-lint) via your distro’s package manager.

Notes:

  • On Windows Molecule can only be installed via WSL
  • I’m assuming you already have Ansible installed and will not cover it here

Windows Install (Ubuntu WSL)

On Ubuntu Molecule needs to be installed via pip. If perhaps you are running another distro in WSL, you can check if the packages are available with your package manager (if you choose to install that way).

Install python-pip.

$ sudo apt install -y python-pip

Create a python virtual environment for Molecule. The software we are going to install will reside in the virtual environment (we can use the environment many times).

$ python3 -m venv molecule

Activate the environment (see that the prompt changes).

$ source molecule/bin/activate
(molecule-venv) $

Install the wheel package.

(molecule-venv) $ python3 -m pip install wheel

Install ansible and ansible-lint. You can do this via Python/Molecule, or via the OS.

OS

$ apt install ansible ansible-lint

Via molecule

(molecule-venv) $ python3 -m pip install "molecule[ansible]"  # or molecule[ansible-base]

Install molecule and docker.

(molecule-venv) $ python3 -m pip install "molecule[docker,lint]"

Linux Install (Arch)

If you are running Arch (I use Arch BTW) you can install everything with pacman (or with pip like we did for Windows WSL).

$ sudo /usr/bin/pacman -S yamllint ansible-lint molecule molecule-docker

Getting Started

Creating a Molecule Role

We will now create and configure a role with Molecule using Docker as the driver.

Run molecule init role [role name] -d docker:

This should have created a new role folder with the required molecule files inside.

molecule.yml

Holds the default configuration for molecule.

./myrole/molecule/default/molecule.yml

---
dependency:
  name: galaxy
driver:
  name: docker
platforms:
  - name: instance
    image: docker.io/pycontribs/centos:8
    pre_build_image: true
provisioner:
  name: ansible
verifier:
  name: ansible

converge.yml

This is the playbook file that molecule will run.

./myrole/molecule/default/converge.yml

---
- name: Converge
  hosts: all
  tasks:
    - name: "Include myrole"
      include_role:
        name: "myrole"

Running a Simple Test

Let’s use the existing configuration in molecule.yml and converge.yml to run a simple test.

Edit the tasks file (myrole/tasks/main.yml) and add a simple task (you can use the example below with the debug module):

---
# tasks file for myrole

- debug:
    msg: "System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}"

Run molecule converge (from the role folder) to build the image and run the playbook.

Now that we got the very basic stuff done, let’s move into a bit more advanced steps so we can better understand Molecule and use it with our code.

Additional Steps and Configuration

Adding Lint

Lint is not enabled by default, but that can be easily changed by editing molecule.yml and adding the lint key to it:

dependency:
  name: galaxy
driver:
  name: docker
platforms:
  - name: instance
    image: docker.io/pycontribs/centos:8
    pre_build_image: true
provisioner:
  name: ansible
verifier:
  name: ansible
lint: |
  set -e
  yamllint .
  ansible-lint .

Now run molecule lint. You should get a lot of warnings due to to information missing in the meta folder:

As instructed by the output of the command, you can quiet or disable the messages by adding a warn_list or skip_list to .ansible_lint

Tip: You can also fine tune yaml linting by editing .yamllint

Running a Full Test

So far we have only run two tests:

  • Converge (dependency, create, prepare converge)
  • Lint (yaml lint and Ansible lint)

Let’s run a full test (default scenario) on our role. Remember, the full test will run dependency, lint, syntax, create, prepare, converge, idempotence, side_effect, verify, cleanup and destroy.

Run molecule test.

Running Roles from Another Folder

You can also use Molecule to test roles from another folder (which makes molecule very flexible for testing).

Let’s say I have the following folder structure:

.
└── Ansible
    ├── Molecule
    └── web-project

Inside my web-project folder I have a role called ‘apache’ that installs (guess what?) httpd.

./Ansible/web-project/roles/apache/tasks/main.yml

---
# tasks file for apache

- name: Installs Apache
  yum:
     name: httpd
     state: present

I can easily modify my existing converge.yml to include that role:

./Ansible/Molecule/myrole/molecule/default/converge.yml

---
- name: Converge
  hosts: all
  tasks:
    - name: "Include myrole"
      include_role:
        name: "../../web-project/roles/apache"

Also edit molecule.yml so we are linting that external folder:

./Ansible/Molecule/myrole/molecule/default/molecule.yml

lint: |
  set -e
  yamllint ../../web-project
  ansible-lint ../../web-project

And then run molecule converge (from the Molecule role folder) to test. Because molecule converge does not include the destroy command, I can login to the container ( with molecule login) and check if httpd was installed:

Tip:

  • When testing against multiple containers you can use molecule login --host [container name]
  • You can also use docker cli to connect to the container - docker exec -ti [container name] /bin/bash

Note: On this example nothing other than the role is imported (e.g. variables and config from ansible.cfg are not imported)

Additional Container Configuration

We can configure different options for our container in molecule.yml under the platforms key section. Configuration here is similar to a docker compose file.

  • name - Name of the container
  • image - Image for the container. Can be local or from a remote registry
  • pre_build_image - Instructs to use pre-build image (pull or local) instead of building from molecule/default/Dockerfile.j2
  • privileged - Give extended privileges (a «privileged» container is given access to all devices)
  • capabilities - Same as --cap-add=SYS_ADMIN. Grants a smaller subset of capabilities to the container compared to the privileged option
  • command - Same as Dockerfile RUN
  • groups - Assigns the container to one or more Ansible groups

Example:

---
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 .

Conclusion

I’m hoping I was able to provide you with enough information on how to get started with Molecule. If you have any comments, suggestions or corrections, please leave them in the comment box below.

code with