Ansible 2.4 -> 2.7, include_role and delegate_to
Short description of the problem: In Ansible 2.4 it was possible to write something like that:
- include_role:
name: foo
delegate_to: foo_host
And it worked as expected: role foo
run on host foo_host
. Ansible 2.5 has changed that, and include_role
ignores delegate_to
. (Contrary, import_role
, does respect it). I found few clever tricks to live with this, but
pointed out that there is a much neater way, by using an apply
parameter for include_role
, which supports delegate_to
as well as tags and become
. I have trouble to find words to express gratitude for that, as it saves me tons of efforts to refactor thousands lines of code.
The final solution
- include_role:
name: foo
apply:
delegate_to: foo_host
(note that apply is offset, as it’s an include_role
's parameter).
Compatibility
Unfortunately, there is no way to write a code which works with 2.4 and 2.7 at the same time. apply
does not understood in 2.4, so transition should be done in atomic manner.
delegate_to, include_role with loops
There is a horrible regression in Ansible 2.5.
In Ansible version 2.4, if you have combination of delegate_to
and include_role
a given role was delegated to specified host. In Ansible 2.5 delegated role will be executed on the original host, causing a big mess to debug. Actually, this problem affects on only include_role, but all other dynamic includes (f.e. include_tasks
). The problem was escalated by the fact that only ‘include’ allowed to be used together with loop statements (with_items
, with_nested
, etc).
After some thoughts, I found a way to do delegation and includes at the same time together with loops. The solution in the nutshell: use iteration over included tasklist, use delegation within tasklist for imports.
Example
I’ll start with bad example. It had worked in Ansible 2.4, it is silently broken in Ansible 2.5.
Bad code
role2/tasks/main.yaml:
---
- include_role: name=role1
delegate_to: localhost
with_items:
- one
- two
role1/tasks/main.yaml
- shell: hostname
register: h
- debug: msg="host: {{h}}, var={{item}}"
play.yaml
- hosts: somehost
gather_facts: no
roles:
- role2
Line to execute:
ansible-playbook -i somehost, play.yaml
You need to have ‘somehost’ to be accessible from your machine by ssh. Replace it with any known hostnames (except for localhost). You need to replace it in ‘line to execute’ and in play.yaml
in the ‘hosts
’ line.
You will find that regardless of using delegation to ‘localhost’, this playbook will output hostname of remote machine. What a bummer.
Good code
This code works in both ansible 2.5 and ansible 2.4. It will output your local machine hostname, as expected.
role2/tasks/main.yaml (changed):
---
- include_tasks: loop.yaml
with_items:
- one
- two
role2/tasks/loop.yaml: (it’s a new file!)
---
- import_role: name=role1
delegate_to: localhost
role1/tasks/main.yaml (the same file as before)
- shell: hostname
register: h
- debug: msg="host: {{h}}, var={{item}}"
play.yaml (the same file as before)
- hosts: somehost
gather_facts: no
roles:
- role2
After executing the same line:
ansible-playbook -i somehost, play.yaml
You will find that hostname output have been changed to a localhost hostname (expected behavior).
The solution, explained
Input:
- We can not use loop with import, we need to use include.
- We can not use delegate to with include, we need to use import.
Solution:
- We use loop + include_tasks to run a separate tasklist.
-
We use import_role + delegate_to to delegate a role to another host.
quato from https://medium.com/@george.shuklin
评论