diff --git a/lib/ansible/plugins/strategy/linear.py b/lib/ansible/plugins/strategy/linear.py index d9a0c185e88..ff51b7d35cd 100644 --- a/lib/ansible/plugins/strategy/linear.py +++ b/lib/ansible/plugins/strategy/linear.py @@ -31,7 +31,7 @@ DOCUMENTATION = ''' author: Ansible Core Team ''' -from ansible.errors import AnsibleError +from ansible.errors import AnsibleError, AnsibleAssertionError from ansible.executor.play_iterator import PlayIterator from ansible.module_utils.six import iteritems from ansible.module_utils._text import to_text @@ -52,6 +52,36 @@ except ImportError: class StrategyModule(StrategyBase): + noop_task = None + + def _replace_with_noop(self, target): + if self.noop_task is None: + raise AnsibleAssertionError('strategy.linear.StrategyModule.noop_task is None, need Task()') + + result = [] + for el in target: + if isinstance(el, Task): + result.append(self.noop_task) + elif isinstance(el, Block): + result.append(self._create_noop_block_from(el, el._parent)) + return result + + def _create_noop_block_from(self, original_block, parent): + noop_block = Block(parent_block=parent) + noop_block.block = self._replace_with_noop(original_block.block) + noop_block.always = self._replace_with_noop(original_block.always) + noop_block.rescue = self._replace_with_noop(original_block.rescue) + + return noop_block + + def _prepare_and_create_noop_block_from(self, original_block, parent, iterator): + self.noop_task = Task() + self.noop_task.action = 'meta' + self.noop_task.args['_raw_params'] = 'noop' + self.noop_task.set_loader(iterator._play._loader) + + return self._create_noop_block_from(original_block, parent) + def _get_next_task_lockstep(self, hosts, iterator): ''' Returns a list of (host, task) tuples, where the task may @@ -309,12 +339,6 @@ class StrategyModule(StrategyBase): if len(included_files) > 0: display.debug("we have included files to process") - # A noop task for use in padding dynamic includes - noop_task = Task() - noop_task.action = 'meta' - noop_task.args['_raw_params'] = 'noop' - noop_task.set_loader(iterator._play._loader) - display.debug("generating all_blocks data") all_blocks = dict((host, []) for host in hosts_left) display.debug("done generating all_blocks data") @@ -345,10 +369,7 @@ class StrategyModule(StrategyBase): final_block = new_block.filter_tagged_tasks(play_context, task_vars) display.debug("done filtering new block on tags") - noop_block = Block(parent_block=task._parent) - noop_block.block = [noop_task for t in new_block.block] - noop_block.always = [noop_task for t in new_block.always] - noop_block.rescue = [noop_task for t in new_block.rescue] + noop_block = self._prepare_and_create_noop_block_from(final_block, task._parent, iterator) for host in hosts_left: if host in included_file._hosts: diff --git a/test/integration/targets/strategy_linear/aliases b/test/integration/targets/strategy_linear/aliases new file mode 100644 index 00000000000..79d8b9285eb --- /dev/null +++ b/test/integration/targets/strategy_linear/aliases @@ -0,0 +1 @@ +posix/ci/group3 diff --git a/test/integration/targets/strategy_linear/roles/role1/tasks/main.yml b/test/integration/targets/strategy_linear/roles/role1/tasks/main.yml new file mode 100644 index 00000000000..51efd43ea68 --- /dev/null +++ b/test/integration/targets/strategy_linear/roles/role1/tasks/main.yml @@ -0,0 +1,6 @@ +- name: Include tasks + include_tasks: "tasks.yml" + +- name: Mark role as finished + set_fact: + role1_complete: True diff --git a/test/integration/targets/strategy_linear/roles/role1/tasks/tasks.yml b/test/integration/targets/strategy_linear/roles/role1/tasks/tasks.yml new file mode 100644 index 00000000000..b7a46aa0980 --- /dev/null +++ b/test/integration/targets/strategy_linear/roles/role1/tasks/tasks.yml @@ -0,0 +1,7 @@ +- name: Call role2 + include_role: + name: role2 + +- name: Call role2 again + include_role: + name: role2 diff --git a/test/integration/targets/strategy_linear/roles/role2/tasks/main.yml b/test/integration/targets/strategy_linear/roles/role2/tasks/main.yml new file mode 100644 index 00000000000..81e041e1acf --- /dev/null +++ b/test/integration/targets/strategy_linear/roles/role2/tasks/main.yml @@ -0,0 +1,7 @@ +- block: + - block: + - name: Nested task 1 + debug: msg="Nested task 1" + + - name: Nested task 2 + debug: msg="Nested task 2" diff --git a/test/integration/targets/strategy_linear/runme.sh b/test/integration/targets/strategy_linear/runme.sh new file mode 100755 index 00000000000..3f95dd76138 --- /dev/null +++ b/test/integration/targets/strategy_linear/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook test_include_file_noop.yml -i ../../inventory "$@" diff --git a/test/integration/targets/strategy_linear/test_include_file_noop.yml b/test/integration/targets/strategy_linear/test_include_file_noop.yml new file mode 100644 index 00000000000..9dbf83dad02 --- /dev/null +++ b/test/integration/targets/strategy_linear/test_include_file_noop.yml @@ -0,0 +1,16 @@ +- hosts: + - testhost + - testhost2 + gather_facts: no + vars: + secondhost: testhost2 + tasks: + - name: Call the first role only on one host + include_role: + name: role1 + when: inventory_hostname is match(secondhost) + + - name: Make sure nothing else runs until role1 finishes + assert: + that: + - "'role1_complete' in hostvars[secondhost]"