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
This commit is contained in:
parent
fe20546d36
commit
e201b542be
7 changed files with 41 additions and 2 deletions
2
changelogs/fragments/73971-non-batch-end_play.yml
Normal file
2
changelogs/fragments/73971-non-batch-end_play.yml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
bugfixes:
|
||||||
|
- Ensure end_play ends play, not batch (https://github.com/ansible/ansible/issues/73971)
|
|
@ -216,6 +216,8 @@ class PlayIterator:
|
||||||
# plays won't try to advance)
|
# plays won't try to advance)
|
||||||
play_context.start_at_task = None
|
play_context.start_at_task = None
|
||||||
|
|
||||||
|
self.end_play = False
|
||||||
|
|
||||||
def get_host_state(self, host):
|
def get_host_state(self, host):
|
||||||
# Since we're using the PlayIterator to carry forward failed hosts,
|
# Since we're using the PlayIterator to carry forward failed hosts,
|
||||||
# in the event that a previous host was not in the current inventory
|
# in the event that a previous host was not in the current inventory
|
||||||
|
|
|
@ -23,7 +23,7 @@ import os
|
||||||
|
|
||||||
from ansible import constants as C
|
from ansible import constants as C
|
||||||
from ansible import context
|
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._text import to_text
|
||||||
from ansible.module_utils.parsing.convert_bool import boolean
|
from ansible.module_utils.parsing.convert_bool import boolean
|
||||||
from ansible.plugins.loader import become_loader, connection_loader, shell_loader
|
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
|
# restrict the inventory to the hosts in the serialized batch
|
||||||
self._inventory.restrict_to_hosts(batch)
|
self._inventory.restrict_to_hosts(batch)
|
||||||
# and run it...
|
# 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
|
# break the play if the result equals the special return code
|
||||||
if result & self._tqm.RUN_FAILED_BREAK_PLAY != 0:
|
if result & self._tqm.RUN_FAILED_BREAK_PLAY != 0:
|
||||||
|
|
|
@ -81,6 +81,11 @@ class FinalQueue(multiprocessing.queues.Queue):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AnsibleEndPlay(Exception):
|
||||||
|
def __init__(self, result):
|
||||||
|
self.result = result
|
||||||
|
|
||||||
|
|
||||||
class TaskQueueManager:
|
class TaskQueueManager:
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
@ -323,6 +328,9 @@ class TaskQueueManager:
|
||||||
for host_name in iterator.get_failed_hosts():
|
for host_name in iterator.get_failed_hosts():
|
||||||
self._failed_hosts[host_name] = True
|
self._failed_hosts[host_name] = True
|
||||||
|
|
||||||
|
if iterator.end_play:
|
||||||
|
raise AnsibleEndPlay(play_return)
|
||||||
|
|
||||||
return play_return
|
return play_return
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
|
|
|
@ -1161,6 +1161,9 @@ class StrategyBase:
|
||||||
for host in self._inventory.get_hosts(iterator._play.hosts):
|
for host in self._inventory.get_hosts(iterator._play.hosts):
|
||||||
if host.name not in self._tqm._unreachable_hosts:
|
if host.name not in self._tqm._unreachable_hosts:
|
||||||
iterator._host_states[host.name].run_state = iterator.ITERATING_COMPLETE
|
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"
|
msg = "ending play"
|
||||||
else:
|
else:
|
||||||
skipped = True
|
skipped = True
|
||||||
|
|
|
@ -49,4 +49,10 @@ for test_strategy in linear free; do
|
||||||
|
|
||||||
grep -q "META: ending play" <<< "$out"
|
grep -q "META: ending play" <<< "$out"
|
||||||
grep -qv 'Failed to end using end_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
|
done
|
||||||
|
|
|
@ -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'
|
Loading…
Reference in a new issue