Define what `diff` means for Ansible task
I was inspired to dig into this topic by this ServerFault question.
So you have some Ansible task that supports diff
output (e.g. copy
).
But imagine that you copy files that are not human readable by default (like pem-certificates, for example).
In this case Ansible will print some base64 "garbage" as task's diff:
--- before: /tmp/cert.pem
+++ after: /path/to/cert.pem
@@ -1,13 +1,13 @@
-----BEGIN CERTIFICATE-----
-MIIB7DCCAVWgAwIBAgIJAMUAMA8xDTALBgNV
-BAMMBHRlc3QwHhcNMTcxMDQ1WjAPMQ0wCwYD
-VQQDDAR0ZXN0MIGfMA0GCSCmJCHr6m0CCEjE
-mtDD1EHUAo8y0jN9lYdF251k07WH1MKVh2tQ
-nxywqt1ktrqI/CPJqItKAerOh897GkOw06Rk
-/ufGeYFxr/Ff+fVaL4qq0g==
+MIIB7DCCAVWgAwIBAgIJCwUAMA8xDTALBgNV
+BAMMBHRlc3QwHhcNMTcxODU0WjAPMQ0wCwYD
+VQQDDAR0ZXN0MIGfMA0GgQChH7tIRNrbLEz6
+kPwL+YijDVXgHUjp2rc9j0pbs04s/oJ9L3ah
+bwFcpP3FmyabwiWgVOR8KyaITlyBuExhH83D
+zPf/JYnGvJw/q94q6/6cEg==
-----END CERTIFICATE-----
If we make things simple, every module that support diff
returns before
and after
strings, that are processed with Python's difflib.unified_diff in default stdout callback.
So to tackle our problem we either need to:
- alter
before
andafter
strings on their way from module - alter
before
andafter
strings while they are processed by callback
If you need to modify a single module, you can write an action plugin on top of the module, or even subclass the original action plugin (like copy
) that will alter those strings.
But if we want a more general approach and be able to override diff processing for any task/module, we need to modify callback function.
Here is prediff.py callback plugin that subclasses Ansible's default stdout callback.
The only method that is overridden is v2_on_file_diff
. I check for prediff_cmd
in task variables and if it's there, execute this command to modify before
/after
strings.
This way if there is no prediff_cmd
variable defined everything work as usual, but if prediff_cmd
is there before
/after
are preprocessed with this command before executing unified_diff
.
To allow easy use of prediff_cmd
I dump strings into temporary file with its path available as %s
placeholder.
Example playbook may look like:
- hosts: localhost
gather_facts: no
tasks:
- copy:
src: cert.pem
dest: /tmp/cert.pem
vars:
prediff_cmd: openssl x509 -in %s -noout -text | head -n 10
Next we set our prediff
callback as stdout plugin:
ANSIBLE_STDOUT_CALLBACK=prediff ansible-playbook --check --diff test.yml
And get nice human readable diff:
--- before: /tmp/cert.pem
+++ after: /path/to/cert.pem
@@ -2,9 +2,9 @@
Data:
Version: 3 (0x2)
Serial Number:
- c6:40:43:a5:99:0d:63:ac
+ db:e2:e2:8e:a2:b2:49:1f
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=test
Validity
- Not Before: Oct 21 09:18:45 2017 GMT
- Not After : Oct 21 09:18:45 2018 GMT
+ Not Before: Oct 21 09:18:54 2017 GMT
+ Not After : Oct 21 09:18:54 2018 GMT