Add end_role meta task (#83263)

ci_complete
pull/83786/head
Martin Krizek 3 months ago committed by GitHub
parent fe7e68bfcb
commit 89137cb5a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,2 @@
minor_changes:
- Add a new meta task ``end_role`` (https://github.com/ansible/ansible/issues/22286)

@ -33,7 +33,12 @@ options:
- V(end_host) (added in Ansible 2.8) is a per-host variation of V(end_play). Causes the play to end for the current host without failing it.
- V(end_batch) (added in Ansible 2.12) causes the current batch (see C(serial)) to end without failing the host(s).
Note that with C(serial=0) or undefined this behaves the same as V(end_play).
choices: [ clear_facts, clear_host_errors, end_host, end_play, flush_handlers, noop, refresh_inventory, reset_connection, end_batch ]
- V(end_role) (added in Ansible 2.18) causes the currently executing role to end without failing the host(s).
Effectively all tasks from within a role after V(end_role) is executed are ignored. Since handlers live in a global,
play scope, all handlers added via the role are unaffected and are still executed if notified. It is an error
to call V(end_role) from outside of a role or from a handler. Note that V(end_role) does not have an effect to
the parent roles or roles that depend (via dependencies in meta/main.yml) on a role executing V(end_role).
choices: [ clear_facts, clear_host_errors, end_host, end_play, flush_handlers, noop, refresh_inventory, reset_connection, end_batch, end_role ]
required: true
extends_documentation_fragment:
- action_common_attributes

@ -293,8 +293,12 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
else:
if use_handlers:
t = Handler.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader)
if t.action in C._ACTION_META and t.args.get('_raw_params') == "end_role":
raise AnsibleParserError("Cannot execute 'end_role' from a handler")
else:
t = Task.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader)
if t.action in C._ACTION_META and t.args.get('_raw_params') == "end_role" and role is None:
raise AnsibleParserError("Cannot execute 'end_role' from outside of a role")
task_list.append(t)

@ -1020,13 +1020,23 @@ class StrategyBase:
# TODO: Nix msg here? Left for historical reasons, but skip_reason exists now.
msg = "end_host conditional evaluated to false, continuing execution for %s" % target_host.name
elif meta_action == 'role_complete':
# Allow users to use this in a play as reported in https://github.com/ansible/ansible/issues/22286?
# How would this work with allow_duplicates??
if task.implicit:
role_obj = self._get_cached_role(task, iterator._play)
if target_host.name in role_obj._had_task_run:
role_obj._completed[target_host.name] = True
msg = 'role_complete for %s' % target_host.name
elif meta_action == 'end_role':
if _evaluate_conditional(target_host):
while True:
state, task = iterator.get_next_task_for_host(target_host, peek=True)
if task.action in C._ACTION_META and task.args.get("_raw_params") == "role_complete":
break
iterator.set_state_for_host(target_host.name, state)
display.debug("'%s' skipped because role has been ended via 'end_role'" % task)
msg = 'ending role %s for %s' % (task._role.get_name(), target_host.name)
else:
skipped = True
skip_reason += 'continuing role %s for %s' % (task._role.get_name(), target_host.name)
elif meta_action == 'reset_connection':
all_vars = self._variable_manager.get_vars(play=iterator._play, host=target_host, task=task,
_hosts=self._hosts_cache, _hosts_all=self._hosts_cache_all)

@ -0,0 +1,35 @@
- hosts: localhost
gather_facts: false
pre_tasks:
- set_fact:
play_checkpoint: 1
roles:
- end_role_inside
tasks:
- set_fact:
play_checkpoint: "{{ play_checkpoint|int + 1 }}"
- import_role:
name: end_role_inside
allow_duplicates: true
- set_fact:
play_checkpoint: "{{ play_checkpoint|int + 1 }}"
- include_role:
name: end_role_inside
allow_duplicates: false
- set_fact:
play_checkpoint: "{{ play_checkpoint|int + 1 }}"
post_tasks:
- assert:
that:
- role_executed|int == 2
- after_end_role is undefined
- play_checkpoint|int == 4
- role_handler_ran is defined
- name: when running this playbook check this appears on stdout to ensure the above assert wasn't skipped
debug:
msg: CHECKPOINT

@ -0,0 +1,9 @@
- hosts: localhost
gather_facts: false
tasks:
- debug:
changed_when: true
notify: invalid_handler
handlers:
- name: invalid_handler
meta: end_role

@ -0,0 +1,6 @@
- hosts: host1,host2
gather_facts: false
tasks:
- include_role:
name: end_role_inside
tasks_from: nested.yml

@ -0,0 +1,3 @@
- name: role_handler
set_fact:
role_handler_ran: true

@ -0,0 +1,10 @@
- set_fact:
role_executed: "{{ role_executed|default(0)|int + 1 }}"
- command: echo
notify: role_handler
- meta: end_role
- set_fact:
after_end_role: true

@ -0,0 +1,22 @@
- set_fact:
end_role_cond: "{{ inventory_hostname == 'host1' }}"
- include_role:
name: end_role_inside_nested
- debug:
msg: CHECKPOINT
- assert:
that:
- after_end_role is undefined
when: inventory_hostname == "host1"
- assert:
that:
- after_end_role
when: inventory_hostname == "host2"
- name: when running this playbook check this appears on stdout to ensure the above assert wasn't skipped
debug:
msg: CHECKPOINT

@ -0,0 +1,5 @@
- meta: end_role
when: end_role_cond
- set_fact:
after_end_role: true

@ -53,3 +53,11 @@ ansible-playbook role_dep_chain.yml -i ../../inventory "$@"
ANSIBLE_PRIVATE_ROLE_VARS=1 ansible-playbook privacy.yml -e @vars/privacy_vars.yml "$@"
ANSIBLE_PRIVATE_ROLE_VARS=0 ansible-playbook privacy.yml -e @vars/privacy_vars.yml "$@"
ansible-playbook privacy.yml -e @vars/privacy_vars.yml "$@"
for strategy in linear free; do
[ "$(ANSIBLE_STRATEGY=$strategy ansible-playbook end_role.yml | grep -c CHECKPOINT)" = "1" ]
[ "$(ANSIBLE_STRATEGY=$strategy ansible-playbook -i host1,host2 end_role_nested.yml | grep -c CHECKPOINT)" = "4" ]
done
[ "$(ansible localhost -m meta -a end_role 2>&1 | grep -c "ERROR! Cannot execute 'end_role' from outside of a role")" = "1" ]
[ "$(ansible-playbook end_role_handler_error.yml 2>&1 | grep -c "ERROR! Cannot execute 'end_role' from a handler")" = "1" ]

Loading…
Cancel
Save