diff --git a/changelogs/fragments/lookup_config.yml b/changelogs/fragments/lookup_config.yml new file mode 100644 index 00000000000..a1315997cd8 --- /dev/null +++ b/changelogs/fragments/lookup_config.yml @@ -0,0 +1,3 @@ +--- +bugfixes: + - config - various fixes to config lookup plugin (https://github.com/ansible/ansible/pull/84398). diff --git a/lib/ansible/plugins/lookup/config.py b/lib/ansible/plugins/lookup/config.py index b31cb057efa..af6daee8fd7 100644 --- a/lib/ansible/plugins/lookup/config.py +++ b/lib/ansible/plugins/lookup/config.py @@ -90,24 +90,20 @@ from ansible.module_utils.six import string_types from ansible.plugins.lookup import LookupBase -class MissingSetting(AnsibleOptionsError): - pass - - def _get_plugin_config(pname, ptype, config, variables): try: # plugin creates settings on load, this is cached so not too expensive to redo - loader = getattr(plugin_loader, '%s_loader' % ptype) + loader = getattr(plugin_loader, f'{ptype}_loader') p = loader.get(pname, class_only=True) if p is None: - raise AnsibleLookupError('Unable to load %s plugin "%s"' % (ptype, pname)) + raise AnsibleLookupError(f'Unable to load {ptype} plugin "{pname}"') result, origin = C.config.get_config_value_and_origin(config, plugin_type=ptype, plugin_name=p._load_name, variables=variables) except AnsibleLookupError: raise except AnsibleError as e: msg = to_native(e) if 'was not defined' in msg: - raise MissingSetting(msg, orig_exc=e) + raise AnsibleOptionsError(msg) from e raise e return result, origin @@ -117,9 +113,9 @@ def _get_global_config(config): try: result = getattr(C, config) if callable(result): - raise AnsibleLookupError('Invalid setting "%s" attempted' % config) + raise AnsibleLookupError(f'Invalid setting "{config}" attempted') except AttributeError as e: - raise MissingSetting(to_native(e), orig_exc=e) + raise AnsibleOptionsError(to_native(e)) from e return result @@ -138,14 +134,11 @@ class LookupModule(LookupBase): if (ptype or pname) and not (ptype and pname): raise AnsibleOptionsError('Both plugin_type and plugin_name are required, cannot use one without the other') - if not isinstance(missing, string_types) or missing not in ['error', 'warn', 'skip']: - raise AnsibleOptionsError('"on_missing" must be a string and one of "error", "warn" or "skip", not %s' % missing) - ret = [] for term in terms: if not isinstance(term, string_types): - raise AnsibleOptionsError('Invalid setting identifier, "%s" is not a string, its a %s' % (term, type(term))) + raise AnsibleOptionsError(f'Invalid setting identifier, "{term}" is not a string, its a {type(term)}') result = Sentinel origin = None @@ -154,13 +147,11 @@ class LookupModule(LookupBase): result, origin = _get_plugin_config(pname, ptype, term, variables) else: result = _get_global_config(term) - except MissingSetting as e: - if missing == 'error': - raise AnsibleLookupError('Unable to find setting %s' % term, orig_exc=e) - elif missing == 'warn': - self._display.warning('Skipping, did not find setting %s' % term) - elif missing == 'skip': - pass # this is not needed, but added to have all 3 options stated + except AnsibleOptionsError as e: + if missing == 'warn': + self._display.warning(f'Skipping, did not find setting {term}') + elif missing != 'skip': + raise AnsibleLookupError(f'Unable to find setting {term}: {e}') from e if result is not Sentinel: if show_origin: diff --git a/test/integration/targets/lookup_config/lookup_plugins/bogus.py b/test/integration/targets/lookup_config/lookup_plugins/bogus.py new file mode 100644 index 00000000000..9c90b3909cc --- /dev/null +++ b/test/integration/targets/lookup_config/lookup_plugins/bogus.py @@ -0,0 +1,43 @@ +# (c) 2021 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import annotations + + +DOCUMENTATION = """ + name: bogus + author: Ansible Core Team + version_added: histerical + short_description: returns what you gave it + description: + - this is mostly a noop + options: + _terms: + description: stuff to pass through + test_list: + description: does nothihng, just for testing values + type: string + required: True +""" + +EXAMPLES = """ +""" + +RETURN = """ + _list: + description: basically the same as you fed in + type: list + elements: raw +""" + +from ansible.plugins.lookup import LookupBase + + +class LookupModule(LookupBase): + + def run(self, terms, variables=None, **kwargs): + + self.set_options(var_options=variables, direct=kwargs) + self.get_option('test_list') + + return terms diff --git a/test/integration/targets/lookup_config/runme.sh b/test/integration/targets/lookup_config/runme.sh new file mode 100755 index 00000000000..485ae3f8293 --- /dev/null +++ b/test/integration/targets/lookup_config/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ANSIBLE_ROLES_PATH=../ ANSIBLE_LOOKUP_PLUGINS=. ansible-playbook runme.yml "$@" diff --git a/test/integration/targets/lookup_config/runme.yml b/test/integration/targets/lookup_config/runme.yml new file mode 100644 index 00000000000..ea7a8e58789 --- /dev/null +++ b/test/integration/targets/lookup_config/runme.yml @@ -0,0 +1,4 @@ +- hosts: localhost + gather_facts: no + roles: + - { role: lookup_config } diff --git a/test/integration/targets/lookup_config/tasks/main.yml b/test/integration/targets/lookup_config/tasks/main.yml index e5699d3484e..62ef767fb51 100644 --- a/test/integration/targets/lookup_config/tasks/main.yml +++ b/test/integration/targets/lookup_config/tasks/main.yml @@ -53,6 +53,36 @@ vars: ansible_remote_tmp: yolo +- name: check if plugin_type and plugin_name is required together + set_fact: + lookup_failure: '{{ q("config", "remote_tmp", plugin_type="shell") }}' + ignore_errors: yes + register: lookup_config_8 + +- name: check if plugin_type and plugin_name is required together + set_fact: + lookup_failure: '{{ q("config", "remote_tmp", plugin_name="sh") }}' + ignore_errors: yes + register: lookup_config_9 + +- name: query non-existent config setting + set_fact: + lookup_failure: "{{ q('config', 'plugin_type1', plugin_type='lookup', plugin_name='config', ) }}" + ignore_errors: yes + register: lookup_config_10 + +- name: query non-existent plugin + set_fact: + lookup_failure: "{{ q('config', 'plugin_type', plugin_type='lookup', plugin_name='some.nonexistent.mylookup', ) }}" + ignore_errors: yes + register: lookup_config_11 + +- name: exception handling while reading configuration + set_fact: + lookup_failure: "{{ q('config', 'test_list', plugin_type='lookup', plugin_name='bogus', ) }}" + ignore_errors: yes + register: lookup_config_12 + - name: Verify lookup_config assert: that: @@ -71,6 +101,16 @@ - '"Invalid setting identifier" in lookup_config_6.msg' - lookup_config_7 is failed - '"Invalid setting" in lookup_config_7.msg' + - lookup_config_8 is failed + - '"Both plugin_type and plugin_name" in lookup_config_8.msg' + - lookup_config_9 is failed + - '"Both plugin_type and plugin_name" in lookup_config_9.msg' + - lookup_config_10 is failed + - '"Unable to find setting plugin_type1" in lookup_config_10.msg' + - lookup_config_11 is failed + - '"Unable to load lookup" in lookup_config_11.msg' + - lookup_config_12 is failed + - '"No setting was provided for required" in lookup_config_12.msg' - 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"]