From 29de2cccba67f1feda93f28e9056240de4e35c3c Mon Sep 17 00:00:00 2001 From: Martin Krizek Date: Wed, 26 Jan 2022 15:43:12 +0100 Subject: [PATCH] Fix task debugger to work with run_once using linear strategy (#76814) * Fix task debugger to work with run_once using linear strategy Fixes #76049 * Fix clog * Add integration test --- .../76049-task-debugger-run_once.yml | 2 ++ lib/ansible/plugins/strategy/__init__.py | 7 ++++ test/integration/targets/debugger/aliases | 3 ++ test/integration/targets/debugger/inventory | 2 ++ test/integration/targets/debugger/runme.sh | 5 +++ .../targets/debugger/test_run_once.py | 35 +++++++++++++++++++ .../debugger/test_run_once_playbook.yml | 12 +++++++ 7 files changed, 66 insertions(+) create mode 100644 changelogs/fragments/76049-task-debugger-run_once.yml create mode 100644 test/integration/targets/debugger/aliases create mode 100644 test/integration/targets/debugger/inventory create mode 100755 test/integration/targets/debugger/runme.sh create mode 100755 test/integration/targets/debugger/test_run_once.py create mode 100644 test/integration/targets/debugger/test_run_once_playbook.yml diff --git a/changelogs/fragments/76049-task-debugger-run_once.yml b/changelogs/fragments/76049-task-debugger-run_once.yml new file mode 100644 index 00000000000..bb6c40d8aca --- /dev/null +++ b/changelogs/fragments/76049-task-debugger-run_once.yml @@ -0,0 +1,2 @@ +bugfixes: + - "Fix task debugger to work with ``run_once`` using ``linear`` strategy (https://github.com/ansible/ansible/issues/76049)" diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index 5af86e3949d..faf48e5acc8 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -53,6 +53,7 @@ from ansible.playbook.task_include import TaskInclude from ansible.plugins import loader as plugin_loader from ansible.template import Templar from ansible.utils.display import Display +from ansible.utils.fqcn import add_internal_fqcns from ansible.utils.unsafe_proxy import wrap_var from ansible.utils.vars import combine_vars from ansible.vars.clean import strip_internal_keys, module_response_deepcopy @@ -158,6 +159,12 @@ def debug_closure(func): if next_action.result == NextAction.REDO: # rollback host state self._tqm.clear_failed_hosts() + if task.run_once and iterator._play.strategy in add_internal_fqcns(('linear',)) and result.is_failed(): + for host_name, state in prev_host_states.items(): + if host_name == host.name: + continue + iterator.set_state_for_host(host_name, state) + iterator._play._removed_hosts.remove(host_name) iterator.set_state_for_host(host.name, prev_host_state) for method, what in status_to_stats_map: if getattr(result, method)(): diff --git a/test/integration/targets/debugger/aliases b/test/integration/targets/debugger/aliases new file mode 100644 index 00000000000..7bf2c12136d --- /dev/null +++ b/test/integration/targets/debugger/aliases @@ -0,0 +1,3 @@ +shippable/posix/group1 +context/controller +setup/always/setup_pexpect diff --git a/test/integration/targets/debugger/inventory b/test/integration/targets/debugger/inventory new file mode 100644 index 00000000000..81502d552ae --- /dev/null +++ b/test/integration/targets/debugger/inventory @@ -0,0 +1,2 @@ +testhost ansible_connection=local +testhost2 ansible_connection=local diff --git a/test/integration/targets/debugger/runme.sh b/test/integration/targets/debugger/runme.sh new file mode 100755 index 00000000000..6a51d23d065 --- /dev/null +++ b/test/integration/targets/debugger/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +./test_run_once.py -i inventory "$@" diff --git a/test/integration/targets/debugger/test_run_once.py b/test/integration/targets/debugger/test_run_once.py new file mode 100755 index 00000000000..237f9c2d903 --- /dev/null +++ b/test/integration/targets/debugger/test_run_once.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import io +import os +import sys + +import pexpect + + +env_vars = { + 'ANSIBLE_NOCOLOR': 'True', + 'ANSIBLE_RETRY_FILES_ENABLED': 'False', +} + +env = os.environ.copy() +env.update(env_vars) + +with io.BytesIO() as logfile: + debugger_test_test = pexpect.spawn( + 'ansible-playbook', + args=['test_run_once_playbook.yml'] + sys.argv[1:], + timeout=10, + env=env + ) + + debugger_test_test.logfile = logfile + + debugger_test_test.expect_exact('TASK: Task 1 (debug)> ') + debugger_test_test.send('task.args["that"] = "true"\r') + debugger_test_test.expect_exact('TASK: Task 1 (debug)> ') + debugger_test_test.send('r\r') + debugger_test_test.expect(pexpect.EOF) + debugger_test_test.close() + + assert str(logfile.getvalue()).count('Task 2 executed') == 2 diff --git a/test/integration/targets/debugger/test_run_once_playbook.yml b/test/integration/targets/debugger/test_run_once_playbook.yml new file mode 100644 index 00000000000..ede3a537074 --- /dev/null +++ b/test/integration/targets/debugger/test_run_once_playbook.yml @@ -0,0 +1,12 @@ +- hosts: testhost, testhost2 + gather_facts: false + debugger: on_failed + tasks: + - name: Task 1 + assert: + that: 'false' + run_once: yes + + - name: Task 2 + debug: + msg: "Task 2 executed"