'How can I compare a remote file with a reference on controller without failing the playbook when different?

I have a very peculiar problem, in my code I have to compare ansible value with customer expected value and customer expected un_matched value, however the final playbook output should be failed =0 instead of failed =1.

The code is:

- name: show file contents of customer-expects.txt
  debug:
    msg: "{{ lookup('file', '/customer-expects.txt') }}"

- shell: cat /etc/issue
  register: issue
  changed_when: false
  ignore_errors: yes

- assert:
    that:
      - lookup('file', '/customer-expects.txt') == issue.stdout
    success_msg: "matched! {{ lookup('file', '/customer-expects.txt') }} = {{ issue.stdout }}"

- name: show file contents customer-expects_unmatched.txt
  debug:
    msg: "{{ lookup('file', '/customer-expects_unmatched.txt') }}"

- shell: cat /etc/issue
  register: issue
  changed_when: false
  ignore_errors: yes

- assert:
    that:
      - lookup('file', '/customer-expects_unmatched.txt') == issue.stdout
    fail_msg: "unmatched! {{ lookup('file', '/customer-expects_unmatched.txt') }} = {{ issue.stdout }}"
    success_msg: "matched! {{ lookup('file', '/customer-expects.txt') }} = {{ issue.stdout }}"

Output is:

[root@ansible-master /]# ansible-playbook tab8.role.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [This output is for Tab-8 of Function Design document] *********

TASK [Gathering Facts] **********************************************
ok: [ansible-client1]

TASK [issue_tab8 : show file contents of customer-expects.txt] ******
ok: [ansible-client1] => {
    "msg": ""
}

TASK [issue_tab8 : shell] *******************************************
ok: [ansible-client1] => {"changed": false, "cmd": "cat /etc/issue", "delta": "0:00:00.005200", "end": "2022-01-25 14:17:57.070688", "rc": 0, "start": "2022-01-25 14:17:57.065488", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

TASK [issue_tab8 : assert] ******************************************
ok: [ansible-client1] => {
    "changed": false,
    "msg": "matched!  = "
}

TASK [issue_tab8 : show file contents customer-expects_unmatched.txt] *
ok: [ansible-client1] => {
    "msg": "abc"
}

TASK [issue_tab8 : shell] *******************************************
ok: [ansible-client1] => {"changed": false, "cmd": "cat /etc/issue", "delta": "0:00:00.004603", "end": "2022-01-25 14:17:57.674059", "rc": 0, "start": "2022-01-25 14:17:57.669456", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

TASK [issue_tab8 : assert] ******************************************
fatal: [ansible-client1]: FAILED! => {
    "assertion": "lookup('file', '/customer-expects_unmatched.txt') == issue.stdout",
    "changed": false,
    "evaluated_to": false,
    "msg": "unmatched! abc = "
}

PLAY RECAP **********************************************************
ansible-client1            : ok=6    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0


Solution 1:[1]

You are taking this the wrong way and make it unnecessarily complicated IMO.

You have to understand that laying out a playbook in ansible consists of describing the state in which you want to find the target machine. In this particular situation, you want to describe the state in which you want to find a particular file on the target. This is basically done with a task using the copy module. Once you have done that, knowing if the file is similar or different is just a matter of command line switches or task options.

For more on the below explanation see Validating tasks

Ansible has a check_mode which will let you run an entire playbook just to verify what it would do. You can also unconditionally apply that mode to a single task so that it always runs as a check (check_mode: true), or always makes changes to the remote target however the playbook was called (check_mode: false). We are interested here in the first form.

Similarly, ansible has a diff possibility that will let you see what are the differences between the state you described and the modification it (should have to) apply on the target to reach that state.

An example whith your above scenario. I made the test targeting my local machine only but you can get the exact same result on any remote target.

First let's create the reference file we want to check against in our ansible directory.

mkdir -p files
echo "I'm the reference file" > files/ansible_reference.txt

For the example, I'll now create one identical and one different file on our target so that we can compare both cases:

echo "I'm the reference file" > /tmp/ansible_similar.txt
echo "I'm a different file" > /tmp/ansible_different.txt

This is the playbook compare.yml

---
- name: compare remote file to a local reference
  hosts: localhost
  gather_facts: false

  vars:
    local_reference: ansible_reference.txt
    remote_files_2_check:
      - /tmp/ansible_similar.txt
      - /tmp/ansible_different.txt

  tasks:
    - name: Dry run a copy with diff to check if remote file is aligned
      copy:
        src: "{{ local_reference }}"
        dest: "{{ item }}"
      check_mode: true
      diff: true
      loop: "{{ remote_files_2_check }}"

Which gives:

$ ansible-playbook compare.yml

PLAY [compare remote file to a local reference] *****************************

TASK [Dry run a copy with diff to check if remote file is aligned] **********
ok: [localhost] => (item=/tmp/ansible_similar.txt)
--- before: /tmp/ansible_different.txt
+++ after: /home/user/ansible_project/files/ansible_reference.txt
@@ -1 +1 @@
-I'm a different file
+I'm the reference file

changed: [localhost] => (item=/tmp/ansible_different.txt)

PLAY RECAP ******************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

Notes:

  • Although Ansible reports changed for the different file this is only what would happen. Since we are in dry-run for the given task, the above will not change your target. You can run it any number of times, it will report the same result as long as you don't change the reference or the target state.
  • I you are not happy with the default reporting of the task, you can register the result, explore its content and use it in subsequent tasks to meet your exact requirement.
  • Note that the above may report a difference on the permission of the target file. You may have to tune this in the copy module to avoid false positive.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1