Register handlers immediately if currently iterating handlers (#80898)

This fixes the issue where handlers notifying other handlers are
not properly run because the notification is not registered unless
another flush_handlers occurs. Instead, if the current host state
is iterating handlers we immediately register the handler to be
run so the notification is not lost.

Fixes #80880
pull/81063/head
James Cammarata 12 months ago committed by GitHub
parent 73e04ef2d6
commit 660f1726c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,4 @@
bugfixes:
- "From issue https://github.com/ansible/ansible/issues/80880, when notifying a
handler from another handler, handler notifications must be registered
immediately as the flush_handler call is not recursive."

@ -652,20 +652,28 @@ class StrategyBase:
# only ensure that notified handlers exist, if so save the notifications for when
# handlers are actually flushed so the last defined handlers are exexcuted,
# otherwise depending on the setting either error or warn
host_state = iterator.get_state_for_host(original_host.name)
for notification in result_item['_ansible_notify']:
if any(self.search_handlers_by_notification(notification, iterator)):
iterator.add_notification(original_host.name, notification)
display.vv(f"Notification for handler {notification} has been saved.")
continue
msg = (
f"The requested handler '{notification}' was not found in either the main handlers"
" list nor in the listening handlers list"
)
if C.ERROR_ON_MISSING_HANDLER:
raise AnsibleError(msg)
for handler in self.search_handlers_by_notification(notification, iterator):
if host_state.run_state == IteratingStates.HANDLERS:
# we're currently iterating handlers, so we need to expand this now
if handler.notify_host(original_host):
# NOTE even with notifications deduplicated this can still happen in case of handlers being
# notified multiple times using different names, like role name or fqcn
self._tqm.send_callback('v2_playbook_on_notify', handler, original_host)
else:
iterator.add_notification(original_host.name, notification)
display.vv(f"Notification for handler {notification} has been saved.")
break
else:
display.warning(msg)
msg = (
f"The requested handler '{notification}' was not found in either the main handlers"
" list nor in the listening handlers list"
)
if C.ERROR_ON_MISSING_HANDLER:
raise AnsibleError(msg)
else:
display.warning(msg)
if 'add_host' in result_item:
# this task added a new host (add_host module)

@ -0,0 +1,34 @@
---
- name: Test notification of handlers from other handlers
hosts: localhost
gather_facts: no
handlers:
- name: Handler 1
debug:
msg: Handler 1
changed_when: true
notify: Handler 2
register: handler1_res
- name: Handler 2
debug:
msg: Handler 2
changed_when: true
notify: Handler 3
register: handler2_res
- name: Handler 3
debug:
msg: Handler 3
register: handler3_res
tasks:
- name: Trigger handlers
ansible.builtin.debug:
msg: Task 1
changed_when: true
notify: Handler 1
post_tasks:
- name: Assert results
ansible.builtin.assert:
that:
- "handler1_res is defined and handler1_res is success"
- "handler2_res is defined and handler2_res is success"
- "handler3_res is defined and handler3_res is success"

@ -50,6 +50,9 @@ for strategy in linear free; do
[ "$(ansible-playbook test_force_handlers.yml -i inventory.handlers -v "$@" --tags force_false_in_play --force-handlers \
| grep -E -o CALLED_HANDLER_. | sort | uniq | xargs)" = "CALLED_HANDLER_B" ]
# https://github.com/ansible/ansible/pull/80898
[ "$(ansible-playbook 80880.yml -i inventory.handlers -vv "$@" 2>&1)" ]
unset ANSIBLE_STRATEGY
done

Loading…
Cancel
Save