summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--changelogs/fragments/84019-ignore_unreachable-loop.yml2
-rw-r--r--lib/ansible/executor/task_executor.py1
-rwxr-xr-xtest/integration/targets/ignore_unreachable/runme.sh8
-rw-r--r--test/integration/targets/ignore_unreachable/test_base_loop_cannot_connect.yml41
4 files changed, 52 insertions, 0 deletions
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'