From 7bec19606172e67ac4dbe5d10a6853b01b22ca8c Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Mon, 1 Nov 2021 11:25:10 -0400 Subject: [PATCH] loop/fact delegation fix (#75768) now set_fact and include_vars intermediate results are congruent with delegation --- changelogs/fragments/set_fact_delegation.yml | 2 ++ lib/ansible/executor/task_executor.py | 16 ++++++++++++-- .../delegate_to/delegate_facts_loop.yml | 21 +++++++++++++++++++ test/integration/targets/delegate_to/runme.sh | 1 + 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 changelogs/fragments/set_fact_delegation.yml create mode 100644 test/integration/targets/delegate_to/delegate_facts_loop.yml diff --git a/changelogs/fragments/set_fact_delegation.yml b/changelogs/fragments/set_fact_delegation.yml new file mode 100644 index 00000000000..05110c658c3 --- /dev/null +++ b/changelogs/fragments/set_fact_delegation.yml @@ -0,0 +1,2 @@ +bugfixes: + - set_fact/include_vars correctly handle delegation assignments within loops diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index 803ab6fe356..a0a1058cbac 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -654,7 +654,13 @@ class TaskExecutor: if 'ansible_facts' in result and self._task.action not in C._ACTION_DEBUG: if self._task.action in C._ACTION_WITH_CLEAN_FACTS: - vars_copy.update(result['ansible_facts']) + if self._task.delegate_to and self._task.delegate_facts: + if '_ansible_delegated_vars' in vars_copy: + vars_copy['_ansible_delegated_vars'].update(result['ansible_facts']) + else: + vars_copy['_ansible_delegated_vars'] = result['ansible_facts'] + else: + vars_copy.update(result['ansible_facts']) else: # TODO: cleaning of facts should eventually become part of taskresults instead of vars af = wrap_var(result['ansible_facts']) @@ -738,7 +744,13 @@ class TaskExecutor: if 'ansible_facts' in result and self._task.action not in C._ACTION_DEBUG: if self._task.action in C._ACTION_WITH_CLEAN_FACTS: - variables.update(result['ansible_facts']) + if self._task.delegate_to and self._task.delegate_facts: + if '_ansible_delegated_vars' in variables: + variables['_ansible_delegated_vars'].update(result['ansible_facts']) + else: + variables['_ansible_delegated_vars'] = result['ansible_facts'] + else: + variables.update(result['ansible_facts']) else: # TODO: cleaning of facts should eventually become part of taskresults instead of vars af = wrap_var(result['ansible_facts']) diff --git a/test/integration/targets/delegate_to/delegate_facts_loop.yml b/test/integration/targets/delegate_to/delegate_facts_loop.yml new file mode 100644 index 00000000000..90a25676dd0 --- /dev/null +++ b/test/integration/targets/delegate_to/delegate_facts_loop.yml @@ -0,0 +1,21 @@ +- hosts: localhost + gather_facts: no + tasks: + - set_fact: + test: 123 + delegate_to: "{{ item }}" + delegate_facts: true + when: test is not defined + loop: "{{ groups['all'] | difference(['localhost']) }}" + + - name: ensure we didnt create it on current host + assert: + that: + - test is undefined + + - name: ensure facts get created + assert: + that: + - "'test' in hostvars[item]" + - hostvars[item]['test'] == 123 + loop: "{{ groups['all'] | difference(['localhost']) }}" diff --git a/test/integration/targets/delegate_to/runme.sh b/test/integration/targets/delegate_to/runme.sh index af090cdf024..c262f8d1684 100755 --- a/test/integration/targets/delegate_to/runme.sh +++ b/test/integration/targets/delegate_to/runme.sh @@ -75,3 +75,4 @@ ansible-playbook resolve_vars.yml -i inventory -v "$@" ansible-playbook test_delegate_to_lookup_context.yml -i inventory -v "$@" ansible-playbook delegate_local_from_root.yml -i inventory -v "$@" -e 'ansible_user=root' ansible-playbook delegate_with_fact_from_delegate_host.yml "$@" +ansible-playbook delegate_facts_loop.yml -i inventory -v "$@"