From 03acb22f99e0724f38c01a3dfe62574bc08febca Mon Sep 17 00:00:00 2001 From: Sloane Hertel <19572925+s-hertel@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:35:23 -0400 Subject: Fix returning unreachable for looped tasks (#84049) * Fix returning unreachable for looped tasks Add tests for ignore_unreachable and loop --- .../fragments/84019-ignore_unreachable-loop.yml | 2 ++ lib/ansible/executor/task_executor.py | 1 + .../targets/ignore_unreachable/runme.sh | 8 +++++ .../test_base_loop_cannot_connect.yml | 41 ++++++++++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 changelogs/fragments/84019-ignore_unreachable-loop.yml create mode 100644 test/integration/targets/ignore_unreachable/test_base_loop_cannot_connect.yml diff --git a/changelogs/fragments/84019-ignore_unreachable-loop.yml b/changelogs/fragments/84019-ignore_unreachable-loop.yml new file mode 100644 index 0000000000..da85af7e4b --- /dev/null +++ b/changelogs/fragments/84019-ignore_unreachable-loop.yml @@ -0,0 +1,2 @@ +bugfixes: + - Fix returning 'unreachable' for the overall task result. This prevents false positives when a looped task has unignored unreachable items (https://github.com/ansible/ansible/issues/84019). diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index 7299d1a54b..ff1c33871f 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -150,6 +150,7 @@ class TaskExecutor: if 'unreachable' in item and item['unreachable']: item_ignore_unreachable = item.pop('_ansible_ignore_unreachable') if not res.get('unreachable'): + res['unreachable'] = True self._task.ignore_unreachable = item_ignore_unreachable elif self._task.ignore_unreachable and not item_ignore_unreachable: self._task.ignore_unreachable = item_ignore_unreachable diff --git a/test/integration/targets/ignore_unreachable/runme.sh b/test/integration/targets/ignore_unreachable/runme.sh index ff0ab736a0..f05dfdc110 100755 --- a/test/integration/targets/ignore_unreachable/runme.sh +++ b/test/integration/targets/ignore_unreachable/runme.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -eux +export ANSIBLE_TIMEOUT=1 + export ANSIBLE_CONNECTION_PLUGINS=./fake_connectors # use fake connectors that raise errors at different stages ansible-playbook test_with_bad_plugins.yml -i inventory -v "$@" @@ -14,3 +16,9 @@ if ansible-playbook test_base_cannot_connect.yml -i inventory -v "$@"; then else echo "Connection to nonexistent hosts failed without using ignore_unreachable. Success!" fi + +if ansible-playbook test_base_loop_cannot_connect.yml -i inventory -v "$@" > out.txt; then + echo "Playbook intended to fail succeeded. Connection succeeded to nonexistent host" + exit 1 +fi +grep out.txt -e 'ignored=1' | grep 'unreachable=2' | grep 'ok=1' diff --git a/test/integration/targets/ignore_unreachable/test_base_loop_cannot_connect.yml b/test/integration/targets/ignore_unreachable/test_base_loop_cannot_connect.yml new file mode 100644 index 0000000000..2e724bc583 --- /dev/null +++ b/test/integration/targets/ignore_unreachable/test_base_loop_cannot_connect.yml @@ -0,0 +1,41 @@ +- hosts: localhost,nonexistent + gather_facts: false + tasks: + - name: Test ignore_unreachable for all items (pass) + ping: + ignore_unreachable: "{{ item.ignore_unreachable }}" + loop: + - ignore_unreachable: true + - ignore_unreachable: true + register: unreachable_ignored + + - name: Test ignore_unreachable for second item (fail) + ping: + ignore_unreachable: "{{ item.ignore_unreachable }}" + loop: + - ignore_unreachable: false + - ignore_unreachable: true + register: unreachable_first + + - meta: clear_host_errors + + - name: Test ignore_unreachable for first item (fail) + ping: + ignore_unreachable: "{{ item.ignore_unreachable }}" + loop: + - ignore_unreachable: true + - ignore_unreachable: false + register: unreachable_last + + - meta: clear_host_errors + + - assert: + that: + - unreachable_ignored is not unreachable + - unreachable_first["results"][0] is unreachable + - unreachable_first["results"][-1] is not unreachable + - unreachable_first is unreachable + - unreachable_last["results"][-1] is unreachable + - unreachable_last["results"][0] is not unreachable + - unreachable_last is unreachable + when: inventory_hostname == 'nonexistent' -- cgit v1.2.3