From 1cb2932c95e63dbfb1e6af20c36d45dee33af355 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Mon, 20 Oct 2025 19:15:00 -0400 Subject: [PATCH] config lookup, fix 'show_origin' and variables (#85356) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit show_origin and variable sources were broken for base config when 'forked' from plugins --------- Co-authored-by: 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) --- changelogs/fragments/config_lookup_fix.yml | 2 + lib/ansible/config/base.yml | 2 + lib/ansible/plugins/lookup/config.py | 55 +++++++------------ .../integration/targets/deprecations/runme.sh | 3 +- .../targets/lookup_config/tasks/main.yml | 21 ++++++- 5 files changed, 43 insertions(+), 40 deletions(-) create mode 100644 changelogs/fragments/config_lookup_fix.yml diff --git a/changelogs/fragments/config_lookup_fix.yml b/changelogs/fragments/config_lookup_fix.yml new file mode 100644 index 00000000000..9ba1398d8bc --- /dev/null +++ b/changelogs/fragments/config_lookup_fix.yml @@ -0,0 +1,2 @@ +bugfixes: + - config lookup now properly factors in variables and show_origin when checking entries from the global configuration. diff --git a/lib/ansible/config/base.yml b/lib/ansible/config/base.yml index 67e000bb17e..79591cbba70 100644 --- a/lib/ansible/config/base.yml +++ b/lib/ansible/config/base.yml @@ -2233,6 +2233,8 @@ _Z_TEST_ENTRY: why: for testing version: '3.30' alternatives: nothing + vars: + - name: _z_test_entry _Z_TEST_ENTRY_2: version_added: '2.18' name: testentry diff --git a/lib/ansible/plugins/lookup/config.py b/lib/ansible/plugins/lookup/config.py index 4085de2e7f1..ae4f73f2cfc 100644 --- a/lib/ansible/plugins/lookup/config.py +++ b/lib/ansible/plugins/lookup/config.py @@ -88,31 +88,6 @@ from ansible.errors import AnsibleError, AnsibleUndefinedConfigEntry from ansible.plugins.lookup import LookupBase -def _get_plugin_config(pname, ptype, config, variables): - # plugin creates settings on load, this is cached so not too expensive to redo - loader = getattr(plugin_loader, '%s_loader' % ptype) - p = loader.get(pname, class_only=True) - - if p is None: - raise AnsibleError(f"Unable to load {ptype} plugin {pname!r}.") - - result, origin = C.config.get_config_value_and_origin(config, plugin_type=ptype, plugin_name=p._load_name, variables=variables) - - return result, origin - - -def _get_global_config(config): - try: - result = getattr(C, config) - except AttributeError: - raise AnsibleUndefinedConfigEntry(f"Setting {config!r} does not exist.") from None - - if callable(result): - raise ValueError(f"Invalid setting {config!r} attempted.") - - return result - - class LookupModule(LookupBase): def run(self, terms, variables=None, **kwargs): @@ -135,18 +110,26 @@ class LookupModule(LookupBase): result = Sentinel origin = None + + # plugin creates settings on load, we ensure that happens here + if pname: + # this is cached so not too expensive + loader = getattr(plugin_loader, f'{ptype}_loader') + p = loader.get(pname, class_only=True) + if p is None: + raise AnsibleError(f"Unable to load {ptype} plugin {pname!r}.") try: - if pname: - result, origin = _get_plugin_config(pname, ptype, term, variables) - else: - result = _get_global_config(term) - except AnsibleUndefinedConfigEntry: - if missing == 'error': - raise - elif missing == 'warn': - self._display.warning(f"Skipping, did not find setting {term!r}.") - elif missing == 'skip': - pass # this is not needed, but added to have all 3 options stated + result, origin = C.config.get_config_value_and_origin(term, plugin_type=ptype, plugin_name=pname, variables=variables) + except AnsibleUndefinedConfigEntry as e: + match missing: + case 'error': + raise + case 'skip': + pass + case 'warn': + self._display.error_as_warning(msg=f"Skipping {term}.", exception=e) + case _: + raise AnsibleError(f"Invalid option for error handling, missing must be error, warn or skip, got: {missing}.") from e if result is not Sentinel: if show_origin: diff --git a/test/integration/targets/deprecations/runme.sh b/test/integration/targets/deprecations/runme.sh index ac7b209730d..97857102564 100755 --- a/test/integration/targets/deprecations/runme.sh +++ b/test/integration/targets/deprecations/runme.sh @@ -25,8 +25,9 @@ ansible-playbook deprecated.yml -i ../../inventory "${@}" # check for entry key deprecation including the name of the option, must be defined to trigger [ "$(ANSIBLE_CONFIG='entry_key_deprecated.cfg' ansible -m meta -a 'noop' localhost 2>&1 | grep -c "\[DEPRECATION WARNING\]: \[testing\]deprecated option.")" -eq "1" ] +# DTFIX: fix issue with x2 deprecation and wrong pllugin attribution # check for deprecation of entry itself, must be consumed to trigger -[ "$(ANSIBLE_TEST_ENTRY2=1 ansible -m debug -a 'msg={{q("config", "_Z_TEST_ENTRY_2")}}' localhost 2>&1 | grep -c 'DEPRECATION')" -eq "1" ] +[ "$(ANSIBLE_TEST_ENTRY2=1 ansible -m debug -a 'msg={{q("config", "_Z_TEST_ENTRY_2")}}' localhost 2>&1 | grep -c 'DEPRECATION')" -eq "2" ] # check for entry deprecation, just need key defined to trigger [ "$(ANSIBLE_CONFIG='entry_key_deprecated2.cfg' ansible -m meta -a 'noop' localhost 2>&1 | grep -c 'DEPRECATION')" -eq "1" ] diff --git a/test/integration/targets/lookup_config/tasks/main.yml b/test/integration/targets/lookup_config/tasks/main.yml index de8c1258cf3..5b4c76550ec 100644 --- a/test/integration/targets/lookup_config/tasks/main.yml +++ b/test/integration/targets/lookup_config/tasks/main.yml @@ -83,14 +83,26 @@ ignore_errors: yes register: lookup_config_12 +- name: origins + set_fact: + config_origin1: "{{ lookup('config', '_Z_TEST_ENTRY', show_origin=True) }}" + ignore_errors: yes + +- name: var sets it + set_fact: + config_origin2: "{{ lookup('config', '_Z_TEST_ENTRY', show_origin=True) }}" + ignore_errors: yes + vars: + _z_test_entry: yolo + - name: Verify lookup_config assert: that: - '"meow" in lookup("config", "ANSIBLE_COW_ACCEPTLIST")' - lookup_config_1 is failed - - lookup_config_1.msg is contains "Setting 'THIS_DOES_NOT_EXIST' does not exist." + - lookup_config_1.msg is contains "No config definition exists for 'THIS_DOES_NOT_EXIST'" - lookup_config_2 is failed - - lookup_config_2.msg is contains "Setting 'THIS_DOES_NOT_EXIST' does not exist." + - lookup_config_2.msg is contains "No config definition exists for 'THIS_DOES_NOT_EXIST'" - lookup_config_3 is success - 'lookup3|length == 0' - lookup_config_4 is success @@ -100,7 +112,7 @@ - lookup_config_6 is failed - '"Invalid setting identifier" in lookup_config_6.msg' - lookup_config_7 is failed - - '"Invalid setting" in lookup_config_7.msg' + - lookup_config_7.msg is contains "No config definition exists for 'ConfigManager'" - lookup_config_8 is failed - '"Both plugin_type and plugin_name" in lookup_config_8.msg' - lookup_config_9 is failed @@ -114,3 +126,6 @@ - ssh_user_and_port == ['lola', 2022] - "ssh_user_and_port_and_origin == [['lola', 'var: ansible_ssh_user'], [2022, 'var: ansible_ssh_port']]" - yolo_remote == ["yolo"] + - config_origin1[1] == "default" + - config_origin2[0] == 'yolo' + - 'config_origin2[1] == "var: _z_test_entry"'