I created an Ansible role that is setting up cron jobs for a user in a wide variety of Linux distros and versions. The cron jobs to be set up are defined in crontab_entries variable. Also there are some env variables that should be set to every cron job.
One clean solution would be to use ansible.builtin.cron module with env: true, which sets up env vars for the entire crontab file. Although there are hosts in the inventory with cron implementations which do not support this, so the fallback solution would be to insert variable declarations into the cron jobs. For example, replacing
job: db/bin/start.sh with job: "export HOME=/tmp; export SHELL=/bin/bash ; {{ script_root }}/db/bin/start.sh"
crontab_entries:
- name: "OS settings"
job: "os-linux/bin/start.sh"
- name: "app settings"
job: "app-linux/bin/start.sh"
- name: "db settings"
job: "db/bin/start.sh"
cron_env:
- var: HOME
value: /tmp
- var: shell
value: /bin/bash
So basically I would need to convert contents of cron_env dict into a string like this:
"export HOME=/tmp; export SHELL=/bin/bash ; "
Can it be done with simple Jinja filters?
Given a list of dictionaries of key var and of value value, you can use a combination of the filters map, zip and join to construct the string you are looking for.
The logic would play as such:
map(attribute='...') to break the dictionaries into two lists, one of the var, the other one of the value.cron_env | map(attribute='var')
and
cron_env | map(attribute='value')
we get:
- HOME
- shell
on one side and
- /tmp
- /bin/bash
on the other.zip those two list back together in order to have a list of list with each var and its corresponding value.cron_env | map(attribute='var')
| zip(cron_env | map(attribute='value'))
will give
- - HOME
- /tmp
- - shell
- /bin/bash
join in map, in order to have a list of strings var=value.map('join', '=')
to get
- HOME=/tmp
- shell=/bin/bash
join each elements with ; export .join('; export ')
to get
HOME=/tmp; export shell=/bin/bash
export at the beginning and a ; at the end.~) is the concatenation symbol in Jinja.
'export ' ~ all_the_above ~ '; '
will finally give
export HOME=/tmp; export shell=/bin/bash;
All together, the task:
- debug:
msg: >-
{{
'export ' ~
cron_env
| map(attribute='var')
| zip(cron_env | map(attribute='value'))
| map('join', '=')
| join('; export ')
~ '; '
}}
vars:
cron_env:
- var: HOME
value: /tmp
- var: shell
value: /bin/bash
Would yield:
ok: [localhost] =>
msg: 'export HOME=/tmp; export shell=/bin/bash; '
Another way could be to add the export and ; to the sub-list with the help of the product filter, then flatten the list of lists.
The advantage of this approach is that, if the cron_env list happens to be empty, the resulting string would be an empty string too.
Here is a task achieving this:
- debug:
msg: >-
{{
['export '] | product(
cron_env
| map(attribute='var')
| zip(cron_env | map(attribute='value'))
| map('join', '=')
) | product(['; '])
| flatten
| join
}}
vars:
cron_env:
- var: HOME
value: /tmp
- var: shell
value: /bin/bash