Is there a way to guarantee idempotence for playbooks that use randomly generated variables?
For example, I want to setup my crontabs to trigger emails on multiple servers at different times, so I create random integers using ansible's set_fact module:
tasks:
- set_fact:
first_run_30="{{ 30 | random }}"
run_once: yes
Then apply those generated variables to my crontab using ansible like so:
- name: Setup cron30job
cron: name=cron30job minute={{first_run_30}},{{first_run_30 | int + 30}} job='/bin/bash /cron30job.sh' state=present user=root
environment:
MAILTO: '[email protected]'
MAILFROM: '[email protected]'
This works very well, however, ansible's indempotence principle is, I believe, broken using this strategy because each time a play is made you see a change:
TASK: [Setup cron30job] *****************************************
changed: [127.0.0.1]
Further, in the crontab checking under root each time during three separate runs:
[ansible]# cat /var/spool/cron/root
#Ansible: cron30job
5,35 * * * * /bin/bash /sw/test/cron30job.sh
#Ansible: cron30job
9,39 * * * * /bin/bash /sw/test/cron30job.sh
#Ansible: cron30job
6,36 * * * * /bin/bash /sw/test/cron30job.sh
If there is a workaround, or maybe indempotence just will not be possible in my scenario, I would like to know.
The resource models are idempotent meaning change commands are not run unless needed, and Ansible will bring the system back to a desired state regardless of the actual state – rather than you having to tell it how to get to the state.
Idempotence is a term given to certain operations in mathematics and computer science whereby: an action which, when performed multiple times, has no further effect on its subject after the first time it is performed.
Introducing Idempotence using Ansible Handlers One way of making our task's working idempotent is to ensure it runs only based on a certain condition. In our case, we can consider restarting HTTPD only if the configuration file of the target node is being changed in any way.
To define a variable in a playbook, simply use the keyword vars before writing your variables with indentation. To access the value of the variable, place it between the double curly braces enclosed with quotation marks. In the above playbook, the greeting variable is substituted by the value Hello world!
As of Ansible version 2.3, it’s possible to initialize the random number generator from a seed. This way, you can create random-but-idempotent numbers:
"{{ 59 |random(seed=inventory_hostname) }} * * * * root /script/from/cron"
Source: random number filter
I've used this pattern to produce random cron start times with:
Requires Ansible >=2.3:
cron:
name: "{{some_name}}_{{item.day}}"
state: present
job: "{{some_job}}"
weekday: "{{item.day}}"
hour: "{{item.hour}}"
minute: "{{59|random(seed=inventory_hostname + item.dow)}}"
with_items:
- { day: 0, hour: 3, dow: "sunday" }
- { day: 1, hour: 7, dow: "monday" }
- { day: 2, hour: 1, dow: "tuesday" }
- { day: 3, hour: 5, dow: "wednesday" }
- { day: 4, hour: 2, dow: "thursday" }
- { day: 5, hour: 4, dow: "friday" }
- { day: 6, hour: 7, dow: "saturday" }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With