Containerized Confidence: Testing Ansible Roles with Molecule and Podman on Debian Linux

Testing Ansible roles is a crucial step in ensuring the reliability and correctness of your infrastructure automation. Among the various tools available for this purpose, Molecule stands out as a versatile framework for testing Ansible roles. When coupled with Podman, a containerization tool similar to Docker, it provides a seamless and efficient environment for testing Ansible roles on Debian Linux. In this blog post, we will explore the process of setting up Molecule with Podman on Debian Linux.

If you want to engage in a conversation with fellow techies, then don’t forget to join my Discord: https://discord.gg/YbSYGsQY

Why Molecule and Podman?

Molecule is a powerful tool that simplifies the testing of Ansible roles by creating isolated environments for role testing. It supports a wide range of containerization and virtualization providers, and one of the most popular choices is Podman, an open-source container management tool.

Here are some key reasons why using Molecule with Podman for Ansible role testing is a winning combination:

  1. Isolation: Molecule creates clean, isolated environments for testing Ansible roles, preventing interference with your local system.
  2. Reproducibility: With Podman containers, you can replicate the exact test environment across different systems, ensuring consistent results.
  3. Efficiency: Podman is a lightweight alternative to Docker, which consumes fewer resources and is well-suited for testing purposes.
  4. Compatibility: Using Podman allows you to test your roles on systems that support the Podman container runtime, including Debian Linux.

Prerequisites

Before getting started, make sure you have the following prerequisites in place:

  1. A working installation of Ansible (PIP magic).
  2. Molecule and Podman installed on your system (PIP and APT magic).
  3. A basic understanding of Ansible roles and how to structure them (brain magic).
  4. An Ansible role with a properly configured “Galaxy Style” meta information.

Testing With Podman

In the Ansible role’s directory initiate a test scenario. In the past Molecule was able to create ansible roles too, but they changed this function and you have to use ansible-galaxy command for creating new roles. I have an already working common role, I will present the process on it.

All of the operations below happen in the Ansible role’s root directory (in my case the /home/tmolnar/ansible/roles/common)!

molecule init scenario -d podman

It will create a new directory and file structure under the molecule directory. Molecule creates test scenarios, and with the above command it created the default scenario. If you want to name your scenario anything else, give it a name after the command:

molecule init scenario [OPTIONS] [SCENARIO_NAME]

The most important files created here are:

  • molecule.yml – this is the configuration of our Molecule scenario.
  • create.yml – this playbook creates the environment, in this case the Podman containers for Ansible testing.
  • destroy.yml – this playbook removes the test containers and the temporary inventory for the tests.
  • converge.yml – this playbook contains the actual testing, here you include your role to be tested.

The most basic molecule.yml file for this scenario must contain the dependencies of the role to be tested (the requirements.yml file), and the platforms that is a custom built Debian Bookworm image in my case.

Most of the code below can be found in the documentation of Molecule. I made minor modifications for my environment.

# molecule.yml
---
dependency:
  name: galaxy
  options:
    requirements-file: requirements.yml
platforms:
  - name: molecule-debian12-test
    image: tmolnar-debian:1.3

The creation of the test containers handled by the create.yml file.

# create.yml
---
- name: Create
  hosts: localhost
  gather_facts: false
  vars:
    molecule_inventory:
      all:
        hosts: {}
        molecule: {}

  tasks:
    - name: Create a container
      containers.podman.podman_container:
        name: "{{ item.name }}"
        image: "{{ item.image }}"
        state: started
        command: sleep 1d
        log_driver: json-file
      register: result
      loop: "{{ molecule_yml.platforms }}"

    - name: Fail if container is not running
      when: >
        item.container.State.ExitCode != 0 or
        not item.container.State.Running
      ansible.builtin.include_tasks:
        file: tasks/create-fail.yml
      loop: "{{ result.results }}"
      loop_control:
        label: "{{ item.container.Name }}"

    - name: Add container to molecule_inventory
      vars:
        inventory_partial_yaml: |
          all:
            children:
              molecule:
                hosts:
                  "{{ item.name }}":
                    ansible_connection: containers.podman.podman
      ansible.builtin.set_fact:
        molecule_inventory: >
          {{ molecule_inventory | combine(inventory_partial_yaml | from_yaml) }}
      loop: "{{ molecule_yml.platforms }}"
      loop_control:
        label: "{{ item.name }}"

    - name: Dump molecule_inventory
      ansible.builtin.copy:
        content: |
          {{ molecule_inventory | to_yaml }}
        dest: "{{ molecule_ephemeral_directory }}/inventory/molecule_inventory.yml"
        mode: '0600'

    - name: Force inventory refresh
      ansible.builtin.meta: refresh_inventory

    - name: Fail if molecule group is missing
      ansible.builtin.assert:
        that: "'molecule' in groups"
        fail_msg: |
          molecule group was not found inside inventory groups: {{ groups }}
      run_once: true # noqa: run-once[task]

To remove the unnecessary test containers the destroy.yml playbook is going to be called.

# destroy.yml
---
- name: Destroy molecule containers
  hosts: molecule
  gather_facts: false

  tasks:
    - name: Stop and remove container
      delegate_to: localhost
      containers.podman.podman_container:
        name: "{{ inventory_hostname }}"
        state: absent
        rm: true
    - name: Remove potentially stopped container
      delegate_to: localhost
      ansible.builtin.command:
        cmd: podman container rm --ignore {{ inventory_hostname }}
      changed_when: false

- name: Remove dynamic molecule inventory
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Remove dynamic inventory file
      ansible.builtin.file:
        path: "{{ molecule_ephemeral_directory }}/inventory/molecule_inventory.yml"
        state: absent

With the create and destroy files you have the general availability of containers, but no testing has happened yet.

The most basic test scenario will simply include the role to be tested (my common role here) in the converge.yml file.

# converge.yml
---
- name: Converge
  hosts: molecule
  gather_facts: false

  vars:
    common_hostname: testhost
    common_interface: enp3s1
    common_ip: 1.2.3.4
    common_gateway: 3.4.5.6

  tasks:
    - name: Check the role
      ansible.builtin.include_role:
        name: common
        tasks_from: main.yml

The above code is a very basic test case indeed, it includes the common role in the tasks section, and it has some pre-configured variables provided in the vars section.

The default scenario test matrix follows the following process: dependency, cleanup, destroy, syntax, create, prepare, converge, idempotence, side_effect, verify, cleanup, destroy.

At this point I have covered the steps with bold text from this procedure.

From this point you can run a full automated test that executes the role in a container and then Molecule removes the container and data at the end of the test with the following command:

molecule test

Whenever I want to keep the test container intact then Molecule can be started with:

molecule converge

The container will remain in a running state on the system and I can look around/debug in it. After finishing the work the

molecule destroy

command is going to remove the traces of this temporary environment.

Conclusion

This is just the tip of the iceberg, I just scratched the very basics of Molecule testing, though this infrastructure opens up a whole new world of possibilities in your DevOps journey.

Using Molecule with Podman simplifies and enhances the process of testing Ansible roles on Debian Linux. With this setup, you can quickly and confidently verify the functionality of your roles in isolated, reproducible environments. Whether you’re developing roles for Apache, Nginx, or any other service, Molecule and Podman provide a robust framework for role testing, helping you ensure the reliability of your infrastructure automation.

If you want to engage in a conversation with fellow techies, then don’t forget to join my Discord: https://discord.gg/YbSYGsQYES

Leave a comment