Writing Tests

goodplay builds upon playbooksAnsible‘s configuration, deployment, and orchestration language.

Ansible Terminology

Quoting from Ansible’s documentation:

At a basic level, playbooks can be used to manage configurations of and deployments to remote machines. At a more advanced level, they can sequence multi-tier rollouts involving rolling updates, and can delegate actions to other hosts, interacting with monitoring servers and load balancers along the way.

A pseudo playbook – written as a YAML file – may look like this:

## playbook_name.yml
# play #1
- hosts: host1:host2
  tasks:
    # play #1, task #1
    - name: first task name
      module1:
        arg1: value1
        arg2: value2

    # play #1, task #2
    - name: second task name
      module2:
        arg1: value1
        arg2: value2
      tags: specialtag

# play #2
- hosts: host3
  tasks:
    # play #2, task #1
    - name: another task name
      module1:
        arg1: value1

Each playbook is composed of one or more plays.

Each play basically defines two things:

  • on which hosts to run a particular set of tasks, and
  • what tasks to run on each of these hosts.

A task refers to the invocation of a module which can be e.g. something like creating a user, installing a package, or starting a service. Ansible already comes bundled with a large module library.

Writing Test Playbooks

After we have briefly introduced the basic terminology of the Ansible language, it is now time to define what a test playbook looks like in the goodplay context.

A test playbook is as the name implies a playbook with the following contraints:

  1. The filename is prefixed with test_.
  2. The filename extension is .yml.
  3. Right beside the test playbook a file or directory named inventory exists. See Defining Environment for details.
  4. If you want to test against Docker containers you may optionally put a docker-compose.yml file right beside the test playbook.
  5. The test playbook contains or includes at least one task tagged with test, also called test task.
  6. Within a test playbook all test task names must be unique.

Basic Example

An example test playbook that verifies that two hosts (host1 and host2 created as Docker containers, each one running centos:centos6 platform image) are reachable:

## docker-compose.yml
version: "2"
services:
  host1:
    image: "centos:centos6"
    tty: True

  host2:
    image: "centos:centos6"
    tty: True

## inventory
host1 ansible_user=root
host2 ansible_user=root
## test_ping_hosts.yml
- hosts: host1:host2
  tasks:
    - name: hosts are reachable
      ping:
      tags: test

The name of the single test task is hosts are reachable. The test task only passes when the task runs successful on both hosts i.e. both hosts are reachable.

Complex Example

A slightly more complicated example making use of more advanced Ansible features, like defining host groups or registering variables and using Ansible’s assert module:

## install_myapp.yml
- hosts: myapp-hosts
  tasks:
    - name: install myapp
      debug:
        msg: "Do whatever is necessary to install the app"
## tests/docker-compose.yml
version: "2"
services:
  host1:
    image: "centos:centos6"
    tty: True

  host2:
    image: "centos:centos6"
    tty: True

## tests/inventory
[myapp-hosts]
host1 ansible_user=root
host2 ansible_user=root
## tests/test_myapp.yml
- include: ../install_myapp.yml

- hosts: myapp-hosts
  tasks:
    - name: config file is only readable by owner
      file:
        path: /etc/myapp/myapp.conf
        mode: 0400
        state: file
      tags: test

    - name: fetch content of myapp.log
      command: cat /var/log/myapp.log
      register: myapp_log
      changed_when: False

    - name: myapp.log contains no errors
      assert:
        that: "'ERROR' not in myapp_log.stdout"
      tags: test

Writing Tests for Ansible Roles

To keep playbooks organized in a consistent manner and make them reusable, Ansible provides the concept of Ansible Roles. An Ansible role is defined as a directory (named after the role) with subdirectories named by convention:

role/
  defaults/
  files/
  handlers/
  meta/
  tasks/
  templates/
  vars/

When writing tests for your role, goodplay expects another subdirectory by convention:

role/
  ...
  tests/

By following this convention, goodplay takes care of making the Ansible role available on the Ansible Roles Path, so you can use them directly in your test playbook.