mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-25 03:41:25 -07:00
Don't use the task for a cache, return a special cache var (#47243)
* Don't use task to cache loop results, use hostvars. Fixes #47207 * Avoid a race condition, supply _ansible_loop_cache through get_vars directly * Add tests * Add changelog fragment * Remove unnecessary copy * Remove unnecessary host from _get_delegated_vars signature
This commit is contained in:
parent
9e2c02455a
commit
77d32b8f57
5 changed files with 63 additions and 10 deletions
2
changelogs/fragments/delegate_to_loop_hostvars.yaml
Normal file
2
changelogs/fragments/delegate_to_loop_hostvars.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
bugfixes:
|
||||||
|
- delegate_to - When templating ``delegate_to`` in a loop, don't use the task for a cache, return a special cache through ``get_vars`` allowing looping over a hostvar (https://github.com/ansible/ansible/issues/47207)
|
|
@ -210,7 +210,12 @@ class TaskExecutor:
|
||||||
|
|
||||||
templar = Templar(loader=self._loader, shared_loader_obj=self._shared_loader_obj, variables=self._job_vars)
|
templar = Templar(loader=self._loader, shared_loader_obj=self._shared_loader_obj, variables=self._job_vars)
|
||||||
items = None
|
items = None
|
||||||
if self._task.loop_with:
|
loop_cache = self._job_vars.get('_ansible_loop_cache')
|
||||||
|
if loop_cache is not None:
|
||||||
|
# _ansible_loop_cache may be set in `get_vars` when calculating `delegate_to`
|
||||||
|
# to avoid reprocessing the loop
|
||||||
|
items = loop_cache
|
||||||
|
elif self._task.loop_with:
|
||||||
if self._task.loop_with in self._shared_loader_obj.lookup_loader:
|
if self._task.loop_with in self._shared_loader_obj.lookup_loader:
|
||||||
fail = True
|
fail = True
|
||||||
if self._task.loop_with == 'first_found':
|
if self._task.loop_with == 'first_found':
|
||||||
|
|
|
@ -430,7 +430,7 @@ class VariableManager:
|
||||||
# if we have a task and we're delegating to another host, figure out the
|
# if we have a task and we're delegating to another host, figure out the
|
||||||
# variables for that host now so we don't have to rely on hostvars later
|
# variables for that host now so we don't have to rely on hostvars later
|
||||||
if task and task.delegate_to is not None and include_delegate_to:
|
if task and task.delegate_to is not None and include_delegate_to:
|
||||||
all_vars['ansible_delegated_vars'] = self._get_delegated_vars(play, task, all_vars)
|
all_vars['ansible_delegated_vars'], all_vars['_ansible_loop_cache'] = self._get_delegated_vars(play, task, all_vars)
|
||||||
|
|
||||||
# 'vars' magic var
|
# 'vars' magic var
|
||||||
if task or play:
|
if task or play:
|
||||||
|
@ -594,16 +594,15 @@ class VariableManager:
|
||||||
include_hostvars=False,
|
include_hostvars=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_ansible_loop_cache = None
|
||||||
if has_loop and cache_items:
|
if has_loop and cache_items:
|
||||||
# delegate_to templating produced a change, update task.loop with templated items,
|
# delegate_to templating produced a change, so we will cache the templated items
|
||||||
|
# in a special private hostvar
|
||||||
# this ensures that delegate_to+loop doesn't produce different results than TaskExecutor
|
# this ensures that delegate_to+loop doesn't produce different results than TaskExecutor
|
||||||
# which may reprocess the loop
|
# which may reprocess the loop
|
||||||
# Set loop_with to None, so we don't do extra unexpected processing on the cached items later
|
_ansible_loop_cache = items
|
||||||
# in TaskExecutor
|
|
||||||
task.loop_with = None
|
|
||||||
task.loop = items
|
|
||||||
|
|
||||||
return delegated_host_vars
|
return delegated_host_vars, _ansible_loop_cache
|
||||||
|
|
||||||
def clear_facts(self, hostname):
|
def clear_facts(self, hostname):
|
||||||
'''
|
'''
|
||||||
|
|
|
@ -9,6 +9,8 @@ ansible-playbook test_loop_control.yml -v "$@"
|
||||||
|
|
||||||
ansible-playbook test_delegate_to_loop_randomness.yml -v "$@"
|
ansible-playbook test_delegate_to_loop_randomness.yml -v "$@"
|
||||||
|
|
||||||
ansible-playbook delegate_and_nolog.yml -v "$@"
|
ansible-playbook delegate_and_nolog.yml -i ../../inventory -v "$@"
|
||||||
|
|
||||||
ansible-playbook delegate_facts_block.yml -v "$@"
|
ansible-playbook delegate_facts_block.yml -i ../../inventory -v "$@"
|
||||||
|
|
||||||
|
ansible-playbook test_delegate_to_loop_caching.yml -i ../../inventory -v "$@"
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
- hosts: testhost,testhost2
|
||||||
|
gather_facts: false
|
||||||
|
vars:
|
||||||
|
delegate_to_host: "localhost"
|
||||||
|
tasks:
|
||||||
|
- set_fact:
|
||||||
|
gandalf:
|
||||||
|
shout: 'You shall not pass!'
|
||||||
|
when: inventory_hostname == 'testhost'
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
gandalf:
|
||||||
|
speak: 'Run you fools!'
|
||||||
|
when: inventory_hostname == 'testhost2'
|
||||||
|
|
||||||
|
- name: works correctly
|
||||||
|
debug: var=item
|
||||||
|
delegate_to: localhost
|
||||||
|
with_dict: "{{ gandalf }}"
|
||||||
|
register: result1
|
||||||
|
|
||||||
|
- name: shows same item for all hosts
|
||||||
|
debug: var=item
|
||||||
|
delegate_to: "{{ delegate_to_host }}"
|
||||||
|
with_dict: "{{ gandalf }}"
|
||||||
|
register: result2
|
||||||
|
|
||||||
|
- debug:
|
||||||
|
var: result2.results[0].item.value
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- result1.results[0].item.value == 'You shall not pass!'
|
||||||
|
- result2.results[0].item.value == 'You shall not pass!'
|
||||||
|
when: inventory_hostname == 'testhost'
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- result1.results[0].item.value == 'Run you fools!'
|
||||||
|
- result2.results[0].item.value == 'Run you fools!'
|
||||||
|
when: inventory_hostname == 'testhost2'
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- _ansible_loop_cache is undefined
|
Loading…
Add table
Add a link
Reference in a new issue