diff options
author | Martin Krizek <martin.krizek@gmail.com> | 2021-06-03 09:26:22 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-03 09:26:22 +0200 |
commit | e201b542be23bccd2418eab661cdf5454af3bea8 (patch) | |
tree | 7d5d12df8c39d87ef0b84fd753993cf90c063cc8 | |
parent | first_found: clear up the skip option usage (#74816) (diff) | |
download | ansible-e201b542be23bccd2418eab661cdf5454af3bea8.tar.xz ansible-e201b542be23bccd2418eab661cdf5454af3bea8.zip |
Ensure end_play ends play, not batch (#74332)
* Ensure end_play ends play, not batch
Fixes #73971
ci_complete
* Preserve result
* Move AnsibleEndPlay to TQM
* Add tests
* Add changelog
* Explaining comment
* Fix changelog name
* ci_complete
-rw-r--r-- | changelogs/fragments/73971-non-batch-end_play.yml | 2 | ||||
-rw-r--r-- | lib/ansible/executor/play_iterator.py | 2 | ||||
-rw-r--r-- | lib/ansible/executor/playbook_executor.py | 9 | ||||
-rw-r--r-- | lib/ansible/executor/task_queue_manager.py | 8 | ||||
-rw-r--r-- | lib/ansible/plugins/strategy/__init__.py | 3 | ||||
-rwxr-xr-x | test/integration/targets/meta_tasks/runme.sh | 6 | ||||
-rw-r--r-- | test/integration/targets/meta_tasks/test_end_play_serial_one.yml | 13 |
7 files changed, 41 insertions, 2 deletions
diff --git a/changelogs/fragments/73971-non-batch-end_play.yml b/changelogs/fragments/73971-non-batch-end_play.yml new file mode 100644 index 0000000000..6445d9ff37 --- /dev/null +++ b/changelogs/fragments/73971-non-batch-end_play.yml @@ -0,0 +1,2 @@ +bugfixes: + - Ensure end_play ends play, not batch (https://github.com/ansible/ansible/issues/73971) diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py index 10494bc341..96f87c2cc9 100644 --- a/lib/ansible/executor/play_iterator.py +++ b/lib/ansible/executor/play_iterator.py @@ -216,6 +216,8 @@ class PlayIterator: # plays won't try to advance) play_context.start_at_task = None + self.end_play = False + def get_host_state(self, host): # Since we're using the PlayIterator to carry forward failed hosts, # in the event that a previous host was not in the current inventory diff --git a/lib/ansible/executor/playbook_executor.py b/lib/ansible/executor/playbook_executor.py index bfaecba91b..e0d3a645aa 100644 --- a/lib/ansible/executor/playbook_executor.py +++ b/lib/ansible/executor/playbook_executor.py @@ -23,7 +23,7 @@ import os from ansible import constants as C from ansible import context -from ansible.executor.task_queue_manager import TaskQueueManager +from ansible.executor.task_queue_manager import TaskQueueManager, AnsibleEndPlay from ansible.module_utils._text import to_text from ansible.module_utils.parsing.convert_bool import boolean from ansible.plugins.loader import become_loader, connection_loader, shell_loader @@ -186,7 +186,12 @@ class PlaybookExecutor: # restrict the inventory to the hosts in the serialized batch self._inventory.restrict_to_hosts(batch) # and run it... - result = self._tqm.run(play=play) + try: + result = self._tqm.run(play=play) + except AnsibleEndPlay as e: + result = e.result + break_play = True + break # break the play if the result equals the special return code if result & self._tqm.RUN_FAILED_BREAK_PLAY != 0: diff --git a/lib/ansible/executor/task_queue_manager.py b/lib/ansible/executor/task_queue_manager.py index eaa262d87a..48ebf1aaff 100644 --- a/lib/ansible/executor/task_queue_manager.py +++ b/lib/ansible/executor/task_queue_manager.py @@ -81,6 +81,11 @@ class FinalQueue(multiprocessing.queues.Queue): ) +class AnsibleEndPlay(Exception): + def __init__(self, result): + self.result = result + + class TaskQueueManager: ''' @@ -323,6 +328,9 @@ class TaskQueueManager: for host_name in iterator.get_failed_hosts(): self._failed_hosts[host_name] = True + if iterator.end_play: + raise AnsibleEndPlay(play_return) + return play_return def cleanup(self): diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index 90f7cbc19d..7f4dafa4c9 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -1161,6 +1161,9 @@ class StrategyBase: for host in self._inventory.get_hosts(iterator._play.hosts): if host.name not in self._tqm._unreachable_hosts: iterator._host_states[host.name].run_state = iterator.ITERATING_COMPLETE + # end_play is used in PlaybookExecutor/TQM to indicate that + # the whole play is supposed to be ended as opposed to just a batch + iterator.end_play = True msg = "ending play" else: skipped = True diff --git a/test/integration/targets/meta_tasks/runme.sh b/test/integration/targets/meta_tasks/runme.sh index 3ee419cb06..cc951bbba5 100755 --- a/test/integration/targets/meta_tasks/runme.sh +++ b/test/integration/targets/meta_tasks/runme.sh @@ -49,4 +49,10 @@ for test_strategy in linear free; do grep -q "META: ending play" <<< "$out" grep -qv 'Failed to end using end_play' <<< "$out" + + out="$(ansible-playbook test_end_play_serial_one.yml -i inventory.yml -e test_strategy=$test_strategy -vv "$@")" + + [ "$(grep -c "Testing end_play on host" <<< "$out" )" -eq 1 ] + grep -q "META: ending play" <<< "$out" + grep -qv 'Failed to end using end_play' <<< "$out" done diff --git a/test/integration/targets/meta_tasks/test_end_play_serial_one.yml b/test/integration/targets/meta_tasks/test_end_play_serial_one.yml new file mode 100644 index 0000000000..f838d4a6bc --- /dev/null +++ b/test/integration/targets/meta_tasks/test_end_play_serial_one.yml @@ -0,0 +1,13 @@ +- name: Testing end_play with serial 1 and strategy {{ test_strategy | default('linear') }} + hosts: testhost:testhost2 + gather_facts: no + serial: 1 + strategy: "{{ test_strategy | default('linear') }}" + tasks: + - debug: + msg: "Testing end_play on host {{ inventory_hostname }}" + + - meta: end_play + + - fail: + msg: 'Failed to end using end_play' |