Testing your ansible roles using travis-CI

06 Apr 2017

Travis + Ansible = match made in heaven

NOTE: The ansible playbook written here can be found at prodicus/ansible-bootstrap-server

Continous Integration

Simply put with each commit that you are making to a shared repository, which is then verified by an automated build. This helps in detection of errors early on.

If you are new to this development style. There are plenty of places which will help you understand this. This practice in itself is quite old. CI/CD anyone?

But I am not writing this to explain what is CI right?

So you made an ansible playbook?

I have talked about Infra as code in some of my earlier blog posts. Automatically provisioning your complete server(s) in minutes is something which every org is trying/has achieved.

If you follow TDD principles, you would be knowing right where I am taking this conversation to.

Here is the directory structure for the ansible role I am going to talk about.

├── .travis.yml
├── ansible.cfg
├── play.yml
├── roles
│ ├── basic_server_hardening
│ │ ├── defaults
│ │ │ └── main.yml
│ │ ├── handlers
│ │ │ └── main.yml
│ │ └── tasks
│ │ └── main.yml
│ ├── create_new_user
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ └── main.yml
│ ├── install_minimal_packages
│ │ └── tasks
│ │ └── main.yml
│ ├── update
│ │ └── tasks
│ │ └── main.yml
│ └── vimserver
│ ├── defaults
│ │ └── main.yml
│ ├── files
│ │ └── vimrc_server
│ └── tasks
│ └── main.yml
└── tests
├── inventory
└── test.yml

If you want to understand how the files are organised. I have written about it in “Organising tasks in roles using Ansible”

Writings tests

I would be running the tests inside the travis build environment.

Why travis?

I like them more! But there are many other good CI/CD providers namely circleCI, bambooCI and some more. Choose whatever fits best to your organisation or personal appeal.

So unlike when I am running the ansible-playbook from the controller node, I would be running the playbook on the localhost. This part would be obvious by now.

Let’s have a look at tests/test.yml

- hosts: localhost
connection: local
become: true
- {role: ../roles/update}
- {role: ../roles/install_minimal_packages}
- {role: ../roles/create_new_user}
- {role: ../roles/basic_server_hardening}
- {role: ../roles/vimserver}

Let’s break it down,

  • hosts: localhost this is simply telling the host/group of hosts which this playbook would be targeting.
  • connection: local would tell ansible to run the tasks on the system itself and not ssh onto to some remote machine for executing the playbook.
  • become: true makes the tasks run as the root user

and the roles part is where we would be organising our roles to be executed sequentially.

Testing against different versions of Ansible

For travis to build your code, it would be requiring a .travis.yml file in the root directory of your project.

In this particular example, the contents of it.

The interesting thing to note here is the list arguments for env here. Travis expands on these environment variables to create multiple build environments one after another.

For this case we have 16 env variables, so there would be 16 seperate builds for the specified ansible versions which this playbook will be tested against.

So for the line


The Build config will be something like, where you can see the "env": "ANSIBLE_VERSION=latest"

Sample build config in travis with build config changing due to env var
  • ansible-playbook -i tests/inventory tests/test.yml --syntax-check:

would check for any syntax errors as obvious from the command itself, helps in checking any errors early on before the build.

  • ansible-playbook -i tests/inventory tests/test.yml -vvvv --skip-tags update,copy_host_ssh_id:

is the line which actually runs the playbook

  • id -u tasdik | grep -q "no" && (echo "user not found" && exit 1) || (echo "user found" && exit 0):

checks whether a user named tasdik exists or not after the playbook has completed its execution.

I have skipped explaining some of the parts in my .travis.yml. You can learn more about build configuration inside travis from the docs

Travis builds will fail, if you try to exceed a particular limit while your build job is running. You can find more about the exact specs and timings in the travis docs talking about build timeouts

Some roles in particular had tasks which would make some network calls which is unnecessary to test in a travis build. I needed to skip them in the builds

Enter ansible tags.

It provides an elegant way to skip or only run tasks with some specified tags.

"ansible-playbook -i tests/inventory tests/test.yml -vvvv --skip-tags update,copy_host_ssh_id"

The above playbook run will run the playbook tests/test.yml on the hosts described on tests/inventory skipping the tags update,copy_host_ssh_id which I have specified inside my tasks.

Would be trying out testing ansible roles on docker next. Stay tuned.

Happy ansibling!

Further reading

Originally published at tasdikrahman.me on April 6, 2017.