4 min read

Testing your Ansible roles with Vagrant

Travis.ci offers a simple and free way to test your Ansible roles but that’s after you’ve pushed and published your code. What if you want to verify how a change looks on a machine or easily see that build error without using an existing machine? This led me down the path of locally provisioning a virtual machine and outside of a normal virtual machine that I have running, I just wanted a standalone build just for a role.

I first went down the Docker route since that’d give me a good starting point but I still enjoy the simplicity of a Vagrant controlled virtual machine. The CLI is incredibly simple and you can easily swap out the base box to test on different distros if you’d like. Also, Vagrant has an ansible_local provisioner that makes it as simple as pointing it at your test playbook to run which I already have in place for Travis.ci.

The setup for this is based off of my Ansible Galaxy logrotate role which I recently added this test virtual machine to verify setup changes as well as to keep backwards compatibility with newer features. Right now when testing it’s a combination of ensuring the role stays as green as possible so that it’s idempodent and verifying template changes on the virtual machine.

In order to get setup locally you’ll need Vagrant and Virtualbox installed as well as Ansible. First, create a new tests directory and then within there a test.yml file which will contain a simple test playbook that’ll run on the virtual machine. We’ll utilize the logrotate role in our playbook but you can use whatever role your testing for, it just needs to be included in the roles list.


---
- hosts: all
  become: True
  vars:
    logrotate_scripts:
      - name: nginx-options
        path: /var/log/nginx/options.log
        options:
          - daily

  roles:
    - ansible-logrotate

The test file isn’t doing much at this point but it’ll end up doing a full role run on the virtual machine with some simple options, next we can setup the Vagrantfile to boot the virtual machine.


# -*- mode: ruby -*-
# vi: set ft=ruby :
@ansible_home = "/home/vagrant/.ansible"

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/trusty64"

  # Copy the Ansible playbook over to the guest machine, run rsync-auto to automatically
  # pull in the latest changes while a VM is running.
  config.vm.synced_folder "../", "#{@ansible_home}/roles/ansible-logrotate", type: 'rsync'

  # The working ansible directory created by ansible_local is owned by root
  config.vm.provision "shell", inline: "chown vagrant:vagrant #{@ansible_home}"

  config.vm.provision "ansible_local" do |ansible|
    ansible.playbook = "test.yml"
  end
end

Within our Vagrantfile we’re doing a few things:

  1. Sycing over the Ansible role that we’re working on into a default Ansible role directory, I’ve found that rsync-auto synced folders are much faster than mounted folders
  2. Ensure that the vagrant user owns the working directory with the shell provisioner
  3. From there we use the ansible_local provisioner provided by Vagrant to run our test playbook with Ansible on the virtual machine

Now that this is in place you can run vagrant up --provision to run the test playbook on the virtual machine. The first run of course will take some time as it downloads the box and adds the guest additions, subsequent runs are fairly fast.

Next we can add an actual test to the playbook and since we’re making changes to the role on our local OS make sure you have rsync-auto running for your virtual machine. This will ensure that your changes are copied over to the machine upon save, this is configured via the Vagrantfile as mentioned above.

Go ahead and add a task that’ll verify that something is in place or checking the state of something. For this example I’m ensuring that doing a config check on the logrotate file that was just put in place doesn’t trigger any errors from the logrotate command. Update your test.yml file so the full version looks like this:


---
- hosts: all
  become: True
  vars:
    logrotate_scripts:
      - name: nginx-options
        path: /var/log/nginx/options.log
        options:
          - daily

  roles:
    - ansible-logrotate

  tasks:
    - name: Verify logrotate config check passes
      shell: logrotate -d "{{ logrotate_conf_dir }}{{ item.name }}"
      with_items: "{{ logrotate_scripts }}"
      register: logrotate_tests
      failed_when: "'error' in logrotate_tests.stderr"

Go ahead and run vagrant up --provision and it’ll run through and you should see green for the initial logrotate steps since those already ran and then yellow for the one task verifying the logrotate file, red would of course be a failure that you’d want to fix.

From here you can continue to make changes to your role, break things, fix things, ssh into the VM to verify, etc. Here’s my workflow as I was building this out for the galaxy role: