From 571e2a9551de1a5161d8c7507ef1d62d2f78e1cc Mon Sep 17 00:00:00 2001 From: Martin Krizek Date: Thu, 4 Jul 2024 09:19:56 +0200 Subject: [PATCH] Fix incorrect rc when executing end_host in rescue (#83522) Fixes #83447 --- .../fragments/83447-end_host-rescue-rc.yml | 2 ++ lib/ansible/executor/play_iterator.py | 16 ++++++++++++++++ lib/ansible/plugins/strategy/__init__.py | 7 +++---- test/integration/targets/meta_tasks/runme.sh | 3 +++ .../meta_tasks/test_end_host_rescue_rc.yml | 7 +++++++ 5 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 changelogs/fragments/83447-end_host-rescue-rc.yml create mode 100644 test/integration/targets/meta_tasks/test_end_host_rescue_rc.yml diff --git a/changelogs/fragments/83447-end_host-rescue-rc.yml b/changelogs/fragments/83447-end_host-rescue-rc.yml new file mode 100644 index 00000000000..b4d82414971 --- /dev/null +++ b/changelogs/fragments/83447-end_host-rescue-rc.yml @@ -0,0 +1,2 @@ +bugfixes: + - "``end_host`` - fix incorrect return code when executing ``end_host`` in the ``rescue`` section (https://github.com/ansible/ansible/issues/83447)" diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py index 474b5da94f4..deae3ea04e4 100644 --- a/lib/ansible/executor/play_iterator.py +++ b/lib/ansible/executor/play_iterator.py @@ -635,3 +635,19 @@ class PlayIterator: def clear_notification(self, hostname: str, notification: str) -> None: self._host_states[hostname].handler_notifications.remove(notification) + + def end_host(self, hostname: str) -> None: + """Used by ``end_host``, ``end_batch`` and ``end_play`` meta tasks to end executing given host.""" + state = self.get_active_state(self.get_state_for_host(hostname)) + if state.run_state == IteratingStates.RESCUE: + # This is a special case for when ending a host occurs in rescue. + # By definition the meta task responsible for ending the host + # is the last task, so we need to clear the fail state to mark + # the host as rescued. + # The reason we need to do that is because this operation is + # normally done when PlayIterator transitions from rescue to + # always when only then we can say that rescue didn't fail + # but with ending a host via meta task, we don't get to that transition. + self.set_fail_state_for_host(hostname, FailedStates.NONE) + self.set_run_state_for_host(hostname, IteratingStates.COMPLETE) + self._play._removed_hosts.append(hostname) diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index a5822c3b018..c2ef9048237 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -997,7 +997,7 @@ class StrategyBase: if _evaluate_conditional(target_host): for host in self._inventory.get_hosts(iterator._play.hosts): if host.name not in self._tqm._unreachable_hosts: - iterator.set_run_state_for_host(host.name, IteratingStates.COMPLETE) + iterator.end_host(host.name) msg = "ending batch" else: skipped = True @@ -1006,7 +1006,7 @@ class StrategyBase: if _evaluate_conditional(target_host): for host in self._inventory.get_hosts(iterator._play.hosts): if host.name not in self._tqm._unreachable_hosts: - iterator.set_run_state_for_host(host.name, IteratingStates.COMPLETE) + iterator.end_host(host.name) # end_play is used in PlaybookExecutor/TQM to indicate that # the whole play is supposed to be ended as opposed to just a batch iterator.end_play = True @@ -1016,8 +1016,7 @@ class StrategyBase: skip_reason += ', continuing play' elif meta_action == 'end_host': if _evaluate_conditional(target_host): - iterator.set_run_state_for_host(target_host.name, IteratingStates.COMPLETE) - iterator._play._removed_hosts.append(target_host.name) + iterator.end_host(target_host.name) msg = "ending play for %s" % target_host.name else: skipped = True diff --git a/test/integration/targets/meta_tasks/runme.sh b/test/integration/targets/meta_tasks/runme.sh index f7d8d8973f4..feb51ae88a8 100755 --- a/test/integration/targets/meta_tasks/runme.sh +++ b/test/integration/targets/meta_tasks/runme.sh @@ -76,3 +76,6 @@ done # test refresh ansible-playbook -i inventory_refresh.yml refresh.yml "$@" ansible-playbook -i inventory_refresh.yml refresh_preserve_dynamic.yml "$@" + +# test rc when end_host in the rescue section +ANSIBLE_FORCE_HANDLERS=0 ansible-playbook test_end_host_rescue_rc.yml diff --git a/test/integration/targets/meta_tasks/test_end_host_rescue_rc.yml b/test/integration/targets/meta_tasks/test_end_host_rescue_rc.yml new file mode 100644 index 00000000000..c2faa171b34 --- /dev/null +++ b/test/integration/targets/meta_tasks/test_end_host_rescue_rc.yml @@ -0,0 +1,7 @@ +- hosts: localhost + gather_facts: false + tasks: + - block: + - fail: + rescue: + - meta: end_host