Many times, people ask, "how can I best integrate testing with Ansible playbooks?" There are many options. Ansible is actually designed
to be a "fail-fast" and ordered system, therefore it makes it easy to embed testing directly in Ansible playbooks. In this chapter,
we'll go into some patterns for integrating tests of infrastructure and discuss the right level of testing that may be appropriate.
..note:: This is a chapter about testing the application you are deploying, not the chapter on how to test ansible modules during development. For that content, please hop over to the Development section.
By incorporating a degree of testing into your deployment workflow, there will be less surprises when code hits production, and in many cases,
tests can be leveraged in production to prevent failed updates from migrating across an entire installation. Since it's push-based and
also makes it very easy to run steps on the localhost or testing servers, Ansible lets you insert as many checks and balances into your upgrade workflow as you would like to insert.
The Right Level of Testing
``````````````````````````
Ansible resources are models of desired-state. As such, it should not be neccessary to test that services are running, packages are
installed, or other such things. Ansible is the system that will ensure these things are declaratively true. Instead, assert these
things into your playbooks.
..code-block:: yaml
tasks:
- service: name=foo state=running enabled=yes
If you think the service may not be running, the best thing to do is request it to be running. If the service fails to start, Ansible
will yell appropriately. (This should not be confused with whether the service is doing something functional, which we'll show more about how to
do later).
Check Mode As A Drift Test
``````````````````````````
In the above setup, `--check` mode in Ansible can be used as a layer of testing as well. If running a deployment playbook against an existing system, using the `--check` flag to the ansible command will report if Ansible thinks it would have had to have made any changes to bring the system into a desired state.
This can let you know up front if there is any need to deploy onto the given system. Ordinarily scripts and commands don't run in check mode, so if you
want certain steps to always execute in check mode, such as calls to the script module, add the 'always_run' flag::
roles:
- webserver
tasks:
- script: verify.sh
always_run: True
Modules That Are Useful for Testing
```````````````````````````````````
Certain playbook modules are particularly good for testing. Below is an example that ensures a port is open::
tasks:
- wait_for: host={{ inventory_hostname }} port=22
delegate_to: localhost
Here's an example of using the URI module to make sure a web service returns::
tasks:
- action: uri url=http://www.example.com return_content=yes
register: webpage
- fail: msg='service is not happy'
when: "'AWESOME' not in webpage.content"
It's easy to push an arbitrary script (in any language) on a remote host and the script will automatically fail if it has a non-zero return code::
tasks:
- script: test_script1
- script: test_script2 --parameter value --parameter2 value
If using roles (you should be, roles are great!), scripts pushed by the script module can live in the 'files/' directory of a role.
And the assert module makes it very easily to validate various kinds of truth::
tasks:
- shell: /usr/bin/some-command --parameter value
register: cmd_result
- assert:
that:
- "'not ready' not in cmd_result.stderr"
- "'gizmo enabled' in cmd_result.stdout"
Should you feel the need to test for existance of files that are not declaratively set by your ansible configuration, the 'stat' module is a great choice::
tasks:
- stat: path=/path/to/something
register: p
- assert:
that:
- p.stat.exists and p.stat.isdir
As mentioned above, there's no need to check things like the return codes of commands. Ansible is checking them automatically.
Rather than checking for a user to exist, consider using the user module to make it exist.
Ansible is a fail-fast system, so when there is an error creating that user, it will stop the playbook run. You do not have
to check up behind it.
Testing Lifecycle
`````````````````
If writing some degree of basic validation of your application into your playbooks, they will run every time you deploy.