Ansible Code Testing — Part 1 — Test Environment Automation

Hadi Azaddel
7 min readJun 10, 2022

Ansible is one of the dominant Infrastructure as Code (IaC) solutions. To ensure the code is applied on the target hosts correctly, It is necessary to test the IaC codes in advance. Also, Ansible is used as a provisioning tool and It is like a programming language at its core, and testing is a vital part of programming. In the following, I will try to survey the Ansible testing approaches.

Step 1: Test environment

Let’s start with the testing environment. Suppose you want to write some unit tests for a python code. There is no need to provide a test environment while your development environment is almost enough for testing. On the contrary, you need a test environment for IaC codes.

- apt:
name:
nginx
state: latest
update_cache: yes

It is not recommended to test the above code locally, maybe you are using a different OS for development. Also, you need a fresh environment for every run that is close enough to the target host. Moreover, it is too tedious to mock apt module functionality and there is no way except to run the apt module in a similar environment to the targets. Consequently, at the beginning of testing Ansible codes, we need a tool for

  1. Describing test environment and also an

2. Automation medium for simply creating and destroying the environment.

In the first step, we need a test environment. But what do you want to test exactly!? So, keep the first step in mind, and we will return.

Step 2: What do we want to test exactly?!

Ansible code types into two categories:

  1. Ansible Playbook/Roles/Collection (= Yaml files)
  2. Ansible Custom Module/Library

