summaryrefslogtreecommitdiff
path: root/lib/ansible/vars
diff options
context:
space:
mode:
authorMatt Martz <matt@sivel.net>2020-10-12 14:21:15 -0500
committerGitHub <noreply@github.com>2020-10-12 14:21:15 -0500
commitc4acd41d6e4ed756b4e25545a03b540277168497 (patch)
treea449c287e8b166205e279b636dce3ea9d8e265c6 /lib/ansible/vars
parent5d811fc5518ae57275e178e45c4aef09ee139da1 (diff)
downloadansible-c4acd41d6e4ed756b4e25545a03b540277168497.tar.gz
Ensure delegate vars calculation has correct loop context. Fixes #37132 (#71477)
Diffstat (limited to 'lib/ansible/vars')
-rw-r--r--lib/ansible/vars/manager.py35
1 files changed, 33 insertions, 2 deletions
diff --git a/lib/ansible/vars/manager.py b/lib/ansible/vars/manager.py
index 346270b7f9..0c819d4105 100644
--- a/lib/ansible/vars/manager.py
+++ b/lib/ansible/vars/manager.py
@@ -517,6 +517,12 @@ class VariableManager:
return variables
def _get_delegated_vars(self, play, task, existing_variables):
+ # This method has a lot of code copied from ``TaskExecutor._get_loop_items``
+ # if this is failing, and ``TaskExecutor._get_loop_items`` is not
+ # then more will have to be copied here.
+ # TODO: dedupe code here and with ``TaskExecutor._get_loop_items``
+ # this may be possible once we move pre-processing pre fork
+
if not hasattr(task, 'loop'):
# This "task" is not a Task, so we need to skip it
return {}, None
@@ -525,16 +531,41 @@ class VariableManager:
# as we're fetching vars before post_validate has been called on
# the task that has been passed in
vars_copy = existing_variables.copy()
+
+ # get search path for this task to pass to lookup plugins
+ vars_copy['ansible_search_path'] = task.get_search_path()
+
+ # ensure basedir is always in (dwim already searches here but we need to display it)
+ if self._loader.get_basedir() not in vars_copy['ansible_search_path']:
+ vars_copy['ansible_search_path'].append(self._loader.get_basedir())
+
templar = Templar(loader=self._loader, variables=vars_copy)
items = []
has_loop = True
if task.loop_with is not None:
if task.loop_with in lookup_loader:
+ fail = True
+ if task.loop_with == 'first_found':
+ # first_found loops are special. If the item is undefined then we want to fall through to the next
+ fail = False
try:
loop_terms = listify_lookup_plugin_terms(terms=task.loop, templar=templar,
- loader=self._loader, fail_on_undefined=True, convert_bare=False)
- items = wrap_var(lookup_loader.get(task.loop_with, loader=self._loader, templar=templar).run(terms=loop_terms, variables=vars_copy))
+ loader=self._loader, fail_on_undefined=fail, convert_bare=False)
+
+ if not fail:
+ loop_terms = [t for t in loop_terms if not templar.is_template(t)]
+
+ mylookup = lookup_loader.get(task.loop_with, loader=self._loader, templar=templar)
+
+ # give lookup task 'context' for subdir (mostly needed for first_found)
+ for subdir in ['template', 'var', 'file']: # TODO: move this to constants?
+ if subdir in task.action:
+ break
+ setattr(mylookup, '_subdir', subdir + 's')
+
+ items = wrap_var(mylookup.run(terms=loop_terms, variables=vars_copy))
+
except AnsibleTemplateError:
# This task will be skipped later due to this, so we just setup
# a dummy array for the later code so it doesn't fail