From 4c04b8c7c3f43b9c50831e97e4479e428cef9bd6 Mon Sep 17 00:00:00 2001 From: Martin Krizek Date: Thu, 7 Aug 2025 21:52:47 +0200 Subject: [PATCH] IncludedFile: store _from args for proper deduplication (#85628) * IncludedFile: store _from args for proper deduplication Fixes #66497 Co-authored-by: Matt Martz --- .../66497-include_role-_from-dedup.yml | 2 ++ lib/ansible/playbook/included_file.py | 2 +- .../targets/includes/includes_from_dedup.yml | 18 ++++++++++++++++++ .../roles/role_from_args/tasks/localhost.yml | 2 ++ .../roles/role_from_args/tasks/testhost.yml | 2 ++ .../roles/role_from_args/vars/localhost.yml | 1 + .../roles/role_from_args/vars/testhost.yml | 1 + test/integration/targets/includes/runme.sh | 2 ++ test/units/playbook/test_included_file.py | 4 ++-- 9 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 changelogs/fragments/66497-include_role-_from-dedup.yml create mode 100644 test/integration/targets/includes/includes_from_dedup.yml create mode 100644 test/integration/targets/includes/roles/role_from_args/tasks/localhost.yml create mode 100644 test/integration/targets/includes/roles/role_from_args/tasks/testhost.yml create mode 100644 test/integration/targets/includes/roles/role_from_args/vars/localhost.yml create mode 100644 test/integration/targets/includes/roles/role_from_args/vars/testhost.yml diff --git a/changelogs/fragments/66497-include_role-_from-dedup.yml b/changelogs/fragments/66497-include_role-_from-dedup.yml new file mode 100644 index 00000000000..95497230160 --- /dev/null +++ b/changelogs/fragments/66497-include_role-_from-dedup.yml @@ -0,0 +1,2 @@ +bugfixes: + - include_role - allow host specific values in all ``*_from`` arguments (https://github.com/ansible/ansible/issues/66497) diff --git a/lib/ansible/playbook/included_file.py b/lib/ansible/playbook/included_file.py index ace611d86f2..51cdc7281a7 100644 --- a/lib/ansible/playbook/included_file.py +++ b/lib/ansible/playbook/included_file.py @@ -203,7 +203,7 @@ class IncludedFile: for from_arg in new_task.FROM_ARGS: if from_arg in include_args: from_key = from_arg.removesuffix('_from') - new_task._from_files[from_key] = include_args.pop(from_arg) + new_task._from_files[from_key] = include_args.get(from_arg) inc_file = IncludedFile(role_name, include_args, special_vars, new_task, is_role=True) diff --git a/test/integration/targets/includes/includes_from_dedup.yml b/test/integration/targets/includes/includes_from_dedup.yml new file mode 100644 index 00000000000..0902e359cab --- /dev/null +++ b/test/integration/targets/includes/includes_from_dedup.yml @@ -0,0 +1,18 @@ +- hosts: testhost,localhost + tasks: + - include_role: + name: role_from_args + vars_from: "{{ inventory_hostname }}.yml" + tasks_from: "{{ inventory_hostname }}.yml" + + - assert: + that: + - f1 is defined + - f1 == inventory_hostname + when: inventory_hostname == "testhost" + + - assert: + that: + - f2 is defined + - f2 == inventory_hostname + when: inventory_hostname == "localhost" diff --git a/test/integration/targets/includes/roles/role_from_args/tasks/localhost.yml b/test/integration/targets/includes/roles/role_from_args/tasks/localhost.yml new file mode 100644 index 00000000000..bd32273d800 --- /dev/null +++ b/test/integration/targets/includes/roles/role_from_args/tasks/localhost.yml @@ -0,0 +1,2 @@ +- set_fact: + f2: "{{ v }}" diff --git a/test/integration/targets/includes/roles/role_from_args/tasks/testhost.yml b/test/integration/targets/includes/roles/role_from_args/tasks/testhost.yml new file mode 100644 index 00000000000..33efa34d2f2 --- /dev/null +++ b/test/integration/targets/includes/roles/role_from_args/tasks/testhost.yml @@ -0,0 +1,2 @@ +- set_fact: + f1: "{{ v }}" diff --git a/test/integration/targets/includes/roles/role_from_args/vars/localhost.yml b/test/integration/targets/includes/roles/role_from_args/vars/localhost.yml new file mode 100644 index 00000000000..be240abd4c4 --- /dev/null +++ b/test/integration/targets/includes/roles/role_from_args/vars/localhost.yml @@ -0,0 +1 @@ +v: localhost diff --git a/test/integration/targets/includes/roles/role_from_args/vars/testhost.yml b/test/integration/targets/includes/roles/role_from_args/vars/testhost.yml new file mode 100644 index 00000000000..1d1e5d31fa2 --- /dev/null +++ b/test/integration/targets/includes/roles/role_from_args/vars/testhost.yml @@ -0,0 +1 @@ +v: testhost diff --git a/test/integration/targets/includes/runme.sh b/test/integration/targets/includes/runme.sh index 97effac9cd8..f87e4048b6d 100755 --- a/test/integration/targets/includes/runme.sh +++ b/test/integration/targets/includes/runme.sh @@ -16,3 +16,5 @@ grep -q "'include_tasks' is not a valid attribute for a Play" <<< "$result" ansible-playbook includes_loop_rescue.yml --extra-vars strategy=linear "$@" ansible-playbook includes_loop_rescue.yml --extra-vars strategy=free "$@" + +ansible-playbook includes_from_dedup.yml -i ../../inventory "$@" diff --git a/test/units/playbook/test_included_file.py b/test/units/playbook/test_included_file.py index 94976d82d83..8c7b39cd67b 100644 --- a/test/units/playbook/test_included_file.py +++ b/test/units/playbook/test_included_file.py @@ -312,8 +312,8 @@ def test_process_include_simulate_free_block_role_tasks(mock_iterator, mock_vari assert res[0]._hosts == [host1] assert res[1]._hosts == [host2] - assert res[0]._args == {} - assert res[1]._args == {} + assert res[0]._args == {'tasks_from': 'task1.yml'} + assert res[1]._args == {'tasks_from': 'task2.yml'} assert res[0]._vars == {} assert res[1]._vars == {}