From a8b6ef7e7cbabaf87e57ea7df9df75eb7e7d1ab5 Mon Sep 17 00:00:00 2001 From: Martin Krizek Date: Mon, 13 Nov 2023 09:57:43 +0100 Subject: [PATCH] flush_handlers: handle a failure in a nested block with force_handlers (#81572) Fixes #81532 ci_complete --- .../81532-fix-nested-flush_handlers.yml | 2 + lib/ansible/executor/play_iterator.py | 44 +++++++------------ .../nested_flush_handlers_failure_force.yml | 19 ++++++++ test/integration/targets/handlers/runme.sh | 4 ++ 4 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 changelogs/fragments/81532-fix-nested-flush_handlers.yml create mode 100644 test/integration/targets/handlers/nested_flush_handlers_failure_force.yml diff --git a/changelogs/fragments/81532-fix-nested-flush_handlers.yml b/changelogs/fragments/81532-fix-nested-flush_handlers.yml new file mode 100644 index 00000000000..e43aa2e1df7 --- /dev/null +++ b/changelogs/fragments/81532-fix-nested-flush_handlers.yml @@ -0,0 +1,2 @@ +bugfixes: + - flush_handlers - properly handle a handler failure in a nested block when ``force_handlers`` is set (http://github.com/ansible/ansible/issues/81532) diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py index 7acbe4172cb..58eebfaea57 100644 --- a/lib/ansible/executor/play_iterator.py +++ b/lib/ansible/executor/play_iterator.py @@ -50,7 +50,7 @@ class FailedStates(IntFlag): TASKS = 2 RESCUE = 4 ALWAYS = 8 - HANDLERS = 16 + HANDLERS = 16 # NOTE not in use anymore class HostState: @@ -429,22 +429,18 @@ class PlayIterator: state.update_handlers = False state.cur_handlers_task = 0 - if state.fail_state & FailedStates.HANDLERS == FailedStates.HANDLERS: - state.update_handlers = True - state.run_state = IteratingStates.COMPLETE - else: - while True: - try: - task = state.handlers[state.cur_handlers_task] - except IndexError: - task = None - state.run_state = state.pre_flushing_run_state - state.update_handlers = True + while True: + try: + task = state.handlers[state.cur_handlers_task] + except IndexError: + task = None + state.run_state = state.pre_flushing_run_state + state.update_handlers = True + break + else: + state.cur_handlers_task += 1 + if task.is_host_notified(host): break - else: - state.cur_handlers_task += 1 - if task.is_host_notified(host): - break elif state.run_state == IteratingStates.COMPLETE: return (state, None) @@ -485,20 +481,16 @@ class PlayIterator: else: state.fail_state |= FailedStates.ALWAYS state.run_state = IteratingStates.COMPLETE - elif state.run_state == IteratingStates.HANDLERS: - state.fail_state |= FailedStates.HANDLERS - state.update_handlers = True - if state._blocks[state.cur_block].rescue: - state.run_state = IteratingStates.RESCUE - elif state._blocks[state.cur_block].always: - state.run_state = IteratingStates.ALWAYS - else: - state.run_state = IteratingStates.COMPLETE return state def mark_host_failed(self, host): s = self.get_host_state(host) display.debug("marking host %s failed, current state: %s" % (host, s)) + if s.run_state == IteratingStates.HANDLERS: + # we are failing `meta: flush_handlers`, so just reset the state to whatever + # it was before and let `_set_failed_state` figure out the next state + s.run_state = s.pre_flushing_run_state + s.update_handlers = True s = self._set_failed_state(s) display.debug("^ failed state is now: %s" % s) self.set_state_for_host(host.name, s) @@ -514,8 +506,6 @@ class PlayIterator: return True elif state.run_state == IteratingStates.ALWAYS and self._check_failed_state(state.always_child_state): return True - elif state.run_state == IteratingStates.HANDLERS and state.fail_state & FailedStates.HANDLERS == FailedStates.HANDLERS: - return True elif state.fail_state != FailedStates.NONE: if state.run_state == IteratingStates.RESCUE and state.fail_state & FailedStates.RESCUE == 0: return False diff --git a/test/integration/targets/handlers/nested_flush_handlers_failure_force.yml b/test/integration/targets/handlers/nested_flush_handlers_failure_force.yml new file mode 100644 index 00000000000..7380923e99a --- /dev/null +++ b/test/integration/targets/handlers/nested_flush_handlers_failure_force.yml @@ -0,0 +1,19 @@ +- hosts: A,B + gather_facts: false + force_handlers: true + tasks: + - block: + - command: echo + notify: h + + - meta: flush_handlers + rescue: + - debug: + msg: flush_handlers_rescued + always: + - debug: + msg: flush_handlers_always + handlers: + - name: h + fail: + when: inventory_hostname == "A" diff --git a/test/integration/targets/handlers/runme.sh b/test/integration/targets/handlers/runme.sh index e26fdd7a1be..e24dad60289 100755 --- a/test/integration/targets/handlers/runme.sh +++ b/test/integration/targets/handlers/runme.sh @@ -206,3 +206,7 @@ ansible-playbook force_handlers_blocks_81533-1.yml -i inventory.handlers "$@" 2> ansible-playbook force_handlers_blocks_81533-2.yml -i inventory.handlers "$@" 2>&1 | tee out.txt [ "$(grep out.txt -ce 'hosts_left')" = "1" ] + +ansible-playbook nested_flush_handlers_failure_force.yml -i inventory.handlers "$@" 2>&1 | tee out.txt +[ "$(grep out.txt -ce 'flush_handlers_rescued')" = "1" ] +[ "$(grep out.txt -ce 'flush_handlers_always')" = "2" ]