Voluntarily writing an infinite loop in Ansible is not immediately obvious: both the loop
and with_*
family of keywords expect some kind of list or other iterable structure which are all inherently finite.
It is not possible to write loop: ∞
after all.
Such a loop could be used as a far more flexible alternative to wait_for
or until
, which can both rapidly grow to become unreadable.
In this short article I will explain how to do it.
My first thought was to reach to import_playbook
:
# self.yml
---
- name: Import self
import_playbok: self.yml
But depending on your exact configuration only two things can happen:
ERROR! Unexpected Exception, this is probably a bug: maximum recursion depth exceeded
Abort trap (core dumped)
The module import_playbook
is static so ansible-playbook
will load the file self.yml
again and again before trying to make sense of it, ultimately trashing its allowed quota of memory (see § Re-using Ansible artifacts for more explanations).
Static re-use of a ressource is thus out of the question and only a dynamic method would work, meaning an include_*
module.
As include_playbook
doesn’t exist there is only one solution: to use the next best thing, that is include_tasks
.
Two files are needed because import_tasks
can not grok those pesky hosts
and gather_facts
keywords:
# loop.yml
---
- hosts: localhost
gather_facts: no
tasks:
- include_tasks: loop-task.yml
# loop-task.yml
---
- debug:
msg: "Hello!"
- include_tasks: loop-task.yml
Let’s run this:
$ ansible-playbook loop.yml
PLAY [localhost] ***************************************************************
TASK [include_tasks] ***********************************************************
included: /bla/bla/loop-task.yml for localhost
TASK [debug] *******************************************************************
ok: [localhost] =>
msg: Hello!
TASK [include_tasks] ***********************************************************
included: /bla/bla/loop-task.yml for localhost
TASK [debug] *******************************************************************
ok: [localhost] =>
msg: Hello!
...
Hooray!
If there is a need to put a limit to the number of recursion a simple counter mechanism can be put into place:
# loop-task.yml
---
- name: Increase counter.
set_fact:
counter: "{{ counter | default(-1) | int + 1 }}"
- debug:
msg: "{{ counter | int }}"
- include_tasks: loop-task.yml
when: 'counter | int < 5'