Part 6: Testing Ansible code
Making sure our code runs!
- Sebastiaan
- 6 min read
What is Ansible Molecule
Ansible Molecule is created for development and testing of Ansible roles and collections. Through Molecule it is possible to test against different OS versions or complete new systems.
prerequisites to local use Molecule:
- Podman of docker installation
- Locally installed ansible
- Python3
- A python virtual environment
Installation
There are different ways of installing Ansible Molecule see this url.
I Chose the pip way with python3 -m pip install molecule ansible-core.
Whether we choose docker or podman we need to install the molecule connector, I personally use podman so thats pip install molecule-podman
Setup
With the installation out of the way, we can setup our Ansible role for testing. We can use the role we’ve created earlier in part4 and add a folder in the role called extensions.
mkdir /Users/sebas/Desktop/ansible/<name role>/extensions
Lets go to the folder /Users/sebas/Desktop/ansible/<name role>/extensions, and run the following command:
python3 -m molecule init scenario
It creates the following 4 files:
create.yml destroy.yml molecule.yml converge.yml
molecule.yml= Is used for central Molecule configurationcreate.yml= Is used for creating the test instancesconverge.yml= Is used to call upon the Ansible roledestroy.yml= Is used for destroying the test instances
To use a container instance we need to create one more file:
touch /Users/sebas/Desktop/ansible/<name role>/extensions/molecule/default/requirements.yml
requirements.yml= Is used by Molecule to ensure that the test environment works correctly
Adding configurations
We now overwrite the current data from the files named above, the info we are going to add is a simplefied config from the Ansible Molecule examples site. Copy the snippets into the dedicated files.
molecule.yml
---
dependency:
name: galaxy
options:
requirements-file: requirements.yml
platforms:
- name: molecule-ubuntu
image: docker.io/geerlingguy/docker-ubuntu2004-ansible
- name: molecule-fedora
image: geerlingguy/docker-fedora39-ansible
driver:
name: podman
options:
managed: false
login_cmd_template: "podman exec -ti {instance} bash"
ansible_connection_options:
ansible_connection: podman
Create.yml
- name: Create
hosts: localhost
gather_facts: false
vars:
molecule_inventory:
all:
children:
molecule:
hosts: {}
tasks:
- name: Create a container
containers.podman.podman_container:
name: "{{ item.name }}"
image: "{{ item.image }}"
state: started
command: "{{ item.command | default('sleep 1d') }}"
register: result
loop: "{{ molecule_yml.platforms }}"
- 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, recursive=true) }}
loop: "{{ molecule_yml.platforms }}"
- name: Dump molecule_inventory
ansible.builtin.copy:
content: "{{ molecule_inventory | to_yaml }}"
dest: "{{ molecule_ephemeral_directory }}/inventory/molecule_inventory.yml"
mode: "0600"
converge.yml
---
- name: Converge
hosts: all
roles:
- role: <Name of the role>
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
requirements.yml
collections:
- containers.podman
How to run
For molecule to work correctly we need to export a variable to our terminal:
export ANSIBLE_ROLES_PATH=/Users/sebas/Desktop/ansible/<name role>
This variable is always pointed to the root of our Ansible role.
Lets go to the extensions folder once more in our terminal cd /Users/sebas/Desktop/ansible/<name role>/extensions.
Now we can run the following command: python3 -m molecule test.
What this does it runs a full lifecycle sequence, which means that it will check all the stages below:
Molecule full lifecycle sequence
└── default
├── dependency
├── cleanup
├── destroy
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
├── cleanup
└── destroy
Output molecule test
This output is an overview how on what the testing flow is, content can differ from what is in the ansible role itself. The output from below is with a simple task to install nginx.
Starting galaxy collection install process
Nothing to do. All requested collections are already installed. If you want to reinstall them, consider using `--force`.
[1;35m[WARNING]: Could not match supplied host pattern, ignoring: molecule[0m
PLAY [Destroy molecule containers] *********************************************
[36mskipping: no hosts matched[0m
PLAY [Remove dynamic molecule inventory] ***************************************
TASK [Remove dynamic inventory file] *******************************************
[32mok: [localhost][0m
PLAY RECAP *********************************************************************
[32mlocalhost[0m : [32mok=1 [0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
playbook: /Users/sebas/Desktop/ansible/my_role/extensions/molecule/default/converge.yml
PLAY [Create] ******************************************************************
TASK [Create a container] ******************************************************
[33mchanged: [localhost] => (item={'image': 'docker.io/geerlingguy/docker-ubuntu2004-ansible', 'name': 'molecule-ubuntu'})[0m
[33mchanged: [localhost] => (item={'image': 'geerlingguy/docker-fedora39-ansible', 'name': 'molecule-fedora'})[0m
TASK [Add container to molecule_inventory] *************************************
[32mok: [localhost] => (item={'image': 'docker.io/geerlingguy/docker-ubuntu2004-ansible', 'name': 'molecule-ubuntu'})[0m
[32mok: [localhost] => (item={'image': 'geerlingguy/docker-fedora39-ansible', 'name': 'molecule-fedora'})[0m
TASK [Dump molecule_inventory] *************************************************
[33mchanged: [localhost][0m
PLAY RECAP *********************************************************************
[33mlocalhost[0m : [32mok=3 [0m [33mchanged=2 [0m unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
PLAY [Converge] ****************************************************************
TASK [Gathering Facts] *********************************************************
[32mok: [molecule-fedora][0m
[32mok: [molecule-ubuntu][0m
TASK [my_role : Gather facts] **************************************************
[32mok: [molecule-fedora][0m
[32mok: [molecule-ubuntu][0m
TASK [my_role : Get facts] *****************************************************
[36mincluded: /Users/sebas/Desktop/ansible/my_role/tasks/help.yml for molecule-fedora, molecule-ubuntu[0m
TASK [my_role : Install python3-apt] *******************************************
[36mskipping: [molecule-fedora][0m
[32mok: [molecule-ubuntu][0m
TASK [my_role : Update all packages on Red Hat/CentOS/Fedora] ******************
[36mskipping: [molecule-ubuntu][0m
[32mok: [molecule-fedora][0m
TASK [my_role : Update apt cache] **********************************************
[36mskipping: [molecule-fedora][0m
[33mchanged: [molecule-ubuntu][0m
TASK [my_role : Install nginx] *************************************************
[33mchanged: [molecule-fedora][0m
[33mchanged: [molecule-ubuntu][0m
PLAY RECAP *********************************************************************
[33mmolecule-fedora[0m : [32mok=5 [0m [33mchanged=1 [0m unreachable=0 failed=0 [36mskipped=2 [0m rescued=0 ignored=0
[33mmolecule-ubuntu[0m : [32mok=6 [0m [33mchanged=2 [0m unreachable=0 failed=0 [36mskipped=1 [0m rescued=0 ignored=0
PLAY [Converge] ****************************************************************
TASK [Gathering Facts] *********************************************************
[32mok: [molecule-ubuntu][0m
[32mok: [molecule-fedora][0m
TASK [my_role : Gather facts] **************************************************
[32mok: [molecule-fedora][0m
[32mok: [molecule-ubuntu][0m
TASK [my_role : Get facts] *****************************************************
[36mincluded: /Users/sebas/Desktop/ansible/my_role/tasks/help.yml for molecule-fedora, molecule-ubuntu[0m
TASK [my_role : Install python3-apt] *******************************************
[36mskipping: [molecule-fedora][0m
[32mok: [molecule-ubuntu][0m
TASK [my_role : Update all packages on Red Hat/CentOS/Fedora] ******************
[36mskipping: [molecule-ubuntu][0m
[32mok: [molecule-fedora][0m
TASK [my_role : Update apt cache] **********************************************
[36mskipping: [molecule-fedora][0m
[32mok: [molecule-ubuntu][0m
TASK [my_role : Install nginx] *************************************************
[32mok: [molecule-fedora][0m
[32mok: [molecule-ubuntu][0m
PLAY RECAP *********************************************************************
[32mmolecule-fedora[0m : [32mok=5 [0m changed=0 unreachable=0 failed=0 [36mskipped=2 [0m rescued=0 ignored=0
[32mmolecule-ubuntu[0m : [32mok=6 [0m changed=0 unreachable=0 failed=0 [36mskipped=1 [0m rescued=0 ignored=0
PLAY [Destroy molecule containers] *********************************************
TASK [Stop and remove container] ***********************************************
[33mchanged: [molecule-fedora -> localhost][0m
[33mchanged: [molecule-ubuntu -> localhost][0m
TASK [Remove potentially stopped container] ************************************
[32mok: [molecule-ubuntu -> localhost][0m
[32mok: [molecule-fedora -> localhost][0m
PLAY [Remove dynamic molecule inventory] ***************************************
TASK [Remove dynamic inventory file] *******************************************
[33mchanged: [localhost][0m
PLAY RECAP *********************************************************************
[33mmolecule-fedora[0m : [32mok=2 [0m [33mchanged=1 [0m unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[33mlocalhost[0m : [32mok=1 [0m [33mchanged=1 [0m unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[33mmolecule-ubuntu[0m : [32mok=2 [0m [33mchanged=1 [0m unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Conclusion
This was the first time for me using Ansible Molecule, I like how you can test against different OSes. The initial setup was a bit bumpy but that was more on my end then on Ansible Molecule’s end. This is a great way of starting with Ansible testing framework, I will definitely intergrade this in all current and future Ansible roles.
This is the end of the Ansible series. When following all the parts you have the fundamentals of how to use and setup Ansible.
- Tags:
- Ansible