2. Scott van Kalken
Everyone just calls me “svk” (it’s easier)
I have the privilege of helping run a
few meetups.
Super passionate about open source (not
just software, but data too)
3. ANSIBLE AND TESTING?!?!?!
Ansible is an awesome toolkit / language to allow you to just get things done.
There are modules and frameworks that allow you to perform testing on roles.
Molecule is one of them.
https://molecule.readthedocs.io/en/stable/
See Rishbah’s fantastic talk on this last time :)
4. ANSIBLE AND TESTING!?!?!?!
What about if I want to do something other than unit testing?
What are my options?
Can I use Ansible?
Is it a good idea to use Ansible?
...someone recently asked me all of these questions and I didn’t have good answers, so I
decided to explore the topic (and do a talk of course).
7. THE ASSERT MODULE
- hosts: "{{ target_hosts | default('localhost') }}"
become: yes
become_user: root
vars:
my_param: 10
tasks:
- name: Check my variable
assert:
that:
- my_param <= 100
- my_param >= 0
fail_msg: "'my_param' must be between 0 and 100"
success_msg: "'my_param' is between 0 and 100"
Custom failure and success messages,
these can be set to anything that you
like.
8. THE ASSERT MODULE
TASK [Check my variable] ********************************
fatal: [localhost]: FAILED! => {
"assertion": "my_param <= 100",
"changed": false,
"evaluated_to": false,
"msg": "'my_param' must be between 0 and 100"
}
TASK [Check my variable] *******************************
ok: [localhost] => {
"changed": false,
"msg": "'my_param' is between 0 and 100"
}
9. A REAL WORLD EXAMPLE
Let’s see what a real world example looks like.
I have a database, and want to make sure that my playbook has been passed all
of the appropriate variables BEFORE I run a task to configure the database.
10. THE PLAYBOOK
- hosts: "{{ target_hosts | default('localhost') }}"
become: yes
become_user: root
tasks:
- assert:
that:
- db_host != ''
- db_port != ''
- db_user != ''
- db_user != 'root'
- db_password != ''
- db_name != ''
tags: ['check_vars']
Assertions around variables that are
set.
This can be used in a playbook at the
beginning to check that variables are
set before running something
There is a trick here though.
Can you see it?
11. THE PLAYBOOK
- hosts: "{{ target_hosts | default('localhost') }}"
become: yes
become_user: root
tasks:
- assert:
that:
- db_host != ''
- db_port != ''
- db_user != ''
- db_user != 'root'
- db_password != ''
- db_name != ''
tags: ['check_vars']
Using the inbuilt tags function of
Ansible, you can run this as a check
before running the playbook.
This is most useful if you are using a
pipeline for deployment!
# ansible-playbook -t check_vars playbook.yml --extra-vars “db_host=foo”
12. CHECK THAT A FILE EXISTS (ASSERT)
- stat:
path: /path/to/something
register: p
- assert:
that:
- p.stat.exists and p.stat.isdir
You can check that a file exists - but can use a logical AND to stack checks (you
can use logical OR also).
14. STANDARD ANSIBLE LOOPS
Don’t forget about standard ansible loops.
Standard ansible loops can be used quite effectively as part of functional
testing if you are waiting for a particular output, or you are waiting for
something to happen.
….and yes because it’s ansible there are a lot of other ways to do this.
15. STANDARD ANSIBLE LOOPS
A CONTRIVED EXAMPLE:
#!/bin/bash
CFGFILE=a.cfg
if [ -f $CFGFILE ]; then
source $CFGFILE
else
echo "COUNT=0" > $CFGFILE
source $CFGFILE
fi
COUNT=$((COUNT+1))
echo $COUNT
echo "COUNT=$COUNT" > $CFGFILE
Script does nothing but increment its
output by one every time it is run.
First run: 0
Second run: 1
Third run: 2
...and so on.
16. STANDARD ANSIBLE LOOPS
The playbook uses a standard
loop until the output from the
command is “3”.
The task will try this 10 times
with a delay of five seconds
between each run.
This is useful if you’re waiting
for something to happen.
- hosts: "{{ target_hosts | default('localhost') }}"
become: yes
become_user: root
gather_facts: false
tasks:
- name: Check output of command and stdout
shell: ./a.sh
register: result
until: result.stdout.find("3") != -1
retries: 10
delay: 5
18. STANDARD ANSIBLE LOOPS
Consider the following example:
● Build Infrastructure
● Install application server
● Wait for application to come up
● Perform functional testing
In this scenario, waiting can be used in combination with assertions to perform
comprehensive functional testing.
19. ROLLING DEPLOYMENT PATTERNS
You can put all of this together to bring a rolling deployment task with
gated testing to your deployments.
For example:
1) Drop application from load balancer
2) Install new version of application
3) Perform functional testing
4) Add application back to load balancer
Using pre tasks, assertions and post tasks, it is possible to set conditions
on failure such that a bad application deployment is automatically tested
and not added back to your live system.
21. ROLLING DEPLOYMENT PATTERNS
- hosts: webservers
pre_tasks:
- name: take out of load balancer pool
command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
roles:
- common
- webserver
- apply_testing_checks
post_tasks:
- name: add back to load balancer pool
command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
22. COOL - I CAN DO TESTING WITH ANSIBLE
Yes you certainly can.
Functional testing.
Microservice testing.
Expect like functionality (for those who know expect)
...and a whole lot more.
23. DON’T CONFUSE DECLARATIVE INTENT & TESTING
Don’t confuse ansible’s declarative intent with testing.
A lot of the time, we may test things in a way to pause before carrying on to
another task.
The declarative intent / enforcement of ansible takes care of the initial thing, but
we need to make sure it has finished before we move on.
Other times we will perform functional testing.
24. DECLARATIVE CONFIGURATION
If you think the service may not be started, the best thing to do is request
it to be started.
- name: Start httpd
systemd:
name: httpd
state: started
If the service fails to start, Ansible will tell you.
25. FUNCTIONAL TESTING
- name: Fetch Java version
shell: java -version 2>&1 | grep version | awk '{print $3}' | sed 's/"//g'
register: java_version
- assert:
that:
- java_version.stdout | version_compare('1.7', '>=')
The snippet above checks something functional (the version of java), but
it could equally be checking a webservice, or the previous example of
checking that AWX has started correctly.
26. SO SHOULD I USE ANSIBLE FOR TESTING?
If you want to have a single language for all of your testing needs.
If you want to test the applications that you’ve deployed - then Ansible is
a good fit.
What @svk has seen:
● There is a (large) company in Melbourne who is currently assessing
running their post application testing using ansible.
● You can embed tests into a pipeline.
● You can make the world a better place by using ansible (of course!)