diff --git a/changelogs/fragments/75244-fix-templated-handler-names.yaml b/changelogs/fragments/75244-fix-templated-handler-names.yaml new file mode 100644 index 00000000000..6a090224036 --- /dev/null +++ b/changelogs/fragments/75244-fix-templated-handler-names.yaml @@ -0,0 +1,2 @@ +bugfixes: + - Give a warning instead of an error if a handler name contains undefined variables and has no listen topics (https://github.com/ansible/ansible/issues/58841). diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index b92fcb15b21..f415709a543 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -492,16 +492,16 @@ class StrategyBase: for handler_block in reversed(handler_blocks): for handler_task in handler_block.block: if handler_task.name: - if not handler_task.cached_name: - if handler_templar.is_template(handler_task.name): - handler_templar.available_variables = self._variable_manager.get_vars(play=iterator._play, - task=handler_task, - _hosts=self._hosts_cache, - _hosts_all=self._hosts_cache_all) - handler_task.name = handler_templar.template(handler_task.name) - handler_task.cached_name = True - try: + if not handler_task.cached_name: + if handler_templar.is_template(handler_task.name): + handler_templar.available_variables = self._variable_manager.get_vars(play=iterator._play, + task=handler_task, + _hosts=self._hosts_cache, + _hosts_all=self._hosts_cache_all) + handler_task.name = handler_templar.template(handler_task.name) + handler_task.cached_name = True + # first we check with the full result of get_name(), which may # include the role name (if the handler is from a role). If that # is not found, we resort to the simple name field, which doesn't @@ -514,11 +514,17 @@ class StrategyBase: if handler_name in candidates: return handler_task - except (UndefinedError, AnsibleUndefinedVariable): + except (UndefinedError, AnsibleUndefinedVariable) as e: # We skip this handler due to the fact that it may be using # a variable in the name that was conditionally included via # set_fact or some other method, and we don't want to error # out unnecessarily + if not handler_task.listen: + display.warning( + "Handler '%s' is unusable because it has no listen topics and " + "the name could not be templated (host-specific variables are " + "not supported in handler names). The error: %s" % (handler_task.name, to_text(e)) + ) continue return None diff --git a/test/integration/targets/handlers/58841.yml b/test/integration/targets/handlers/58841.yml new file mode 100644 index 00000000000..eea5c2f3c38 --- /dev/null +++ b/test/integration/targets/handlers/58841.yml @@ -0,0 +1,9 @@ +--- +- hosts: localhost + gather_facts: no + tasks: + - include_role: + name: import_template_handler_names + tags: + - lazy_evaluation + - evaluation_time diff --git a/test/integration/targets/handlers/roles/import_template_handler_names/tasks/main.yml b/test/integration/targets/handlers/roles/import_template_handler_names/tasks/main.yml new file mode 100644 index 00000000000..3bc285e52a9 --- /dev/null +++ b/test/integration/targets/handlers/roles/import_template_handler_names/tasks/main.yml @@ -0,0 +1,11 @@ +- import_role: + name: template_handler_names + tasks_from: lazy_evaluation + tags: + - lazy_evaluation + +- import_role: + name: template_handler_names + tasks_from: evaluation_time + tags: + - evaluation_time diff --git a/test/integration/targets/handlers/roles/template_handler_names/handlers/main.yml b/test/integration/targets/handlers/roles/template_handler_names/handlers/main.yml new file mode 100644 index 00000000000..bf8ca851401 --- /dev/null +++ b/test/integration/targets/handlers/roles/template_handler_names/handlers/main.yml @@ -0,0 +1,5 @@ +- name: handler name with {{ test_var }} + debug: msg='handler with var ran' + +- name: handler name + debug: msg='handler ran' diff --git a/test/integration/targets/handlers/roles/template_handler_names/tasks/evaluation_time.yml b/test/integration/targets/handlers/roles/template_handler_names/tasks/evaluation_time.yml new file mode 100644 index 00000000000..c0706fc50e9 --- /dev/null +++ b/test/integration/targets/handlers/roles/template_handler_names/tasks/evaluation_time.yml @@ -0,0 +1,5 @@ +- debug: msg='notify handler with variable in name' + notify: handler name with myvar + changed_when: True + tags: + - evaluation_time diff --git a/test/integration/targets/handlers/roles/template_handler_names/tasks/lazy_evaluation.yml b/test/integration/targets/handlers/roles/template_handler_names/tasks/lazy_evaluation.yml new file mode 100644 index 00000000000..e82dca06e34 --- /dev/null +++ b/test/integration/targets/handlers/roles/template_handler_names/tasks/lazy_evaluation.yml @@ -0,0 +1,5 @@ +- debug: msg='notify handler' + notify: handler name + changed_when: True + tags: + - lazy_evaluation diff --git a/test/integration/targets/handlers/runme.sh b/test/integration/targets/handlers/runme.sh index 17255de194b..7c403b4ea2a 100755 --- a/test/integration/targets/handlers/runme.sh +++ b/test/integration/targets/handlers/runme.sh @@ -96,3 +96,21 @@ result="$(ansible-playbook test_handlers_template_run_once.yml -i inventory.hand set -e grep -q "handler A" <<< "$result" grep -q "handler B" <<< "$result" + +# Test an undefined variable in another handler name isn't a failure +ansible-playbook 58841.yml "$@" --tags lazy_evaluation 2>&1 | tee out.txt ; cat out.txt +grep out.txt -e "\[WARNING\]: Handler 'handler name with {{ test_var }}' is unusable" +[ "$(grep out.txt -ce 'handler ran')" = "1" ] +[ "$(grep out.txt -ce 'handler with var ran')" = "0" ] + +# Test templating a handler name with a defined variable +ansible-playbook 58841.yml "$@" --tags evaluation_time -e test_var=myvar | tee out.txt ; cat out.txt +[ "$(grep out.txt -ce 'handler ran')" = "0" ] +[ "$(grep out.txt -ce 'handler with var ran')" = "1" ] + +# Test the handler is not found when the variable is undefined +ansible-playbook 58841.yml "$@" --tags evaluation_time 2>&1 | tee out.txt ; cat out.txt +grep out.txt -e "ERROR! The requested handler 'handler name with myvar' was not found" +grep out.txt -e "\[WARNING\]: Handler 'handler name with {{ test_var }}' is unusable" +[ "$(grep out.txt -ce 'handler ran')" = "0" ] +[ "$(grep out.txt -ce 'handler with var ran')" = "0" ]