Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve messages when module/action loading fails after a redirect #73603

Closed
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/73603-redirect-names.yml
@@ -0,0 +1,2 @@
bugfixes:
- "core - improve error message when loading an action/module or lookup is redirected, but the result not found (https://github.com/ansible/ansible/pull/73603)."
15 changes: 11 additions & 4 deletions lib/ansible/cli/doc.py
Expand Up @@ -69,7 +69,9 @@ def add_collection_plugins(plugin_list, plugin_type, coll_filter=None):


class PluginNotFound(Exception):
pass
def __init__(self, message):
super(PluginNotFound, self).__init__(message)
self.message = message


class RoleMixin(object):
Expand Down Expand Up @@ -557,8 +559,8 @@ def _get_plugins_docs(self, plugin_type, loader):
for plugin in context.CLIARGS['args']:
try:
doc, plainexamples, returndocs, metadata = DocCLI._get_plugin_doc(plugin, plugin_type, loader, search_paths)
except PluginNotFound:
display.warning("%s %s not found in:\n%s\n" % (plugin_type, plugin, search_paths))
except PluginNotFound as e:
display.warning(e.message)
continue
except Exception as e:
display.vvv(traceback.format_exc())
Expand Down Expand Up @@ -729,7 +731,12 @@ def _get_plugin_doc(plugin, plugin_type, loader, search_paths):
# if the plugin lives in a non-python file (eg, win_X.ps1), require the corresponding python file for docs
result = loader.find_plugin_with_context(plugin, mod_type='.py', ignore_deprecated=True, check_aliases=True)
if not result.resolved:
raise PluginNotFound('%s was not found in %s' % (plugin, search_paths))
if result.redirect_list and len(result.redirect_list) > 1:
# take the last one in the redirect list, we may have successfully jumped through N other redirects
target_module_name = result.redirect_list[-1]
felixfontein marked this conversation as resolved.
Show resolved Hide resolved
raise PluginNotFound('%s %s was redirected to %s, which was not found in: %s' % (
plugin_type, plugin, target_module_name, search_paths))
raise PluginNotFound('%s %s was not found in: %s' % (plugin_type, plugin, search_paths))
plugin_name = result.plugin_resolved_name
filename = result.plugin_resolved_path
collection_name = result.plugin_resolved_collection
Expand Down
10 changes: 10 additions & 0 deletions lib/ansible/parsing/mod_args.py
Expand Up @@ -298,6 +298,7 @@ def parse(self, skip_action_validation=False):
non_task_ds = dict((k, v) for k, v in iteritems(self._task_ds) if (k not in self._task_attrs) and (not k.startswith('with_')))

# walk the filtered input dictionary to see if we recognize a module name
bad_attempts = []
for item, value in iteritems(non_task_ds):
is_action_candidate = False
if item in BUILTIN_TASKS:
Expand All @@ -308,9 +309,13 @@ def parse(self, skip_action_validation=False):
# If the plugin is resolved and redirected smuggle the list of candidate names via the task attribute 'internal_redirect_list'
context = action_loader.find_plugin_with_context(item, collection_list=self._collection_list)
if not context.resolved:
if context.redirect_list and len(context.redirect_list) > 1:
bad_attempts.append((item, context.redirect_list[-1]))
context = module_loader.find_plugin_with_context(item, collection_list=self._collection_list)
if context.resolved and context.redirect_list:
self.internal_redirect_list = context.redirect_list
if not context.resolved and context.redirect_list and len(context.redirect_list) > 1:
bad_attempts.append((item, context.redirect_list[-1]))
elif context.redirect_list:
self.internal_redirect_list = context.redirect_list

Expand All @@ -327,6 +332,11 @@ def parse(self, skip_action_validation=False):
# if we didn't see any module in the task at all, it's not a task really
if action is None:
if non_task_ds: # there was one non-task action, but we couldn't find it
if bad_attempts:
bad_action, redirect = bad_attempts[0]
raise AnsibleParserError("module/action '{0}' was redirected to '{1}', which could "
"not be loaded.".format(bad_action, redirect),
obj=self._task_ds)
bad_action = list(non_task_ds.keys())[0]
raise AnsibleParserError("couldn't resolve module/action '{0}'. This often indicates a "
"misspelling, missing collection, or incorrect module path.".format(bad_action),
Expand Down
3 changes: 3 additions & 0 deletions lib/ansible/template/__init__.py
Expand Up @@ -976,6 +976,9 @@ def _lookup(self, name, *args, **kwargs):
instance = lookup_loader.get(name, loader=self._loader, templar=self)

if instance is None:
context = lookup_loader.find_plugin_with_context(name)
if not context.resolved and context.redirect_list and len(context.redirect_list) > 1:
raise AnsibleError("lookup plugin '%s' was redirected to '%s', which could not be loaded" % (name, context.redirect_list[0]))
raise AnsibleError("lookup plugin (%s) not found" % name)

wantlist = kwargs.pop('wantlist', False)
Expand Down
2 changes: 1 addition & 1 deletion test/integration/targets/ansible-doc/test.yml
Expand Up @@ -60,7 +60,7 @@
register: result
- assert:
that:
- '"[WARNING]: module test_does_not_exist not found in:" in result.stderr'
- '"[WARNING]: module test_does_not_exist was not found in:" in result.stderr'

- name: documented module
command: ansible-doc test_docs
Expand Down