diff --git a/changelogs/fragments/75642-free-strat-fix-executing-includes.yml b/changelogs/fragments/75642-free-strat-fix-executing-includes.yml new file mode 100644 index 00000000000..6c11758cb97 --- /dev/null +++ b/changelogs/fragments/75642-free-strat-fix-executing-includes.yml @@ -0,0 +1,2 @@ +bugfixes: + - Fix executing includes in the always section in the free strategy (https://github.com/ansible/ansible/issues/75642) diff --git a/lib/ansible/plugins/strategy/free.py b/lib/ansible/plugins/strategy/free.py index 7e0406984d8..326c347ad3e 100644 --- a/lib/ansible/plugins/strategy/free.py +++ b/lib/ansible/plugins/strategy/free.py @@ -120,17 +120,19 @@ class StrategyModule(StrategyBase): (state, task) = iterator.get_next_task_for_host(host, peek=True) display.debug("free host state: %s" % state, host=host_name) display.debug("free host task: %s" % task, host=host_name) - if host_name not in self._tqm._unreachable_hosts and task: + # check if there is work to do, either there is a task or the host is still blocked which could + # mean that it is processing an include task and after its result is processed there might be + # more tasks to run + if (task or self._blocked_hosts.get(host_name, False)) and not self._tqm._unreachable_hosts.get(host_name, False): + display.debug("this host has work to do", host=host_name) # set the flag so the outer loop knows we've still found # some work which needs to be done work_to_do = True - display.debug("this host has work to do", host=host_name) - + if not self._tqm._unreachable_hosts.get(host_name, False) and task: # check to see if this host is blocked (still executing a previous task) - if (host_name not in self._blocked_hosts or not self._blocked_hosts[host_name]): - + if not self._blocked_hosts.get(host_name, False): display.debug("getting variables", host=host_name) task_vars = self._variable_manager.get_vars(play=iterator._play, host=host, task=task, _hosts=self._hosts_cache, diff --git a/test/integration/targets/strategy_free/aliases b/test/integration/targets/strategy_free/aliases new file mode 100644 index 00000000000..b59832142f2 --- /dev/null +++ b/test/integration/targets/strategy_free/aliases @@ -0,0 +1 @@ +shippable/posix/group3 diff --git a/test/integration/targets/strategy_free/inventory b/test/integration/targets/strategy_free/inventory new file mode 100644 index 00000000000..39034f1435e --- /dev/null +++ b/test/integration/targets/strategy_free/inventory @@ -0,0 +1,2 @@ +[local] +testhost ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/test/integration/targets/strategy_free/last_include_tasks.yml b/test/integration/targets/strategy_free/last_include_tasks.yml new file mode 100644 index 00000000000..6c87242ccb0 --- /dev/null +++ b/test/integration/targets/strategy_free/last_include_tasks.yml @@ -0,0 +1,2 @@ +- debug: + msg: "INCLUDED TASK EXECUTED" diff --git a/test/integration/targets/strategy_free/runme.sh b/test/integration/targets/strategy_free/runme.sh new file mode 100755 index 00000000000..f5b912c6da7 --- /dev/null +++ b/test/integration/targets/strategy_free/runme.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -eux + +export ANSIBLE_STRATEGY=free + +set +e +result="$(ansible-playbook test_last_include_in_always.yml -i inventory "$@" 2>&1)" +set -e +grep -q "INCLUDED TASK EXECUTED" <<< "$result" diff --git a/test/integration/targets/strategy_free/test_last_include_in_always.yml b/test/integration/targets/strategy_free/test_last_include_in_always.yml new file mode 100644 index 00000000000..205f3231277 --- /dev/null +++ b/test/integration/targets/strategy_free/test_last_include_in_always.yml @@ -0,0 +1,9 @@ +- hosts: testhost + gather_facts: false + strategy: free + tasks: + - block: + - name: EXPECTED FAILURE + fail: + always: + - include_tasks: last_include_tasks.yml