Automation

TravisCI pipeline to test Ansible roles with Molecule on AWS EC2.

5 min

Automation is awesome!” – This is one of the slogans that I read more and more often on blogs and articles dealing with DevOps and CI / CD. I agree: the today possibilities to automate integration and deployment processes are incredible.

Suppose, for example, that we have to automate the installation of Apache on Ubuntu. We write an Ansible role to carry out this operation and we can reuse it as a building block of a larger automated process, such as deploying a complete solution.

One wonders: how to test the role during its development and subsequent updates? Having recently faced a similar challenge, I had the opportunity to create a CI pipeline using very powerful (and free!) tools. Let’s see.

Ansible Role

If Ansible is not already installed in your system, please install it.

We are going to create our my-httpd script in the simplest way possible: we create all the directories that normally describe Ansible roles, as part of an awesome-ci example project.

mkdir -p awesome-ci/roles/my-httpd
cd awesome-ci/roles/my-httpd
mkdir defaults files handlers meta templates tasks vars

In the tasks directory we create the main.yml file with the Ansible code for installing Apache. In the project’s root directory we create a playbook.yml which can use our role. The content of the two files will be for example:

Now that we have written all the code needed to install Apache with Ansible, let’s focus on building our pipeline, starting with the first tests.

Code linting

What is code linting? Wikipedia suggests us to be “a tool that analyzes source code to flag programming errors, bugs, stylistic errors, and suspicious constructs“.

ansible-lint allows us to perform this type of verification on our playbook and role. To install it use PIP:

pip install ansible-lint

To check our playbook, move to the project’s root directory and simply run:

ansible-lint playbook.yml

ansible-lint verify the content of the playbook and the roles used against certain rules, reporting any errors and violations of best practices, like this:

[201] Trailing whitespace
/home/awesome-ci/roles/my-httpd/tasks/main.yml:8
        name: httpd 

Each violation is identified by a code (in this case 201). The related documentation describes causes and solutions. In my lucky case, the only violation reported concerns a simple space at the end of the indicated line. It is possible to ignore some rules by specifying their identification codes on the command line.

ansible-lint playbook.yml -x 201

Whenever we change our role, we can use ansible-lint to check it again against appropriate best practices. But let’s not stop here! The time has come to check whether our role actually does what we need.

Molecule

Molecule is a tool designed to test your Ansible roles, in different scenarios and with different operating systems, relying on different virtualization and Cloud providers.

To install it, use PIP. However, I recommend checking the official documentation for installation requirements.

pip install molecule

At the end of the installation process, we are ready to recreate our role using Molecule.

# Save old role
mv my-httpd my-httpd-orig

# Init new role using Molecule
molecule init role -r my-httpd -d docker

# Move task file to new role
cp my-httpd-orig/tasks/main.yml my-httpd/tasks/
rm -r my-httpd-orig 

With the role initialization command, we instructed Molecule to use docker as a test environment. Let’s move to the role directory to check it!

cd my-httpd
molecule test

Unfortunately we will immediately see that our test fails during the linting phase. Molecule creates a role using ansible-galaxy, therefore complete with all the necessary directories, META included. So we need to properly correct META by replacing the generic information reported. Alternatively, ansible-lint can be instructed to ignore some rules. Let’s edit the Molecule configuration file.

The molecule directory of our role includes a subdirectory for each test scenario. The scenario we created is default. In this folder the molecule.yml reports the configuration of each phase, including the linting one. We add the option to exclude certain rules.

provisioner:
  name: ansible
  lint:
    name: ansible-lint
    options:
      x: [201, 701, 703]

Let’s run the test again: the code linting phase is successfully passed but we run into another error.

fatal: [instance]: FAILED! => {"changed": false, "cmd": "apt-get update", "msg": "[Errno 2] No such file or directory", "rc": 2}

The reason? Not so immediately identifiable, but by analyzing a bit the molecule configuration file that we have previously modified, we realize that the docker image used to test the role is based on CentOS. Instead we decide to use Ubuntu 18.04, for which the “apt-get update” command makes sense.

platforms:
  - name: instance
    image: ubuntu:18.04

