Multiplatform Testing With Molecule and Travis

Recently I had been trying to convert all my Bourne shell scripts into possible Ansible Galaxy Roles so that I can use it every now and then in my playbooks. Normally my regular job requires concentration on FreeBSD based systems only but one of my new gigs actually has a multi platform scenario running CentOS, Ubuntu, Debian, FreeBSD, Arch Linux and what not. So thought about rewriting the shell scripts into reusable multi platform Ansible Roles.

The first place I thought about heading into was Ansible Galaxy which is a platform for sharing Ansible Roles with others. Ths site is nice and clean; has a good option for searching including name, tags and platform. For a network infrastructure the first crucial part is time synching. So I started searching for ntp which supports FreeBSD. There are lots of roles supporting that search. But some are broken, some do not work on that platform, some requires earlier version of Ansible. So rather than finding the needle in the haystack it was better to write my own version. Writing a dirty oneliner or hack for an internal project is one thing and writing and sharing something on the web which will be reused by others is another thing. It requires standardized code writing and build testing. In DevOPS philosophy what we call Continuous Integration/Continuous Deployment. Whenever we are changing a line of code the code is pushed into a testing system for a successfull build; once the build is successful it is deployed into production.

Testing Ansible Roles on multi platforms is not an easy task. For local projects we can create Docker containers to test specially on Linux systems or we can test with Vagrant using Virtualbox. For normal syntax check we can use ansible-playbook with --syntax-check or ansible-lint. But for real world scenario we need a good Testing Framework. molecule is one good framework for the purpose. It has support for multi cloud platforms to deploy our testing. We can use molecule to test locally or on remote cloud platforms or integrate it with additional CI/CD platforms like Travis or Cirrus by creating a pipline from Github or Gitlab. Both of them have commercial versions but for Open Source applications both of them are free. Ansible Galaxy has support for Travis and Github. So if my role is hosted at Github and I have a proper definition of testing in a file called .travis.yml in my root directory; whenever I have code change pushed into the git repository my Ansible role will be tested and the result will be notified in my Ansible Galaxy project. Now the big problem for me is that Travis do not have support for FreeBSD like Cirrus.

So testing for my roles in a multi platform is facing a Road Block. We have to be a bit innovative. molecule supports Vagrant and Vagrant has support for multiple Providers like Virtualbox, Docker, Vmware, libvirt. For a commandline based interface libvirt looks like a good choice. So we are going to create a Vagrant and libvirt interface within our limited access Travis platform; and push our code into that libvirt environment. Let’s jump into the code and see how we can accomplish this with molecule and Travis.

My .travis.yml looks like this.

---
dist: bionic

language: python
python:
  - 3.6

env:
  global:
    - VAGRANT_VER: 2.2.7
  matrix:
    - MOLECULE_DISTRO_BOX: generic/ubuntu1604
    - MOLECULE_DISTRO_BOX: generic/ubuntu1804
    - MOLECULE_DISTRO_BOX: debian/stretch64
    - MOLECULE_DISTRO_BOX: debian/buster64
    - MOLECULE_DISTRO_BOX: centos/7
    - MOLECULE_DISTRO_BOX: centos/8
    - MOLECULE_DISTRO_BOX: generic/freebsd11
    - MOLECULE_DISTRO_BOX: generic/freebsd12

install:
  - sudo add-apt-repository universe && sudo apt update
  - wget https://releases.hashicorp.com/vagrant/$VAGRANT_VER/vagrant_"$VAGRANT_VER"_x86_64.deb
  - sudo dpkg -i vagrant_"$VAGRANT_VER"_x86_64.deb
  - sudo apt install qemu-kvm qemu-utils libvirt-bin libvirt0 libvirt-dev
  - vagrant plugin install vagrant-libvirt
  - pip install molecule molecule-vagrant docker python-vagrant paramiko
  - sudo chmod o+rwx /var/run/libvirt/libvirt-sock

script:
  # Check ansible version
  - ansible --version
  # Check molecule version
  - molecule --version
  # Basic role syntax check
  - ansible-playbook tests/test.yml -i tests/inventory --syntax-check
  - molecule test

notifications:
  webhooks: https://galaxy.ansible.com/api/v1/notifications/

My corresponding molecule/default/molecule.yml looks like the following:

---
dependency:
  name: galaxy
driver:
  name: vagrant
  provider:
    name: libvirt
    type: libvirt
    driver: kvm
    options:
      memory: 2048
      cpus: 2
platforms:
  - name: instance
  box: ${MOLECULE_DISTRO_BOX:-""}
  interfaces:
    - network_name: public_network
      dev: "virbr0"
      type: "bridge"
      mode: "bridge"
provisioner:
  name: ansible
  log: true
verifier:
  name: ansible

In the future I will try to create a CookieCutter template for my roles for easier creation of directory and files layout. Please bear in mind that Travis is supporting the OSS culture with free access to resources and do not abuse this service.