Recently I’ve worked on a very simple Ansible task. My goal was to start an environment, wait until it becomes available (online) and do some things after. With environment I mean a web service - imagine any language or framework you want. Let’s say you need to access some specific URL like /api/heartbeat to make sure it’s initialized properly.

Piece of cake, right?

Attempt #1 Link to this heading

If you take a look at the list of all Ansible modules here, first of all you probably notice wait_for. Let’s try to use it:

yml
1---

2
- hosts: localhost
3tasks:
4
    - wait_for: host=http://domain.com/api/heartbeat port=80 timeout=5

(I’m using small timeout here just for demo purposes.)

Result: msg: Timeout when waiting for …

So, wait_for doesn’t actually work in our case - it accepts only hostnames without additional path. Ok, skip it.

How about uri module? Looks promising:

yml
1---

2
- hosts: localhost
3tasks:
4
    - uri: url=http://domain.com/api/heartbeat timeout=5

It should work, but I have a very specific use case - every environment I start can also create a new CNAME address. It requires some time to become resolvable. So, unfortunately result is msg: Unable to resolve the host name given.

I’ve realized that I couldn’t do it with Ansible tools.

Attempt #2 Link to this heading

If Ansible is powerless let’s just use old school bash. How about that:

yml
1---

2
- hosts: localhost
3tasks:
4
    - shell: curl --silent --show-error --output /dev/null --retry 90 --retry-delay 10 --retry-max-time 900 http://domain.com/api/heartbeat

Looks like it works! Except it doesn’t :-/

If you run this playbook you see that it actually waits for the URL to be available. But there is a problem in the interval between environment becoming resolvable and environment returning HTTP reply. Curl fails in that specific moment.

Wget is better in this case, it has retry-connrefused flag that really helps with the issue. Unfortunately it fails with the part of resolving hostname.

Attempt #3 Link to this heading

I’ve decided to continue with Curl approach, but improve it as much as I can. So, finally:

yml
1---

2
- hosts: localhost
3tasks:
4
    - script: scripts/wait_for.sh http://domain.com/api/heartbeat

where wait_for.sh:

bash
1#!/bin/sh

2until curl --silent --output /dev/null $1; do
3
  echo Could not fetch, retrying...
4
  sleep 30
5done

Looks really optimistic (and simple!), but you can always cancel it.

Conclusion Link to this heading

I really like the Ansible way, because you can always switch to old school bash and implement whatever you want. I’ve almost decided that this task is impossible to do with Ansible but finally did it in a different way. Don’t be afraid and experiment!