Perhaps, there are some overlaps between these two categories but this division helps us use more compatible tools for testing. To make it simple, I’ll continue with The first (the more common category) and leave the second to be described in the future posts. Before deep-diving into the details of tools, I introduce some of them in different levels of test types. Refer to the IaC Testing pyramid.

  1. Static Analysis: We need a linter or a tool to just check syntax issues
  2. Unit Test: Same as a Unit test in a programming language. Take each Ansible Task as a unit. To provide a test environment we use Molecule (https://github.com/ansible-community/molecule) and to test the results after applying our ansible codes we can some test libraries like Testinfra, Goss, etc. Besides, Ansible is used for testing purposes, too.
  3. Integration Test: You can take a playbook, role, or a collection as a group to be tested entirely. The same libraries are used for this level.
  4. End-2-End(E2E) Test: In this type of test it is a must to provide a similar testing environment to the production. As moving up the pyramid level, testing time increases. To decrease the time, If the production environment is simple enough, you can describe it with Molecule instead.

Main Tools:

  • Molecule: A platform for automating test scenarios consists of describing, and creating, destroying the test environment.
  • Validation Testing Tools (Testinfra/Ansible/Goss/…): Test codes can be written with these libraries or modules. They are comparable to Pytest for python and JUnit for Java.

For simplicity, we used Ansible as a Validation Tool alongside Molecule, and leaved the rest.

Molecule:

Ansible is everywhere! It means you can use Ansible to start a test environment (like a container or VM), create an inventory host file, static analyses, and so on. Good news: To facilitate all these phases, Molecule is developed. Ansible announced its adoption of Molecule as an official Red Hat Ansible project.

In the first place, Make sure you installed theses requirements:

  • Ansible
  • Vagrant (For testing in VM environment)
  • Docker (For testing in Container environment)
  • Molecule + Drivers:
pip install ‘molecule[docker]’ molecule-vagrant
  • Install Ansible linters:
pip install ansible-lint yamllint

I have created a project just for testing.
https://github.com/hadi2f244/testing_ansible

We defined a molecule config for the whole project but you can create different molecule configs for every role individually by :

molecule init role my_new_role — driver-name docker -s mysenario

The driver can be altered to other options including Vagrant. -s shows the scenario name which would be default if you omit -s mysenario. In the test project, I provided two scenario: docker and vagrant (They are arbitrary names). Let’s continue step by step:

  1. Create an environment: (Delete -s docker if the environment name is ‘default’)
molecule create -s docker

2. Run the ansible codes:

molecule converge -s docker

3. Verify the test codes:

molecule verify -s docker

4. Delete environment:

molecule destroy -s docker

Question: Where is the description of the test environment?

Answer: molecule.yaml

---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: test-docker-1
image: kilip/molecule-ubuntu:18.04
pre_build_image: true
privileged: true
volumes:
- /var/run/docker.sock:/var/run/docker.sock:rw
- /sys/fs/cgroup:/sys/fs/cgroup:ro
- /run/dbus/system_bus_socket:/run/dbus/system_bus_socket:ro
capabilities:
- SYS_ADMIN
groups:
- molecule-docker
provisioner:
name: ansible
inventory:
hosts:
molecule-docker:
links:
group_vars: ../../group_vars
host_vars: ../../host_vars
verifier:
name: ansible
lint: |
set -e
export ANSIBLE_ROLES_PATH=./roles
yamllint -c ./molecule/.yamllint .
ansible-lint
flake8

Molecule.yaml includes these main parts :

Note that for some functionalities you must run the container in the privileged mode. Nevertheless, there are some situations where you should use VM inevitably.

  • Platforms: Description of test environment (Options like docker image or VM resource allocations). This example contains one test environment (test-docker-1), Although you can describe different ones. Take occasions such as:
  1. Testing your roles one different operating systems

2. Testing a monitoring system and you need two containers (VM). Consequently, the monitoring server is installed on the first and the monitoring agent is installed on another container. (You need to config docker network or vagrant-hostmanager)

  • Verifier: Same as Validation testing tool. As mentioned above, I used Ansible as Verifier (Look at Verify.yaml file).
  • Lint: Linter is responsible for syntax and code style checking.
  • Provisioner: ansible configs can be detailed in this part
  • Dependency: Dependency manager (ansible-galaxy)

Together with molecule.yaml, there are two other files:

  • Converge.yaml: Call your ansible roles and playbooks here. (Like main.yaml in the root of ansible projects)
  • Verify.yaml: Testing codes (Validation testing tools)

Converge.yaml :

---
- name: gather facts
hosts: molecule-docker
- name: Converge edge servers
hosts: molecule-docker
gather_facts: true
roles:
- {role: package, tags: "package", become: true}

Here the package role is run on all hosts of molecule-docker group.

Verify.yaml :

- name: Verify_All
hosts: molecule-docker
gather_facts: true
tasks:
- name: "Test nginx service is running and enabled | Check"
service:
name: nginx
state: started
enabled: true
check_mode: true
ignore_errors: true
register: nginx_service_status_res
- name: "Test nginx service is running and enabled | Debug"
debug:
msg: "{{ nginx_service_status_res }}"
when: debug_mode
- name: "Test nginx service is running and enabled | Assert"
assert:
that:
- not nginx_service_status_res.changed
- not nginx_service_status_res.failed

It checks Nginx service is running and enabled.

To summarize :

  • You Implement test environments in molecule.yaml
  • Create a test environment (‘ molecule create -s docker ‘)
  • Run Ansible codes (‘ molecule converge ’)
  • Running test codes (‘ molecule verify ’)
  • You can destroy the environment (‘ molecule destroy ’)

Molecule is not limited to these commands . Check Linting and Idempotency commands:

  • Check syntax and linting:
molecule lint -s docker
  • Check idempotency:
molecule idempotence -s docker

All of these steps can be run by `molecule test -s docker`. Besides the order can be customized in a Molecule Scenario. Add this to molecule.yaml:

scenario:
name: default
test_sequence:
- lint
- destroy
- syntax
- create
- converge
- idempotence
- verify
- destroy

Note: Connect to the test environment by :

molecule login — host test-docker-1 -s docker

More about Molecule

Custom Driver:

Molecule allows implementing custom driver in various ways :

  1. Delegated driver
  2. Custom Create/Destroy playbook: Creation and destruction are customizable by custom create.yaml and destroy.yaml. Put these files near molecule.yaml.
create.yaml exmaple

3. Custom molecule driver: For instance,

  1. Custom molecule driver: For instance,

My Personal Experiences with Molecule:

Generally, testing IaC (Ansible) codes are a tedious journey! It means you must write some ansible codes and wait for them to be run completely. There is no silver bullet to escape it!

Some handy tips may help:

  • VM snapshot: Divide ansible codes to roles and playbook as steps, then after each step make a snapshot of the environment. It is simple and you don’t need to run previous steps every time you are debugging a playbook.
  • Test two non-related codes individually: Create different scenarios for each part of the code. Instead, each role can consist of a scenario if there are not dependent.
  • Use remote environments: Using public cloud and remote VMs assist you with better resources and network connections.
  • Use CI/CD to decrease manual steps.

Summary

During this blog post, I tried to dig different levels of testing Ansible codes. Also, you have successfully completed testing an Ansible project with Molecule. In the next upcoming parts of this blog post series, I will try to introduce Testinfra, Ansible Module testing, and etc.

Follow Part2 : Ansible Code Testing — Part 2 — Writing Tests
I have collected some related references:

https://www.adictosaltrabajo.com/2020/05/08/ansible-testing-using-molecule-with-ansible-as-verifier/

https://github.com/rhythmictech/ansible-role-molecule-demo

https://www.ansible.com/hubfs//AnsibleFest%20ATL%20Slide%20Decks/Practical%20Ansible%20Testing%20with%20Molecule.pdf

https://habr.com/en/post/500226/

https://www.ansible.com/blog/developing-and-testing-ansible-roles-with-molecule-and-podman-part-1

https://www.ansible.com/blog/developing-and-testing-ansible-roles-with-molecule-and-podman-part-2

--

--