Skip to content

Commit

Permalink
Allow for searching handler subdir for included tasks (ansible#73809)
Browse files Browse the repository at this point in the history
* Allow for searching handler subdir for included tasks
  • Loading branch information
Shrews authored Mar 15, 2021
1 parent 3098022 commit 1e5ccb3
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 49 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/73809-search-handler-subdir.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- A handler defined within a role will now search handlers subdir for included tasks (issue https://github.com/ansible/ansible/issues/71222).
102 changes: 53 additions & 49 deletions lib/ansible/playbook/included_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from ansible import constants as C
from ansible.errors import AnsibleError
from ansible.module_utils._text import to_text
from ansible.playbook.handler import Handler
from ansible.playbook.task_include import TaskInclude
from ansible.playbook.role_include import IncludeRole
from ansible.template import Templar
Expand Down Expand Up @@ -114,60 +115,63 @@ def process_include_results(results, iterator, loader, variable_manager):

if original_task.action in C._ACTION_ALL_INCLUDE_TASKS:
include_file = None
if original_task:
if original_task.static:
continue

if original_task._parent:
# handle relative includes by walking up the list of parent include
# tasks and checking the relative result to see if it exists
parent_include = original_task._parent
cumulative_path = None
while parent_include is not None:
if not isinstance(parent_include, TaskInclude):
parent_include = parent_include._parent
continue
if isinstance(parent_include, IncludeRole):
parent_include_dir = parent_include._role_path
else:
if original_task.static:
continue

if original_task._parent:
# handle relative includes by walking up the list of parent include
# tasks and checking the relative result to see if it exists
parent_include = original_task._parent
cumulative_path = None
while parent_include is not None:
if not isinstance(parent_include, TaskInclude):
parent_include = parent_include._parent
continue
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:
cumulative_path = parent_include_dir
include_target = templar.template(include_result['include'])
if original_task._role:
new_basedir = os.path.join(original_task._role._role_path, 'tasks', cumulative_path)
candidates = [loader.path_dwim_relative(original_task._role._role_path, 'tasks', include_target),
loader.path_dwim_relative(new_basedir, 'tasks', include_target)]
for include_file in candidates:
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:
cumulative_path = parent_include_dir
include_target = templar.template(include_result['include'])
if original_task._role:
new_basedir = os.path.join(original_task._role._role_path, 'tasks', cumulative_path)
candidates = [loader.path_dwim_relative(original_task._role._role_path, 'tasks', include_target),
loader.path_dwim_relative(new_basedir, 'tasks', include_target)]
for include_file in candidates:
try:
# may throw OSError
os.stat(include_file)
# or select the task file if it exists
break
except OSError:
pass
else:
include_file = loader.path_dwim_relative(loader.get_basedir(), cumulative_path, include_target)

if os.path.exists(include_file):
break
else:
parent_include = parent_include._parent
# may throw OSError
os.stat(include_file)
# or select the task file if it exists
break
except OSError:
pass
else:
include_file = loader.path_dwim_relative(loader.get_basedir(), cumulative_path, include_target)

if os.path.exists(include_file):
break
else:
parent_include = parent_include._parent

if include_file is None:
if original_task._role:
include_target = templar.template(include_result['include'])
include_file = loader.path_dwim_relative(original_task._role._role_path, 'tasks', include_target)
include_file = loader.path_dwim_relative(
original_task._role._role_path,
'handlers' if isinstance(original_task, Handler) else 'tasks',
include_target,
is_role=True)
else:
include_file = loader.path_dwim(include_result['include'])

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- debug: msg="handler with tasks from A.yml called"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- name: role-based handler from handler subdir
include_tasks: A.yml

- name: role-based handler from tasks subdir
include_tasks: B.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- debug: msg="handler with tasks from B.yml called"
3 changes: 3 additions & 0 deletions test/integration/targets/handlers/runme.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ set -e
# https://github.com/ansible/ansible/issues/47287
[ "$(ansible-playbook test_handlers_including_task.yml -i ../../inventory -v "$@" | grep -E -o 'failed=[0-9]+')" = "failed=0" ]

# https://github.com/ansible/ansible/issues/71222
ansible-playbook test_role_handlers_including_tasks.yml -i ../../inventory -v "$@"

# https://github.com/ansible/ansible/issues/27237
set +e
result="$(ansible-playbook test_handlers_template_run_once.yml -i inventory.handlers "$@" 2>&1)"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
- name: Verify a role handler can include other tasks from handlers and tasks subdirs
hosts: testhost
roles:
- test_role_handlers_include_tasks

tasks:
- name: notify a role-based handler (include tasks from handler subdir)
debug:
msg: notifying role handler
changed_when: yes
notify: role-based handler from handler subdir

- name: notify a role-based handler (include tasks from tasks subdir)
debug:
msg: notifying another role handler
changed_when: yes
notify: role-based handler from tasks subdir

0 comments on commit 1e5ccb3

Please sign in to comment.