Fix a bug adding unrelated candidates to the plugin loader redirect_list (#73863)
* Fix a bug adding unrelated candidates to the plugin loader redirect_list * Add tests for the redirect list * test redirect list for builtin module * test redirect list for redirected builtin module * test redirect list for collection module * test redirect list for redirected collection module * test redirect list for legacy module * changelog
This commit is contained in:
parent
3e63eb8418
commit
48c0fbd1cb
6 changed files with 118 additions and 6 deletions
2
changelogs/fragments/73863-fix-plugin-redirect-list.yaml
Normal file
2
changelogs/fragments/73863-fix-plugin-redirect-list.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
bugfixes:
|
||||||
|
- Fix adding unrelated candidate names to the plugin loader redirect list.
|
|
@ -476,6 +476,11 @@ class PluginLoader:
|
||||||
if redirect:
|
if redirect:
|
||||||
# FIXME: remove once this is covered in debug or whatever
|
# FIXME: remove once this is covered in debug or whatever
|
||||||
display.vv("redirecting (type: {0}) {1} to {2}".format(plugin_type, fq_name, redirect))
|
display.vv("redirecting (type: {0}) {1} to {2}".format(plugin_type, fq_name, redirect))
|
||||||
|
# The name doing the redirection is added at the beginning of _resolve_plugin_step,
|
||||||
|
# but if the unqualified name is used in conjunction with the collections keyword, only
|
||||||
|
# the unqualified name is in the redirect list.
|
||||||
|
if fq_name not in plugin_load_context.redirect_list:
|
||||||
|
plugin_load_context.redirect_list.append(fq_name)
|
||||||
return plugin_load_context.redirect(redirect)
|
return plugin_load_context.redirect(redirect)
|
||||||
# TODO: non-FQCN case, do we support `.` prefix for current collection, assume it with no dots, require it for subdirs in current, or ?
|
# TODO: non-FQCN case, do we support `.` prefix for current collection, assume it with no dots, require it for subdirs in current, or ?
|
||||||
|
|
||||||
|
@ -603,7 +608,12 @@ class PluginLoader:
|
||||||
# 'ansible.builtin' should be handled here. This means only internal, or builtin, paths are searched.
|
# 'ansible.builtin' should be handled here. This means only internal, or builtin, paths are searched.
|
||||||
plugin_load_context = self._find_fq_plugin(candidate_name, suffix, plugin_load_context=plugin_load_context)
|
plugin_load_context = self._find_fq_plugin(candidate_name, suffix, plugin_load_context=plugin_load_context)
|
||||||
|
|
||||||
if candidate_name != plugin_load_context.original_name and candidate_name not in plugin_load_context.redirect_list:
|
# Pending redirects are added to the redirect_list at the beginning of _resolve_plugin_step.
|
||||||
|
# Once redirects are resolved, ensure the final FQCN is added here.
|
||||||
|
# e.g. 'ns.coll.module' is included rather than only 'module' if a collections list is provided:
|
||||||
|
# - module:
|
||||||
|
# collections: ['ns.coll']
|
||||||
|
if plugin_load_context.resolved and candidate_name not in plugin_load_context.redirect_list:
|
||||||
plugin_load_context.redirect_list.append(candidate_name)
|
plugin_load_context.redirect_list.append(candidate_name)
|
||||||
|
|
||||||
if plugin_load_context.resolved or plugin_load_context.pending_redirect: # if we got an answer or need to chase down a redirect, return
|
if plugin_load_context.resolved or plugin_load_context.pending_redirect: # if we got an answer or need to chase down a redirect, return
|
||||||
|
|
|
@ -15,19 +15,26 @@ class ActionModule(ActionBase):
|
||||||
|
|
||||||
result = super(ActionModule, self).run(None, task_vars)
|
result = super(ActionModule, self).run(None, task_vars)
|
||||||
|
|
||||||
type = self._task.args.get('type')
|
plugin_type = self._task.args.get('type')
|
||||||
name = self._task.args.get('name')
|
name = self._task.args.get('name')
|
||||||
|
|
||||||
result = dict(changed=False, collection_list=self._task.collections)
|
result = dict(changed=False, collection_list=self._task.collections)
|
||||||
|
|
||||||
if all([type, name]):
|
if all([plugin_type, name]):
|
||||||
attr_name = '{0}_loader'.format(type)
|
attr_name = '{0}_loader'.format(plugin_type)
|
||||||
|
|
||||||
typed_loader = getattr(loader, attr_name, None)
|
typed_loader = getattr(loader, attr_name, None)
|
||||||
|
|
||||||
if not typed_loader:
|
if not typed_loader:
|
||||||
return (dict(failed=True, msg='invalid plugin type {0}'.format(type)))
|
return (dict(failed=True, msg='invalid plugin type {0}'.format(plugin_type)))
|
||||||
|
|
||||||
result['plugin_path'] = typed_loader.find_plugin(name, collection_list=self._task.collections)
|
context = typed_loader.find_plugin_with_context(name, collection_list=self._task.collections)
|
||||||
|
|
||||||
|
if not context.resolved:
|
||||||
|
result['plugin_path'] = None
|
||||||
|
result['redirect_list'] = []
|
||||||
|
else:
|
||||||
|
result['plugin_path'] = context.plugin_resolved_path
|
||||||
|
result['redirect_list'] = context.redirect_list
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
plugin_routing:
|
||||||
|
modules:
|
||||||
|
ping:
|
||||||
|
redirect: testns.testcoll.ping
|
|
@ -101,6 +101,9 @@ fi
|
||||||
# test collection inventories
|
# test collection inventories
|
||||||
ansible-playbook inventory_test.yml -i a.statichost.yml -i redirected.statichost.yml "$@"
|
ansible-playbook inventory_test.yml -i a.statichost.yml -i redirected.statichost.yml "$@"
|
||||||
|
|
||||||
|
# test plugin loader redirect_list
|
||||||
|
ansible-playbook test_redirect_list.yml -v "$@"
|
||||||
|
|
||||||
# test adjacent with --playbook-dir
|
# test adjacent with --playbook-dir
|
||||||
export ANSIBLE_COLLECTIONS_PATH=''
|
export ANSIBLE_COLLECTIONS_PATH=''
|
||||||
ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=1 ansible-inventory --list --export --playbook-dir=. -v "$@"
|
ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=1 ansible-inventory --list --export --playbook-dir=. -v "$@"
|
||||||
|
|
86
test/integration/targets/collections/test_redirect_list.yml
Normal file
86
test/integration/targets/collections/test_redirect_list.yml
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
---
|
||||||
|
- hosts: localhost
|
||||||
|
gather_facts: no
|
||||||
|
module_defaults:
|
||||||
|
testns.testcoll.plugin_lookup:
|
||||||
|
type: module
|
||||||
|
tasks:
|
||||||
|
- name: test builtin
|
||||||
|
testns.testcoll.plugin_lookup:
|
||||||
|
name: dnf
|
||||||
|
register: result
|
||||||
|
failed_when:
|
||||||
|
- result['redirect_list'] != ['dnf'] or result['plugin_path'].endswith('library/dnf.py')
|
||||||
|
|
||||||
|
- name: test builtin with collections kw
|
||||||
|
testns.testcoll.plugin_lookup:
|
||||||
|
name: dnf
|
||||||
|
register: result
|
||||||
|
failed_when:
|
||||||
|
- result['redirect_list'] != ['dnf'] or result['plugin_path'].endswith('library/dnf.py')
|
||||||
|
collections:
|
||||||
|
- testns.unrelatedcoll
|
||||||
|
|
||||||
|
- name: test redirected builtin
|
||||||
|
testns.testcoll.plugin_lookup:
|
||||||
|
name: formerly_core_ping
|
||||||
|
register: result
|
||||||
|
failed_when: result['redirect_list'] != expected_redirect_list
|
||||||
|
vars:
|
||||||
|
expected_redirect_list:
|
||||||
|
- formerly_core_ping
|
||||||
|
- ansible.builtin.formerly_core_ping
|
||||||
|
- testns.testcoll.ping
|
||||||
|
|
||||||
|
- name: test redirected builtin with collections kw
|
||||||
|
testns.testcoll.plugin_lookup:
|
||||||
|
name: formerly_core_ping
|
||||||
|
register: result
|
||||||
|
failed_when: result['redirect_list'] != expected_redirect_list
|
||||||
|
vars:
|
||||||
|
expected_redirect_list:
|
||||||
|
- formerly_core_ping
|
||||||
|
- ansible.builtin.formerly_core_ping
|
||||||
|
- testns.testcoll.ping
|
||||||
|
collections:
|
||||||
|
- testns.unrelatedcoll
|
||||||
|
- testns.testcoll
|
||||||
|
|
||||||
|
- name: test collection module with collections kw
|
||||||
|
testns.testcoll.plugin_lookup:
|
||||||
|
name: ping
|
||||||
|
register: result
|
||||||
|
failed_when: result['redirect_list'] != expected_redirect_list
|
||||||
|
vars:
|
||||||
|
expected_redirect_list:
|
||||||
|
- ping
|
||||||
|
- testns.testcoll.ping
|
||||||
|
collections:
|
||||||
|
- testns.unrelatedcoll
|
||||||
|
- testns.testcoll
|
||||||
|
|
||||||
|
- name: test redirected collection module with collections kw
|
||||||
|
testns.testcoll.plugin_lookup:
|
||||||
|
name: ping
|
||||||
|
register: result
|
||||||
|
failed_when: result['redirect_list'] != expected_redirect_list
|
||||||
|
vars:
|
||||||
|
expected_redirect_list:
|
||||||
|
- ping
|
||||||
|
- testns.testredirect.ping
|
||||||
|
- testns.testcoll.ping
|
||||||
|
collections:
|
||||||
|
- testns.unrelatedcoll
|
||||||
|
- testns.testredirect
|
||||||
|
|
||||||
|
- name: test legacy module with collections kw
|
||||||
|
testns.testcoll.plugin_lookup:
|
||||||
|
name: ping
|
||||||
|
register: result
|
||||||
|
failed_when:
|
||||||
|
- result['redirect_list'] != expected_redirect_list or not result['plugin_path'].endswith('library/ping.py')
|
||||||
|
vars:
|
||||||
|
expected_redirect_list:
|
||||||
|
- ping
|
||||||
|
collections:
|
||||||
|
- testns.unrelatedcoll
|
Loading…
Reference in a new issue