From 58496bed29f4d2c22d51626cc7b98f4e16b898a1 Mon Sep 17 00:00:00 2001 From: Sloane Hertel <19572925+s-hertel@users.noreply.github.com> Date: Wed, 11 Jun 2025 11:27:00 -0400 Subject: [PATCH] fix handler include_tasks templating (#85015) * Add test for a handler including tasks from a variable filename * Add FieldAttributeBase attribute to indicate if the object should be post validated Co-authored-by: Matt Davis <6775756+nitzmahone@users.noreply.github.com> (cherry picked from commit d3977ebc88ab9088364196996a7629a704f23e57) --- .../85015-fix-handler-include_tasks-templating.yml | 2 ++ lib/ansible/playbook/base.py | 9 +++++++-- lib/ansible/playbook/play_context.py | 2 ++ lib/ansible/playbook/task.py | 2 ++ .../targets/handlers/test_handlers_including_task.yml | 9 ++++++++- 5 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 changelogs/fragments/85015-fix-handler-include_tasks-templating.yml diff --git a/changelogs/fragments/85015-fix-handler-include_tasks-templating.yml b/changelogs/fragments/85015-fix-handler-include_tasks-templating.yml new file mode 100644 index 00000000000..17e4d6155b5 --- /dev/null +++ b/changelogs/fragments/85015-fix-handler-include_tasks-templating.yml @@ -0,0 +1,2 @@ +bugfixes: + - include_tasks - fix templating options when used as a handler (https://github.com/ansible/ansible/pull/85015). diff --git a/lib/ansible/playbook/base.py b/lib/ansible/playbook/base.py index 890401654d5..9fc5519904d 100644 --- a/lib/ansible/playbook/base.py +++ b/lib/ansible/playbook/base.py @@ -83,6 +83,11 @@ class _ClassProperty: class FieldAttributeBase: + _post_validate_object = False + """ + `False` skips FieldAttribute post-validation on intermediate objects and mixins for attributes without `always_post_validate`. + Leaf objects (e.g., `Task`) should set this attribute `True` to opt-in to post-validation. + """ fattributes = _ClassProperty() @classmethod @@ -566,8 +571,8 @@ class FieldAttributeBase: # only import_role is checked here because import_tasks never reaches this point return Sentinel - # FIXME: compare types, not strings - if not attribute.always_post_validate and self.__class__.__name__ not in ('Task', 'Handler', 'PlayContext', 'IncludeRole', 'TaskInclude'): + # Skip post validation unless always_post_validate is True, or the object requires post validation. + if not attribute.always_post_validate and not self._post_validate_object: # Intermediate objects like Play() won't have their fields validated by # default, as their values are often inherited by other objects and validated # later, so we don't want them to fail out early diff --git a/lib/ansible/playbook/play_context.py b/lib/ansible/playbook/play_context.py index 699331626d8..42ffa56a153 100644 --- a/lib/ansible/playbook/play_context.py +++ b/lib/ansible/playbook/play_context.py @@ -71,6 +71,8 @@ class PlayContext(Base): connection/authentication information. """ + _post_validate_object = True + # base module_compression = FieldAttribute(isa='string', default=C.DEFAULT_MODULE_COMPRESSION) shell = FieldAttribute(isa='string') diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 304e9d3c752..4f97e268194 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -65,6 +65,8 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl Task.something(...) """ + _post_validate_object = True + # ================================================================================= # ATTRIBUTES # load_ and diff --git a/test/integration/targets/handlers/test_handlers_including_task.yml b/test/integration/targets/handlers/test_handlers_including_task.yml index 8f7933ab05e..9b0c7ab70d3 100644 --- a/test/integration/targets/handlers/test_handlers_including_task.yml +++ b/test/integration/targets/handlers/test_handlers_including_task.yml @@ -9,8 +9,15 @@ debug: msg: notifying handler changed_when: yes - notify: include a task from the handlers section + notify: + - include a task from the handlers section + - include a task in a loop from the handlers section handlers: - name: include a task from the handlers section include_tasks: handlers.yml + + - name: include a task in a loop from the handlers section + include_tasks: "{{ item }}" + with_first_found: + - handlers.yml