Give IncludedFile more context via ansible_search_path (#50045)

* Give IncludedFile more context via ansible_search_path to template lookups. Fixes #49969

* Update units
This commit is contained in:
Matt Martz 2019-01-03 15:13:02 -06:00 committed by ansibot
parent 6620facd19
commit 3b49bbcfde
8 changed files with 50 additions and 2 deletions

View file

@ -0,0 +1,3 @@
bugfixes:
- include_tasks - Ensure we give IncludedFile the same context as TaskExecutor when templating the parent include path
allowing for lookups in the included file path (https://github.com/ansible/ansible/issues/49969)

View file

@ -21,6 +21,8 @@ __metaclass__ = type
import os
from ansible.errors import AnsibleError
from ansible.module_utils._text import to_text
from ansible.playbook.task_include import TaskInclude
from ansible.playbook.role_include import IncludeRole
from ansible.template import Templar
@ -78,7 +80,6 @@ class IncludedFile:
task_vars = task_vars_cache[cache_key]
except KeyError:
task_vars = task_vars_cache[cache_key] = variable_manager.get_vars(play=iterator._play, host=original_host, task=original_task)
templar = Templar(loader=loader, variables=task_vars)
include_variables = include_result.get('include_variables', dict())
loop_var = 'item'
@ -95,6 +96,16 @@ class IncludedFile:
if original_task.no_log and '_ansible_no_log' not in include_variables:
task_vars['_ansible_no_log'] = include_variables['_ansible_no_log'] = original_task.no_log
# get search path for this task to pass to lookup plugins that may be used in pathing to
# the included file
task_vars['ansible_search_path'] = original_task.get_search_path()
# ensure basedir is always in (dwim already searches here but we need to display it)
if loader.get_basedir() not in task_vars['ansible_search_path']:
task_vars['ansible_search_path'].append(loader.get_basedir())
templar = Templar(loader=loader, variables=task_vars)
if original_task.action in ('include', 'include_tasks'):
include_file = None
if original_task:
@ -113,7 +124,15 @@ class IncludedFile:
if isinstance(parent_include, IncludeRole):
parent_include_dir = parent_include._role_path
else:
try:
parent_include_dir = os.path.dirname(templar.template(parent_include.args.get('_raw_params')))
except AnsibleError as e:
parent_include_dir = ''
display.warning(
'Templating the path of the parent %s failed. The path to the '
'included file may not be found. '
'The error was: %s.' % (original_task.action, to_text(e))
)
if cumulative_path is not None and not os.path.isabs(cumulative_path):
cumulative_path = os.path.join(parent_include_dir, cumulative_path)
else:

View file

@ -0,0 +1,11 @@
# https://github.com/ansible/ansible/issues/49969
- hosts: localhost
gather_facts: false
tasks:
- include_role:
name: test
public: true
- assert:
that:
- included_other is defined

View file

@ -0,0 +1 @@
- include_tasks: other.yml

View file

@ -0,0 +1 @@
- include_tasks: "{{ lookup('first_found', inventory_hostname ~ '.yml') }}"

View file

@ -0,0 +1,2 @@
- set_fact:
included_other: true

View file

@ -90,3 +90,7 @@ ansible-playbook run_once/playbook.yml "$@"
# https://github.com/ansible/ansible/issues/48936
ansible-playbook -v handler_addressing/playbook.yml 2>&1 | tee test_handler_addressing.out
test "$(egrep -c 'include handler task|ERROR! The requested handler '"'"'do_import'"'"' was not found' test_handler_addressing.out)" = 2
# https://github.com/ansible/ansible/issues/49969
ansible-playbook -v parent_templating/playbook.yml 2>&1 | tee test_parent_templating.out
test "$(egrep -c 'Templating the path of the parent include_tasks failed.' test_parent_templating.out)" = 0

View file

@ -65,6 +65,7 @@ def test_process_include_results(mock_iterator, mock_variable_manager):
parent_task_ds = {'debug': 'msg=foo'}
parent_task = Task.load(parent_task_ds)
parent_task._play = None
task_ds = {'include': 'include_test.yml'}
loaded_task = TaskInclude.load(task_ds, task_include=parent_task)
@ -91,12 +92,15 @@ def test_process_include_diff_files(mock_iterator, mock_variable_manager):
parent_task_ds = {'debug': 'msg=foo'}
parent_task = Task.load(parent_task_ds)
parent_task._play = None
task_ds = {'include': 'include_test.yml'}
loaded_task = TaskInclude.load(task_ds, task_include=parent_task)
loaded_task._play = None
child_task_ds = {'include': 'other_include_test.yml'}
loaded_child_task = TaskInclude.load(child_task_ds, task_include=loaded_task)
loaded_child_task._play = None
return_data = {'include': 'include_test.yml'}
# The task in the TaskResult has to be a TaskInclude so it has a .static attr
@ -129,6 +133,9 @@ def test_process_include_simulate_free(mock_iterator, mock_variable_manager):
parent_task1 = Task.load(parent_task_ds)
parent_task2 = Task.load(parent_task_ds)
parent_task1._play = None
parent_task2._play = None
task_ds = {'include': 'include_test.yml'}
loaded_task1 = TaskInclude.load(task_ds, task_include=parent_task1)
loaded_task2 = TaskInclude.load(task_ds, task_include=parent_task2)