Test Kitchen – Let’s shake this role up!

Test Kitchen - Let’s shake this role up!

Originally posted on Medium

Test Kitchen

Test Kitchen is a test tool used to, indeed, test your own work before going live! It’s quite amazing how easily you can spin up your test environment - using AWS EC2, Vagrant, Rackspace and so on - and deploy your applications. As I did with the the previous two posts, I’m going to show you, step by step, how to build your test environment for our Ansible Role.

Getting started

Dependencies

The guys who developed Kitchen are quite awesome and not just for the tool per se, but also because they wrote a wonderful documentation that can be found here: http://kitchen.ci/

Although the documentation is freaking awesome, I’m gonna tell you what you need and how to install test kitchen! Below the list of the dependencies you’ll need:

  • Ruby - I'd recommend a version >= 2.2 (and also use RVM rather than any other ruby package)
  • Vagrant
  • Virtualbox
  • Git

Installation

Once you’re all settled, let’s install test kitchen using the ruby gem command:

$ gem install test-kitchen

This will take care of the basic test-kitchen and its dependencies installation but, as you may know, we want to test our Ansible Role therefore we need to install kitchen-ansible and kitchen-vagrant:

$ gem install kitchen-ansible
$ gem install kitchen-vagrant

Kitchen file

All the basic informations we want to provide to Kitchen, regarding mainly the host, are stored within a file called .kitchen.yml.

Let’s take a look at the one I’ve created for our role:

---
driver:
  name: vagrant

provisioner:
  name: ansible_playbook
  hosts: test-kitchen
  require_ansible_repo: true
  ansible_verbose: true
  ansible_version: latest
  require_ruby_for_busser: true
  extra_vars:
    env: local

busser:
  ruby_bindir: /usr/bin

platforms:
  — name: ubuntu-14.04

suites:
  — name: default
  driver:
    network:
      — [“private_network”, {ip: “192.168.33.10”}]

Driver

With driver we want to specify what is going to host our application; for simplicity’s sake, we will create our test environment in a Virtual Machine using one of my favourite combo: Vagrant and VirtualBox.

If you don’t know what Vagrant is or how it works, I highly recommend to visit their website: vagrantup.com

Provisioner

The provisioner is the tool that is going to take care of our product and, since we are writing an Ansible Role, we need Ansible playbook to kick it off.

I mentioned that I also work with Chef and, when I need to test one of my cookbooks, test kitchen can use the chef too.

Just to be clear: we will have a quick overview of what an Ansible Playbook looks like and some of the properties we define in the provisioner block refer to what a playbook uses. Let’s take a very quick look at them:

  • hosts - it’s the group of hosts defined in our inventory file;
  • ansible_verbose - verbosity of the Ansible logs;
  • ansible_version - you can either specify a version or use the latest official release;
  • extra_vars - you can declare an extra var which I usually do to tell Ansible which environment is deploying to.

Don’t worry if some of those aren’t very clear; you’ll get them once we will talk about Ansible Playbooks.

Busser

In provisioner we defined a require_ruby_for_busser which tells Kitchen that we want ruby installed in our box. Why is that? Because after our first provision we want to run our unit test to make sure that we expect to see the right things. With ruby_bindir we specify the path to our ruby executable file.

Platforms

Very easy to understand what this property does, right? Our test environment will use a standard Ubuntu 14.04 vagrant box which, I think, is even more than enough.

Suites

We are going to use one suite only but, let’s say that we want to create two different suites: one for the webserver and one for the database. We wouldn’t use the default one but we’d rather think to create two of them unless we wanted to run a LAMP stack or similar.

As default, test kitchen uses the 127.0.0.1 address but I always prefer to specify a different ip address which allows me to avoid conflicts with other applications running on my local machine. We could also define how to forward some ports and things like this. The ip is set with:

driver:
  network:
    - ["private_network", {ip: "192.168.33.10"}]

Test playbook

As first thing, let’s create our new directories which will contain both the test playbook and the serverspec files:

.
|__ test
|  |__ integration
|     |__ default
|        |__ default.yml
|        |__ serverspec
|           |__ localhost
|           |  |__ test_spec.rb
|           |__ spec_helper.rb

Let’s focus on the file default.yml for now and then we will go ahead with the serverspec ones.

Default playbook

---
- hosts: test-kitchen
  roles:
    - ansible-role-getting_started

Ladies and gentlemen, this is a very first glimpse of an Ansible playbook.

If you remember, we defined the hosts bit in our kitchen file and now we are telling our playbook which host it has to deploy to!

One thing you could usually do with kitchen, is to define a set of variables within the default playbook right below

Serverspec

In order to run our unit test after we created the test environment, we’ll always need a file called spec_helper.rb that looks like something like this:

require ‘serverspec’
set :backend, :exec

This role, now, will need some unit test to make sure that we did everything properly. We are the one who decide which tests have to be run and in particular we are the ones who decide the strategy of our unit tests.

If you want to know more about serverspec, visit http://serverspec.org/

require ‘spec_helper’

if os[:family] == ‘ubuntu’
  describe ‘/etc/apache2/sites-enabled/’ do
    it { should_not be_empty }
  end

#Test the apache vhost
  describe file(“/etc/apache2/sites-enabled/test.conf”) do
    it { should contain(“<VirtualHost *:80>”) }
    it { should be_linked_to “../sites-available/test.conf” }
  end
end

#Check if anything is listening to port 80
describe port(80) do
  it { should be_listening }
end

describe port(8080) do
  it { should_not be_listening }
end

#Check index.html content
describe file(“/var/www/test/index.html”) do
  it { should contain(“test”) }
  it { should contain(“<h1>This Is My First Role</h1>”) }
end

Looking at the first block of code, we can see an if statement which looks for the OS family; if it is ubuntu then it’ll make sure that the apache’s sites-enabled folder is not empty. Yes, it is as simple as you read it.

Right after it we check the vhost itself:

  • first we check whether it contains the line <VirtualHost *:80> exists; then
  • we check if the file it’s linked to the one in the sites-available folder.

Because I’ve always been very keen on having positive and negative test, I decided to write an example of them in our unit tests and the example I’m talking about is the should and should_not be listening instructions. As you can imagine with the first one I make sure that my webserver is listening to the port 80 and, of course, with the second instruction I make sure that it is not listening to port 8080.

Not too much to say on the last block as it is similar to our vhost inspection: we check for the index.html to contain two specific lines we know they must be there as… we wrote it, right?

Let’s start!

Well, it’s all set… so:

$ kitchen create

Will create your vm

$ kitchen converge

Will install all the dependencies (including Ansible) and run your role

$ kitchen verify

Will run your unit test

$ kitchen destroy

Will… well, you know what it does, right?

Alternately you can run the command below:

$ kitchen test

It will run create, converge, verify and destroy in sequence and you can just chill, watch the logs and enjoy your fantastic Ansible Role!

If you liked these three articles, please share them and feel free to clone the role repository from https://github.com/davidedimauro88/ansible-role-getting_started

Spread the love