diff --git a/changelogs/fragments/module_defaults-action-plugin-templating.yml b/changelogs/fragments/module_defaults-action-plugin-templating.yml new file mode 100644 index 00000000000..81f4d49da51 --- /dev/null +++ b/changelogs/fragments/module_defaults-action-plugin-templating.yml @@ -0,0 +1,2 @@ +bugfixes: + - package, service, gather_facts - fix templating module_defaults for modules executed by these action plugins. (https://github.com/ansible/ansible/issues/85848) diff --git a/lib/ansible/executor/module_common.py b/lib/ansible/executor/module_common.py index a9f8d0a1207..8ac60b36482 100644 --- a/lib/ansible/executor/module_common.py +++ b/lib/ansible/executor/module_common.py @@ -1357,6 +1357,9 @@ def modify_module( def _get_action_arg_defaults(action: str, task: Task, templar: TemplateEngine) -> dict[str, t.Any]: + """ + Get module_defaults that match or contain a fully qualified action/module name. + """ action_groups = task._parent._play._action_groups defaults = task.module_defaults @@ -1393,7 +1396,14 @@ def _get_action_arg_defaults(action: str, task: Task, templar: TemplateEngine) - def _apply_action_arg_defaults(action: str, task: Task, action_args: dict[str, t.Any], templar: Templar) -> dict[str, t.Any]: + """ + Finalize arguments from module_defaults and update with action_args. + + This is used by action plugins like gather_facts, package, and service, + which select modules to execute after normal task argument finalization. + """ args = _get_action_arg_defaults(action, task, templar._engine) + args = templar.template({k: v for k, v in args.items() if k not in action_args}) args.update(action_args) return args diff --git a/test/integration/targets/gathering_facts/runme.sh b/test/integration/targets/gathering_facts/runme.sh index fe3714e0fe7..9714b940fd1 100755 --- a/test/integration/targets/gathering_facts/runme.sh +++ b/test/integration/targets/gathering_facts/runme.sh @@ -26,6 +26,9 @@ ANSIBLE_FACTS_MODULES='ansible.legacy.setup' ansible-playbook test_module_defaul ansible-playbook test_module_defaults.yml "$@" --tags networking +# test gather_facts action templates module_defaults for different module name +ansible-playbook test_module_defaults.yml "$@" --tags templating + # test it works by default ANSIBLE_FACTS_MODULES='ansible.legacy.slow' ansible -m gather_facts localhost --playbook-dir ./ "$@" diff --git a/test/integration/targets/gathering_facts/test_module_defaults.yml b/test/integration/targets/gathering_facts/test_module_defaults.yml index 038b8ecf792..5b3fe89c160 100644 --- a/test/integration/targets/gathering_facts/test_module_defaults.yml +++ b/test/integration/targets/gathering_facts/test_module_defaults.yml @@ -128,3 +128,21 @@ - assert: that: - "ansible_facts.gather_subset == 'min'" + +- name: Test module_defaults templating when the action and module differ + hosts: localhost + gather_facts: True + tags: + - templating + vars: + subset_facts: + - "!all" + - "!min" + - env + module_defaults: + setup: + gather_subset: "{{ subset_facts }}" + tasks: + - assert: + that: + - ansible_facts.gather_subset == subset_facts diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml index 5968c6f620a..215a60ded2b 100644 --- a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml @@ -69,6 +69,8 @@ action_groups: - metadata: extend_group: - testgroup + contains_setup: + - ansible.builtin.setup empty_metadata: - metadata: {} bad_metadata_format: diff --git a/test/integration/targets/module_defaults/test_action_groups.yml b/test/integration/targets/module_defaults/test_action_groups.yml index 33a3c9c5d94..238fd04f3f8 100644 --- a/test/integration/targets/module_defaults/test_action_groups.yml +++ b/test/integration/targets/module_defaults/test_action_groups.yml @@ -130,3 +130,18 @@ - metadata: collections: - testns.testcoll + + - name: Test action plugin templates group defaults + vars: + setup_subset: + - "!all" + - "!min" + - local + module_defaults: + group/testns.testcoll.contains_setup: + gather_subset: "{{ setup_subset }}" + block: + - gather_facts: + - assert: + that: + - ansible_facts.gather_subset == setup_subset diff --git a/test/integration/targets/package/tasks/main.yml b/test/integration/targets/package/tasks/main.yml index f66f11e46bb..5f8ad7f9ef7 100644 --- a/test/integration/targets/package/tasks/main.yml +++ b/test/integration/targets/package/tasks/main.yml @@ -69,28 +69,53 @@ # Verify module_defaults for package and the underlying module are utilized # Validates: https://github.com/ansible/ansible/issues/72918 - block: - # 'name' is required + # note: dnf module unexpectedly succeeds when no arguments are provided. + # Until the requires_one_of (?) bug is fixed, this test asserts changes are made to avoid false positives. - name: install apt with package defaults package: module_defaults: package: name: apt state: present + register: result + + - assert: + that: result is changed + - name: uninstall apt between tests + dnf5: + name: apt + state: absent + + # Validate package handles applying templated defaults for dnf + # https://github.com/ansible/ansible/issues/85848 - name: install apt with dnf defaults (auto) package: module_defaults: - dnf: - name: apt + dnf5: + name: "{{ 'apt' }}" state: present + register: result - - name: install apt with dnf defaults (use dnf) + - assert: + that: result is changed + + - name: uninstall apt between tests + dnf5: + name: apt + state: absent + + - name: install apt with dnf defaults (use dnf5) package: - use: dnf + use: dnf5 module_defaults: - dnf: + dnf5: name: apt state: present + register: result + + - assert: + that: result is changed always: - name: remove apt dnf: diff --git a/test/integration/targets/service/tasks/tests.yml b/test/integration/targets/service/tasks/tests.yml index 8277c0c9969..8f66143f87b 100644 --- a/test/integration/targets/service/tasks/tests.yml +++ b/test/integration/targets/service/tasks/tests.yml @@ -32,10 +32,12 @@ when: "ansible_service_mgr in ['sysvinit', 'systemd']" module_defaults: sysvinit: - name: ansible_test + # Test the action plugin templates sysvinit defaults + # https://github.com/ansible/ansible/issues/85848 + name: "{{ 'ansible_test' }}" enabled: yes systemd: - name: ansible_test + name: "{{ 'ansible_test' }}" enabled: yes - name: assert that changes reported for check mode run