ansible stuffs - controlling shell tasks using creates, find, and until

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