Test Ansible Roles using Molecule and Podman
Written by Ilkka TengvallI needed to have testing added to Ansible roles. There are various people in the customer organization developing roles, and we want a lightweight, easy to use test process to unify the looks and quality of the roles.
Solution
Meet Molecule with Podman plugin and Ansible as test language!
I created also a short video about this as a support material:
Benefits of podman for containers and ansible for tests
- Containers are slim and quick
- Podman requires no root, which makes it easily part of CI/CD
- Easy testing language
We are testing ansible roles by the people who write ansible roles, so writing test cases couldn’t get simplier than writing them in ansible too! And since molecule has developed since Peter’s earlier blog using VMs, now we can use podman as molecule plugin to spin up tests quickly in containers. And thanks to podman, no root gets hurt during the process. Which means we can put this safely into the CI/CD process, because of everything gets run as a user.
This hands on is done using RHEL8 Linux. However, you can run different container images for testing the different Linuxes. Also, if Solaris etc… is needed, you need to run those cases in VMs like Peter explained earlier. You can however use ansible as a test language, instead of Test Infra.
Wait, isn’t there quite much to put into one blog?
Sure, and molecule has been blogged a lot about already as well as ansible and podman. So instead of going into those, check out e.g. these blogs about them:
- Practical Ansible Testing with Molecule, Fabian von Feilitzsch is good read to understand the molecule tree structure
- Jeff Geerling’s two Vlogs about Ansible and Molecule are great, and I recommend you to have his book Ansible for DevOps, it’s there in written.
In this blog I shortly describe the process of running all this in podman on RHEL8, as that’s the use case at hand. I trust you learn molecule and ansible from elsewhere. By making this public, I hope you can reuse it in your work.
Let’s go through the process
We use RHEL8 in the guide, along with RHEL8 UBI container images. RHEL is set up with required repos, see ansible playbook.
Install Podman, Ansible and required tools for python
sudo dnf install -y podman ansible python3 python3-virtualenv gcc git
Install Molecule
Create a python virtual environment where we install all molecule tools. We use pip to install python modules, and python virtual environment to keep them separate from system modules.
python3 -m virtualenv molecule_ansible
source molecule_ansible/bin/activate
pip install ansible testinfra molecule podman python-vagrant ansible-lint \
flake8 molecule[lint] molecule[podman]
Apply molecule to ansible role
Please note that the molecule test we are going to do require you to be the root
user!
Activate molecule virtual environment if already didn’t:
source molecule_ansible/bin/activate
Create a new role by using molecule to get the basic molecule files in place. This will run ansible-galaxy init role in background, and will add the initial molecule config and test files.
molecule init role my-new-role --driver-name podman
If you have an existing ansible role, and want to add molecule testing to that role, run:
molecule init scenario -r ftp --driver-name podman
If you are a bit lazy and just want to go on with testing molecule, git clone the below example role with added molecule testing:
git clone https://github.com/RedHatNordicsSA/molecule-podman-blog.git
Read the following chapters and play with it by modifying the settings and test cases.
Molecule
Now let’s get going. Investigate how:
- container gets prepared for the test in the converge.yml file. In this very simple case it includes the role to handle FTP.
- molecule is set up to use podman, and a container that has init in it. This is in the molecule.yml file.
- tests are written in ansible in verify.yml file.
Molecule options
Notice how the podman is defined as driver, and init container is set with required options, and ansible is set to be the test tool:
driver:
name: podman
platforms:
- name: instance
image: ubi8/ubi-init
pre_build_image: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:ro
privileged: true
command: "/usr/sbin/init"
provisioner:
name: ansible
verifier:
name: ansible
You can see the selected tests also in the molecule.yml.
Ansible test cases
Test cases are trivial to write with ansible. In this case it just runs install and deactivate commands for the service in check-only mode, and verifies if it would have needed to apply changes. In our case a change would show a failure, as everything should be already setup. So it’s ok if nothing would have needed a change.
- name: check if vsftpd is installed
package:
name: vsftpd
state: present
check_mode: yes
register: pkg
- name: fail if package was not installed
assert:
that:
- pkg.changed is false
fail_msg: "Package vsftpd was not installed!"
success_msg: "Package vsftpd was installed."
Podman
You don’t need to know about podman in this case. I’d only add image pruning to be done once in a while to the test machine, if it’s a permanent VM. You could be running these tests in Ansible Tower or Jenkins machine, in which case you want to prune the images. Remember, no root required, so podman gives you a lot of options where to run it.
Here’s how it looks like when the container is started step by step. Podman has pulled the image, and is running a test container. You can do only those parts by the folliwing commands:
(molecule_ansible) [cloud-user@localhost ftp]$ molecule converge
(molecule_ansible) [cloud-user@localhost ftp]$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
712c7b4ac78c registry.access.redhat.com/ubi8/ubi-init:latest /usr/sbin/init 33 seconds ago Up 33 seconds ago instance
(molecule_ansible) [cloud-user@localhost ftp]$ molecule login
[root@712c7b4ac78c /]# whoami
root
[root@712c7b4ac78c /]# exit
(molecule_ansible) [cloud-user@localhost ftp]$ molecule destroy
Executing the test
First and foremost, this is enough to run the tests:
(molecule_ansible) [cloud-user@localhost ftp]$ molecule test
In our case it has a happy ending:
TASK [check if vsftpd is installed] ********************************************
ok: [instance]
TASK [fail if package was not installed] ***************************************
ok: [instance] => {
"changed": false,
"msg": "Package vsftpd was installed."
}
TASK [check service is stopped] ************************************************
ok: [instance]
TASK [fail if service was activated] *******************************************
ok: [instance] => {
"changed": false,
"msg": "Service vsftpd was disabled."
}
TASK [test result] *************************************************************
ok: [instance] => {
"msg": "FTP daemon was installed and disabled. Test OK"
}
PLAY RECAP *********************************************************************
instance : ok=6 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
See Molecule command documentation for details.
Additional Molecule commands
These commands are all executed by the molecule test
, but sometimes it’s
handy to run them separately. E.g. when writing the tests, or debugging failed
tets.
molecule lint
: check the code syntaxmolecule create
: create container for the test setupmolecule list
: check the container is runningmolecule converge
: run the tests in the running containermolecule idempotence
: test idempotence by running the role in loopmolecule login
: execute shell into test container to inspect itmolecule destroy
: delete the test container
Conclusion
It is really convenient to run tests in light weight manner. I do not go into CI/CD tooling in this case. My customers are often in disconnected environments where they have each different set of tools. This method should be applicable to adopt to many of them.
Good luck in testing, may the tests be successful for you!
BR, Ilkka
I work as an SA at Red Hat Nordics, mainly with speeding things up using automation and hybrid cloud.