The full code review
In this section we explain the full code of the testing template, we take the main.yml and
disect it section by section and explain why its there and what it does.
On which hosts are we testing?
As any other playbook it needs to know on which hosts the play will run.
We passed the hosts to test on by giving the parameter 'test_hosts' with a host or group to test on.
This will be used as hosts for the play, but we add the localhost to the play as well. We do that so we can report
the result of the play to the reporting host from localhost.
---
- name: Prepare for testing
hosts: "{{ test_hosts }},localhost"
any_errors_fatal: true
gather_facts: false
Initialize Variables
Before we do any tests, we need to initialize some variables zo we can find things, like a gi repository to checkout..
We create a specal group 'test' with all the host of this play in it. We set the changed when_false on almost every
task of the main.yml itself, so the play overview will have changes grom the role or test plays only.
We also get the distribution_major_version from the targeted hosts, this is used in the report section.
A fact is set with the inventory os_buildingblock_name and the major_ditrubution_version (eg. 'rhel8') fir use in the report
record that will be written. This is done because you might want to test the same code against rhel8 and rhel9.
pre_tasks:
- name: Set full repo URL from collection_to_test
ansible.builtin.set_fact:
repo_name: "{{ repo_url }}{{ collection_to_test }}.git"
when: inventory_hostname == 'localhost'
- name: Create temp_group
ansible.builtin.add_host:
name: "{{ inventory_hostname }}"
group: test
run_once: true
changed_when: false
- name: Gather distribution facts
ansible.builtin.setup:
gather_subset: 'distribution_major_version'
when:
- tower_job_id is defined
- inventory_hostname != 'localhost'
- name: Set os_fact
ansible.builtin.set_fact:
bb_name: "{{ hostvars[inventory_hostname]['os']['name'] }}{{ hostvars[inventory_hostname]['ansible_distribution_major_version'] }}"
when:
- tower_job_id is defined
- inventory_hostname != 'localhost'
Report we have started the play
This section writes the record stating that the test-run has started (RUNNING), this means here that it has started
if the play breaks anywhere, the always section of the test block ensures that the final message reads (FAILED or SUCCESS).
The report record will only be written if there is a job_id (eg. runs through tower or AWX), from the
command line you can see it runnig obviously.
- name: Gather date_time facts
ansible.builtin.setup:
gather_subset: 'date_time'
when:
- tower_job_id is defined
- inventory_hostname != 'localhost'
- name: Create directory for report file if not exist
ansible.builtin.file:
path: "{{ report_dir }}"
state: directory
mode: '0755'
recurse: true
delegate_to: "{{ report_host }}"
failed_when: false
when:
- tower_job_id is defined
- inventory_hostname == 'localhost'
- name: Write header to central repo file
ansible.builtin.lineinfile:
path: "{{report_dir }}{{ report_file }}"
regexp: "^Collection"
line: "Collection,Env,Role,Result,Job_id,Timestamp,State"
state: present
create: true
delegate_to: "{{ report_host }}"
- name: Set timestamp
ansible.builtin.set_fact:
timestamp: "{{ ansible_date_time.date }} {{ ansible_date_time.time }}"
when: inventory_hostname != 'localhost'
- name: Write initial test fail to central repo file
ansible.builtin.lineinfile:
path: "{{report_dir }}{{ report_file }}"
regexp: "^{{ collection_to_test }}, {{ env_version }}, {{ hostvars['localhost']['bb_name'] }}"
line: "{{ collection_to_test }}, {{ env_version }}, {{ hostvars['localhost']['bb_name'] }}, RUNNING, {{ tower_job_id }}, {{ timestamp }}, 1"
state: present
create: true
changed_when: false
delegate_to: "{{ report_host }}"
when:
- tower_job_id is defined
- inventory_hostname != 'localhost'
Find and run the 'preparation' tasks
The first task is a find in the current directory on localhost for any files that match 'prep_*.yml'.
By default, this should find the following file:
* prep_03_check_credentials.yml
This checks if the defined custom credentials are valid, if any.
Any ansible task you want to run in this stage, is automaticly added if the naming is correct. The order in wich they are added, is the numbering order of the filename.
- name: Find prepare tasks
ansible.builtin.find:
paths: "."
patterns: 'prep*.yml'
register: _preps
when: inventory_hostname == 'localhost'
tasks:
- name: Include preparation tasks
ansible.builtin.include_tasks: "{{ _prep.path }}"
loop_control:
loop_var: _prep
with_items: "{{ _preps.files | sort(attribute='path') }}"
when:
- _preps.files is defined
- inventory_hostname == 'localhost'
Prepare for running the tests
In this section we include the vault role, so we have all passwords we need to run the tests.
Additionally, we include any vars_files we can find so the vars needed for tests are availlable.
Last we do a search on the current directory to find any test plays we have to run.
- name: Run the tests
any_errors_fatal: true
hosts: "{{ [test_hosts] | flatten | join(',') }},localhost"
pre_tasks:
- name: Include vault role
ansible.builtin.include_role:
name: "{{ use_vault_role | join('') }}"
when: use_vault_role is defined
- name: Find vars file
ansible.builtin.find:
paths: "."
patterns: 'vars*.yml'
register: _varsfiles
delegate_to: localhost
- name: Include vars for testing
ansible.builtin.include_vars:
file: "{{ _vars.path }}"
loop_control:
loop_var: _vars
with_items: "{{ _varsfiles.files }}"
when:
- _varsfiles.files is defined
- _varsfiles.matched > 0
- name: Find test playbooks
ansible.builtin.find:
paths: "."
patterns: 'test*.yml'
register: _tests
delegate_to: localhost
Finally run the tests
So now we include all test files we found in the previous section and include them into
the play to run them in sequence. We run this as a block, so we can always do a cleanup
at the end of the play (eg. remove checked out roles).
tasks:
- name: Run as block
block:
- name: Include testcases
ansible.builtin.include_tasks: "{{ _test_case.path }}"
loop_control:
loop_var: _test_case
with_items: "{{ _tests.files | sort(attribute='path') }}"
when:
- _tests.files is defined
- inventory_hostname != 'localhost'
Cleanup any mess
As the tests have run, we clean all roles we checked out, even if the tests failed. And here we overwrite the running state with failed, so any failure is always registered.
always:
- name: Remove vault role
ansible.builtin.file:
path: "roles/{{ use_vault_role | join('') }}"
state: absent
changed_when: false
when:
- use_vault_role is defined
- inventory_hostname == 'localhost'
- name: Remove extra roles
ansible.builtin.file:
path: "roles/{{ _extr_role }}"
state: absent
changed_when: false
loop_control:
loop_var: _extr_role
with_items: "{{ extra_roles }}"
when: inventory_hostname == 'localhost'
- name: "Write test result to central repo file"
ansible.builtin.lineinfile:
path: "{{ report_dir }}{{ report_file }}"
regexp: "^{{ collection_to_test }}, {{ env_version }}, {{ hostvars['localhost']['bb_name'] }}"
line: "{{ collection_to_test }}, {{ env_version }}, {{ hostvars['localhost']['bb_name'] }}, FAILED, {{ tower_job_id }}, {{ timestamp }}, 2"
state: present
changed_when: false
delegate_to: "{{ report_host }}"
when:
- tower_job_id is defined
- inventory_hostname != 'localhost'
Write final record, if we got here
When run through tower or AWX, update the report by replacing the line thet said failed
with a line that says 'SUCCESS', so your report shows the correct result.
When you read the file you will see the correct result, but beware...tools like splunk read every
update and could show you the wrong results...You will have to tweak the report for this behavure.
- name: "Write test result to central repo file"
ansible.builtin.lineinfile:
path: "{{ report_dir }}{{ report_file }}"
regexp: "^{{ collection_to_test }}, {{ env_version }}, {{ hostvars['localhost']['bb_name'] }}"
line: "{{ collection_to_test }}, {{ env_version }}, {{ hostvars['localhost']['bb_name'] }}, SUCCESS, {{ tower_job_id }}, {{ timestamp }}, 0"
state: present
changed_when: false
delegate_to: "{{ report_host }}"
when:
- tower_job_id is defined
- inventory_hostname != 'localhost'
This is all there is...
Happy testing (and maybe happy managers)