Let’s run the test again and … boom! Passed! However, we are still missing something: the operations indicated in our role are carried out without errors but we have not actually taken care of verifying that the Apache2 service is installed and active. We can configure Molecule to check it for us at the end of the playbook execution, regardless of the actions taken in the role.

We need to edit the test_default.py file located in the Molecule tests directory.

Let’s run the test again paying attention to the “verify” action.

--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in /home/awesome-ci/roles/my-httpd/molecule/default/tests/...
    ============================= test session starts ==============================
    platform darwin -- Python 3.8.1, pytest-4.6.4, py-1.8.1, pluggy-0.13.1
    rootdir: /home/awesome-ci/roles/my-httpd/molecule/default
    plugins: testinfra-3.3.0
collected 2 items                                                              
    
    tests/test_default.py ..                                                 [100%]
    
    =========================== 2 passed in 3.14 seconds ===========================
Verifier completed successfully.

Great! Let’s recap:

  1. We configured Molecule to verify our role through ansible-lint.
  2. The role is used by Molecule in a playbook to install and start Apache in an Ubuntu based docker container.
  3. Upon completion, Molecule verifies that Apache is actually installed and started.

All in an automated way! The time has come to move to the Cloud. Why not try our role in a totally different scenario? For example on AWS EC2?

Molecule & AWS EC2

To test our role on AWS we need to create a new Molecule scenario. From the role directory we initiate the new aws-ec2 scenario with:

molecule init scenario -d ec2 -r my-httpd -s aws-ec2

As you can see, this time we use ec2 as a test environment. The molecule.yml file is obviously different from the previous scenario, because includes specific settings for the AWS platform.

platforms:
    - name: instance
      image: ami-0047b5df4f5c2a90e     # change according to your EC2
      instance_type: t2.micro
      vpc_subnet_id: subnet-9248d8c8   # change according to your VPC

The AMI image and subnet specified in the configuration file must be modified in accordance with the region that you intend to use for the test. To find the correct Ubuntu AMI I recommend using this site.

The configurations provided in the docker scenario also apply: specify the exclusions in the code linting rules and the end-of-deployment tests for the verification of the Apache service.

We must specify AWS regions and credentials: it can be done by setting some environment variables before starting the test with Molecule.

export EC2_REGION=eu-west-2
export AWS_ACCESS_KEY_ID=YOUR_ACCESSKEY
export AWS_SECRET_ACCESS_KEY=YOUR_SECRETACCESSKEY 

molecule test

Success! We quickly created a new scenario and are also able to test our role on AWS. On EC2 console we are able to see that Molecule take care of starting a new virtual machine, run the test playbook on it and verify role activities. At the end of the operation, Molecule terminate the EC2 instance.

Now let’s see how to create a CI pipeline to execute these tests with Molecule on AWS at every push of our GitHub source repository,

Travis CI

With TravisCI is possible to configure the CI pipeline for the GitHub repository which hosts our automation project. The configuration is done by creating the .travis.yml file in root.

In the install section, the test-requirements.txt file lists the packages to be installed. Its content is:

ansible-lint
molecule
boto 
boto3

The script section indicates the operations to be executed: ansible-lint is used preliminarily for the playbook and role checks; Molecule is used to test the role in the AWS-EC2 scenario.

Something is missing? Yes, AWS credentials. To share them securely we use Travis’ CLI. First let’s install it:

gem install travis

We proceed to encode the credentials securely, adding them to the Travis configuration file. Credentials will be passed in the form of environment variables to Molecule during the execution of the tests.

travis login --pro

travis encrypt AWS_ACCESS_KEY_ID="<your_key>" --add env.global --pro
travis encrypt AWS_SECRET_ACCESS_KEY="<your_secret>" --add env.global --pro

Let’s try a build with TravisCI and … bingo! We finished.

Conclusions

We have seen how the use of these tools allows you to develop Ansible roles and playbooks by constantly subjecting them to a pipeline capable of verifying their correctness, on completely different environments and scenarios.

I have used the same tools in a bigger project, available on this GitHub repository: it is an Ansible playbook dedicated to the creation of a Docker Swarm cluster.

We had fun? See you next time!

Leave a Comment