From 29cf8b599ef32c75f8a1c512c4f730a679e6f986 Mon Sep 17 00:00:00 2001 From: Martin Krizek Date: Tue, 19 Mar 2024 17:33:37 +0100 Subject: [PATCH] any_errors_fatal: do not fail unreachable hosts Fixes #82834 ci_complete --- .../82834-any_errors_fatal-unreachable.yml | 2 ++ lib/ansible/plugins/strategy/linear.py | 11 ++++++++++- test/integration/targets/any_errors_fatal/82834.yml | 13 +++++++++++++ test/integration/targets/any_errors_fatal/runme.sh | 4 ++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/82834-any_errors_fatal-unreachable.yml create mode 100644 test/integration/targets/any_errors_fatal/82834.yml diff --git a/changelogs/fragments/82834-any_errors_fatal-unreachable.yml b/changelogs/fragments/82834-any_errors_fatal-unreachable.yml new file mode 100644 index 00000000000..85f7734c494 --- /dev/null +++ b/changelogs/fragments/82834-any_errors_fatal-unreachable.yml @@ -0,0 +1,2 @@ +bugfixes: + - any_errors_fatal - fix an issue where errors caused by hosts being unreachable did not always result in a fatal error (https://github.com/ansible/ansible/issues/82834) diff --git a/lib/ansible/plugins/strategy/linear.py b/lib/ansible/plugins/strategy/linear.py index 71d6380211f..3bd9881f032 100644 --- a/lib/ansible/plugins/strategy/linear.py +++ b/lib/ansible/plugins/strategy/linear.py @@ -371,8 +371,15 @@ class StrategyModule(StrategyBase): unreachable_hosts.append(res._host.name) if any_errors_fatal and (failed_hosts or unreachable_hosts): + # After processing the task results hosts_left are now outdated, + # use updated failed and unreachable hosts for filtering. + # Note that `failed_hosts` contains hosts that were already + # failed in `_process_pending_results` but are not necessarily + # in `_tqm._failed_hosts` as they may still have to execute + # rescue/always section, so we can't fail them again now. + ignore_hosts = set(failed_hosts).union(self._tqm._failed_hosts, self._tqm._unreachable_hosts) for host in hosts_left: - if host.name not in failed_hosts: + if host.name not in ignore_hosts: self._tqm._failed_hosts[host.name] = True iterator.mark_host_failed(host) display.debug("done checking for any_errors_fatal") @@ -382,6 +389,8 @@ class StrategyModule(StrategyBase): percentage = iterator._play.max_fail_percentage / 100.0 if (len(self._tqm._failed_hosts) / iterator.batch_size) > percentage: + # FIXME hosts_left should also be filtered further on updated + # host statuses as with any_errors_fatal above for host in hosts_left: # don't double-mark hosts, or the iterator will potentially # fail them out of the rescue/always states diff --git a/test/integration/targets/any_errors_fatal/82834.yml b/test/integration/targets/any_errors_fatal/82834.yml new file mode 100644 index 00000000000..69b9222e41c --- /dev/null +++ b/test/integration/targets/any_errors_fatal/82834.yml @@ -0,0 +1,13 @@ +- hosts: localhost + gather_facts: false + tasks: + - add_host: + hostname: unreachable_host + +- hosts: unreachable_host, localhost + gather_facts: false + any_errors_fatal: true + serial: + - 1 + tasks: + - command: echo "SHOULD NOT HAPPEN" diff --git a/test/integration/targets/any_errors_fatal/runme.sh b/test/integration/targets/any_errors_fatal/runme.sh index 58f0ddfcafa..da54b52a1e1 100755 --- a/test/integration/targets/any_errors_fatal/runme.sh +++ b/test/integration/targets/any_errors_fatal/runme.sh @@ -49,3 +49,7 @@ ansible-playbook -i inventory "$@" 80981.yml | tee out.txt [ "$(grep -c 'SHOULD NOT HAPPEN' out.txt)" -eq 0 ] [ "$(grep -c 'rescue' out.txt)" -eq 2 ] [ "$(grep -c 'recovered' out.txt)" -eq 2 ] + +# -v here is intentional as we rely on the output of the command module that is by default not printed +ansible-playbook -i inventory -v "$@" 82834.yml | tee out.txt +[ "$(grep -c 'SHOULD NOT HAPPEN' out.txt)" -eq 0 ]