diff --git a/changelogs/fragments/include-apply-insert-order.yaml b/changelogs/fragments/include-apply-insert-order.yaml new file mode 100644 index 0000000000..b4b9d55bc0 --- /dev/null +++ b/changelogs/fragments/include-apply-insert-order.yaml @@ -0,0 +1,2 @@ +bugfixes: +- include - Change order of where the new block is inserted with apply so that apply args are not applied to the include also (https://github.com/ansible/ansible/pull/44912) diff --git a/lib/ansible/playbook/role_include.py b/lib/ansible/playbook/role_include.py index bef6ca65a3..057964752f 100644 --- a/lib/ansible/playbook/role_include.py +++ b/lib/ansible/playbook/role_include.py @@ -100,14 +100,16 @@ class IncludeRole(TaskInclude): dep_chain = list(self._parent_role._parents) dep_chain.append(self._parent_role) + p_block = self.build_parent_block() + blocks = actual_role.compile(play=myplay, dep_chain=dep_chain) for b in blocks: - b._parent = self + b._parent = p_block # updated available handlers in play handlers = actual_role.get_handler_blocks(play=myplay) for h in handlers: - h._parent = self + h._parent = p_block myplay.handlers = myplay.handlers + handlers return blocks, handlers @@ -143,22 +145,11 @@ class IncludeRole(TaskInclude): from_key = key.replace('_from', '') ir._from_files[from_key] = basename(ir.args.get(key)) - apply_attrs = ir.args.pop('apply', {}) + apply_attrs = ir.args.get('apply', {}) if apply_attrs and ir.action != 'include_role': raise AnsibleParserError('Invalid options for %s: apply' % ir.action, obj=data) - elif apply_attrs: - apply_attrs['block'] = [] - p_block = Block.load( - apply_attrs, - play=block._play, - parent_block=block, - role=role, - task_include=task_include, - use_handlers=block._use_handlers, - variable_manager=variable_manager, - loader=loader, - ) - ir._parent = p_block + elif not isinstance(apply_attrs, dict): + raise AnsibleParserError('Expected a dict for apply but got %s instead' % type(apply_attrs), obj=data) # manual list as otherwise the options would set other task parameters we don't want. for option in my_arg_names.intersection(IncludeRole.OTHER_ARGS): diff --git a/lib/ansible/playbook/task_include.py b/lib/ansible/playbook/task_include.py index f4266d1b62..ba048311a1 100644 --- a/lib/ansible/playbook/task_include.py +++ b/lib/ansible/playbook/task_include.py @@ -69,22 +69,11 @@ class TaskInclude(Task): if not task.args.get('_raw_params'): task.args['_raw_params'] = task.args.pop('file') - apply_attrs = task.args.pop('apply', {}) + apply_attrs = task.args.get('apply', {}) if apply_attrs and task.action != 'include_tasks': raise AnsibleParserError('Invalid options for %s: apply' % task.action, obj=data) - elif apply_attrs: - apply_attrs['block'] = [] - p_block = Block.load( - apply_attrs, - play=block._play, - parent_block=block, - role=role, - task_include=task_include, - use_handlers=block._use_handlers, - variable_manager=variable_manager, - loader=loader, - ) - task._parent = p_block + elif not isinstance(apply_attrs, dict): + raise AnsibleParserError('Expected a dict for apply but got %s instead' % type(apply_attrs), obj=data) return task @@ -115,3 +104,24 @@ class TaskInclude(Task): del all_vars['when'] return all_vars + + def build_parent_block(self): + ''' + This method is used to create the parent block for the included tasks + when ``apply`` is specified + ''' + apply_attrs = self.args.pop('apply', {}) + if apply_attrs: + apply_attrs['block'] = [] + p_block = Block.load( + apply_attrs, + play=self._parent._play, + task_include=self, + role=self._role, + variable_manager=self._variable_manager, + loader=self._loader, + ) + else: + p_block = self + + return p_block diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index b81518214e..6d9f777951 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -833,8 +833,7 @@ class StrategyBase: block_list = load_list_of_blocks( data, play=iterator._play, - parent_block=None, - task_include=ti_copy, + parent_block=ti_copy.build_parent_block(), role=included_file._task._role, use_handlers=is_handler, loader=self._loader, diff --git a/test/integration/targets/include_import/apply/include_apply.yml b/test/integration/targets/include_import/apply/include_apply.yml index 8f71530268..ca41b38df5 100644 --- a/test/integration/targets/include_import/apply/include_apply.yml +++ b/test/integration/targets/include_import/apply/include_apply.yml @@ -29,3 +29,17 @@ - include_role_result is defined tags: - always + + - include_role: + name: include_role2 + apply: + tags: + - foo + tags: + - not_specified_on_purpose + + - assert: + that: + - include_role2_result is undefined + tags: + - always diff --git a/test/integration/targets/include_import/apply/roles/include_role2/tasks/main.yml b/test/integration/targets/include_import/apply/roles/include_role2/tasks/main.yml new file mode 100644 index 0000000000..028c30d517 --- /dev/null +++ b/test/integration/targets/include_import/apply/roles/include_role2/tasks/main.yml @@ -0,0 +1,2 @@ +- set_fact: + include_role2_result: true