ansible stuffs - controlling shell tasks using creates, find, and until
The Ansible shell module is a straightforward tool. With some conditionals and functions, it’s usefulness can be improved and tasks can be quite smart. This post explores three Ansible conditionals/functions, creates
, find
, and until
. creates
causes shell to run only if a file does not exist, find
causes shell to run only once if certain output exists, and using until
and find
causes a command to run repeatedly waiting for specific output.
running an Ansible shell command only once - creates
There are times when an Ansible playbook should only run a shell command once. This could be a command that generates dynamic output, like a TLS private key. Using the creates
shell argument, the task can be configured to ONLY run once. Here’s an example from the CFSSL key generation task:
- name: Generate CAs.
shell: >
docker exec cfssl \
sh -c "/go/bin/cfssl gencert -initca /cfssl/json/{{ item }}-ca-csr.json \
| /go/bin/cfssljson -bare /cfssl/certs/{{ item }}-ca"
args:
creates: "{{ volume_path }}/cfssl/certs/{{ item }}-ca-key.pem"
with_items:
- etcd
- etcdclient
- controller
- node
This is fairly well documented by Ansible. The above will only run if the file {{ volume_path }}/cfssl/certs/{{ item }}-ca-key.pem
doesn’t exist. A simpler example would be:
- name: Test the shell args creates.
shell: echo "only one line" > file
args:
creates: file
Pretty simple way of only running a command once. But what if the command does not output any file?
running an Ansible shell command only once - find
So, when running a command that doesn’t create a file, how can this behavior be emulated? Cue using find with the when conditional. In this example, two tasks are run, one to check if a Kubernetes daemonset exists and another to create it if it doesn’t exist:
- name: Check for Weave daemonset.
shell: kubectl --namespace kube-system get daemonset | grep weave | awk '{ print $1 }'
register: weave_daemonset
- name: Deploy the Weave CNI.
shell: kubectl apply -f /data/kubectl/weave-net.yml
when: weave_daemonset.stdout.find('weave') == -1
Neat. Note that it’s good to use awk
with Ansible after using grep
. If grep
does not find any output, it exits 1
, which will cause Ansible to error, stopping the playbook.
finally, using until to wait for certain output
Sometimes, things take time to start up. Sometimes it takes time for services to reach the needed state. One example might be waiting for an Elasticsearch cluster to go green:
name: Check to see if Elasticsearch cluster is green.
shell: curl -s http://localhost:9200/_cluster/health | jq .status
register: health
until: health.stdout.find('green') != -1
delay: 5
retries: 100
The above will run a curl against localhost port 9200, capturing the status (which for Elasticsearch, can be green, yellow, or red), and wait until the status is green. It will do this 100 times, every 5 seconds. Once the status is green, the Ansible playbook continues.
conclusion
Here we have three Ansible controls, creates
, find
, and until
. Some used together for different effects. With them, Ansible can control shell with a bit more granularity. Ansible can run shell only once if a file exists (creates
), it can run only once if certain output exists (find
), and it can run a command repeatedly waiting for specific output (until
with find
).
-b