This blog post will extend the actions we described on this https://werner-dijkerman.nl/2016/07/10/testing-ansible-roles-with-molecule-testinfra-and-docker/ page. We only configured a very basic configuration with 2 tests in the previously mentioned page, but thats not enough to and on this page we will continue to complete the tests. Github page for Molecule (In case you forgot 😉 )
We will discuss the following in this blogpost:
- Make the TestInfra tests OS aware
- Use group_vars
- Role dependencies
- Configure Travis
Lets dive into the tasks.
TestInfra OS Aware
Ok, not really molecule related, but very important if our role needs to work on several different operating systems. In the earlier mentioned blogpost we had configured Molecule to use a Ubuntu docker image. Before we can make the tests OS aware, we have to add some docker containers to the configuration which have different operating systems.
We create the following configuration:
docker: containers: - name: zabbix-agent-centos ansible_groups: - group1 image: milcom/centos7-systemd image_version: latest privileged: True - name: zabbix-agent-debian ansible_groups: - group2 image: maint/debian-systemd image_version: latest privileged: True - name: zabbix-agent-ubuntu ansible_groups: - group2 image: rastasheep/ubuntu-sshd image_version: latest privileged: True
We have 3 docker containers configured: Ubuntu, Debian and CentOS. I’ve searched for Docker containers that have systemd configured, as the Zabbix Agent uses this. The official docker images of the mentioned OS’es doesn’t have the systemd enabled/configured.
We would like to run the ‘molecule test’ command, but it will fail on the ‘verify’ part.
We currently have this test:
def test_zabbix_package(Package, SystemInfo): zabbixagent = Package('zabbix-agent') assert zabbixagent.is_installed assert zabbixagent.version.startswith("3.0")
This test will validate if Zabbix is installed and the version starts with 3.0 (Thats the default version to be installed). When running this test, it will fail on the Debian and Ubuntu host. Why?They use a slightly different version naming than CentOS.
We have to update the function by adding the SystemInfo class to the function:
def test_zabbix_package(Package, SystemInfo): zabbixagent = Package('zabbix-agent') assert zabbixagent.is_installed if SystemInfo.distribution == 'debian': assert zabbixagent.version.startswith("1:3.0") if SystemInfo.distribution == 'centos': assert zabbixagent.version.startswith("3.0")
Now we have added the SystemInfo class to the function, we can use this to determine which tests we can execute on which OS. In the above example we see that if the distribution ‘debian’ is, we validate if the version starts with ‘1:3.0’. With CentOS we validate if it is ‘3.0’. When we execute the test on all 3 hosts, it will run successfully.
Using group_vars
This part also applies to using ‘host_vars’, just replace the word ‘group_vars’ with ‘host_vars’ 😉 We configure the molecule.yml file by adding some group_vars related data. We configure the 1st block in the molecule.yml like this:
ansible: playbook: playbook.yml group_vars: mysql: database_type: mysql database_type_long: mysql postgresql: database_type: pgsql database_type_long: postgresql postgresql_pg_hba_conf: - "host all all 127.0.0.1/32 trust" - "host all all ::1/128 trust" postgresql_pg_hba_local_ipv4: false postgresql_pg_hba_local_ipv6: false
This setup will “create” 2 group_var files: mysql and postgresql. When we run a molecule command, the group_vars will be created in the .molecule directory. Lets run a ‘molecule list’ command. For know we ignore the output, but lets see what is created in the .molecule directory:
[vagrant@localhost ansible-zabbix-server]$ find .molecule -type f | xargs ls -lrt -rw-rw-r--. 1 501 games 3 Jul 17 20:31 .molecule/state -rw-rw-r--. 1 501 games 215 Jul 17 20:31 .molecule/group_vars/postgresql -rw-rw-r--. 1 501 games 51 Jul 17 20:31 .molecule/group_vars/mysql [vagrant@localhost ansible-zabbix-server]$ cat .molecule/group_vars/mysql --- database_type: mysql database_type_long: mysql [vagrant@localhost ansible-zabbix-server]$ cat .molecule/group_vars/postgresql --- database_type: pgsql database_type_long: postgresql postgresql_pg_hba_conf: - host all all 127.0.0.1/32 trust - host all all ::1/128 trust postgresql_pg_hba_local_ipv4: false postgresql_pg_hba_local_ipv6: false
Within the .molecule directory a group_vars directory is created and 2 files are present (We will ignore the ‘state’ file). The output of these files are the same as how we configured the molecule.yml file.
Role dependencies
When your role has some dependencies, we really want them to download before we execute our role in Molecule. It will fail if we don’t do this. We have to download them first.
Molecule has a simple configuration for this. Within the molecule.yml file, we add the ‘requirements_file’ option in the Ansible configuration. Our example now looks like this:
dependency: name: galaxy requirements_file: requirements.yml options: ignore-certs: True ignore-errors: True ansible: playbook: playbook.yml group_vars: mysql: database_type: mysql database_type_long: mysql postgresql: database_type: pgsql database_type_long: postgresql postgresql_pg_hba_conf: - "host all all 127.0.0.1/32 trust" - "host all all ::1/128 trust" postgresql_pg_hba_local_ipv4: false postgresql_pg_hba_local_ipv6: false
In the root directory of the repository, a file called ‘requirements.yml’ is found which contains the dependencies.
When we run the ‘converge’ subcommand, the roles will be downloaded and after this the role is executed:
[vagrant@localhost ansible-zabbix-server]$ molecule converge WARNING:vagrant:The Vagrant executable cannot be found. Please check if it is in the system path. --> Installing role dependencies ... - downloading role 'apache', owned by geerlingguy - downloading role from https://github.com/geerlingguy/ansible-role-apache/archive/1.7.2.tar.gz - extracting geerlingguy.apache to .molecule/roles/geerlingguy.apache - geerlingguy.apache was installed successfully - downloading role 'mysql', owned by geerlingguy - downloading role from https://github.com/geerlingguy/ansible-role-mysql/archive/2.3.0.tar.gz - extracting geerlingguy.mysql to .molecule/roles/geerlingguy.mysql - geerlingguy.mysql was installed successfully - downloading role 'postgresql', owned by galaxyprojectdotorg - downloading role from https://github.com/galaxyproject/ansible-postgresql/archive/0.9.2.tar.gz - extracting galaxyprojectdotorg.postgresql to .molecule/roles/galaxyprojectdotorg.postgresql - galaxyprojectdotorg.postgresql was installed successfully --> Starting Ansible Run ... PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [zabbix-server-centos-mysql] ... <snip>
Configure CI
We don’t want to run manually the molecule commands every time a change occurs. To quote Apple: “There is an app for that”. Well almost. I explain 2 ways of running Molecule in a automated way: Using Travis and Using Jenkins.
Travis
A commonly known cloud platform for CI, and it is very simple to make use of it. The Molecule documentation has a very nice example on how to configure the travis setup. We create in the root directory of our role, a file named: .travis.yml
sudo: required language: python services: - docker before_install: - sudo apt-get -qq update - sudo apt-get install -o Dpkg::Options::="--force-confold" --force-yes -y docker-engine install: - pip install molecule ansible docker script: - molecule test notifications: webhooks: https://galaxy.ansible.com/api/v1/notifications/
Its a very basic travis configuration. First block is to let Travis know that we need to make use of sudo, that the language Python is and we use the ‘docker’ service.
2nd block is to update the Ubuntu’s apt cache and install Docker. When Docker is installed, we install molecule and after that, molecule test is executed. When the job is finished, we send some data via a web hook to the Ansible Galaxy page. The last step is to show the ‘passing’ (Or failing) badge on the Ansible Role page.
Jenkins
This example will show you a basic Jenkinsfile. I don’t do a single action manually in Jenkins. A Jenkins job is basically code and we should use it as code. With Jenkins 2, we can make use of a Jenkinsfile (Also with Jenkins 1, but was named jenkins-template I believe?)
When you have a Jenkins server running, create a new Pipeline job and configure the correct git repository. (Okay, for this blogpost we do this manually, but I have a playbook for this. So this is on my part automated as well. 😉 )
A basic example:
node(){ stage 'INFO: Checkout code' checkout scm stage 'Installing Molecule' sh 'sudo pip install molecule' stage 'Creating Containers' sh 'molecule create' stage 'Installing the Role' sh 'molecule converge' stage 'Idempotence check' sh 'molecule idempotence' stage 'Verify the application' sh 'molecule verify' stage 'Destroy the containers' sh 'molecule destroy' }
When the job is executed, it will first download the Jenkinsfile from the configured git repository and the tasks will be executed one by one. This is just to get you started and the Jenkinsfile should be extended with some error handling like e-mail configuration etc.
So this was the follow up on the original blogpost for how to use Molecule with your Ansible role. The guys are really busy with Molecule so Molecule grows really fast. Maybe doing an 3rd blogpost very soon with new features.. 🙂
Pingback: Testing Ansible roles in a cluster setup with Docker and Molecule – werner-dijkerman.nl