ansible/ansible-lint

Collection modules inside tasks cause exceptions

Closed this issue · 8 comments

Issue

ansible-lint uses ansible ModuleArgsParser logic to extract the action from a task, it finds core modules successfully but starting with ansible 2.8 for collections it expects the collection to be loaded from the environment via other pieces of ansible, and a module from a collection causes an exception (related, #372 reports exception on custom modules)

Background

Currently ansible-lint does not use other pieces of ansible to go outside the role it is linting and search for (and load) installed collections. This is probably best since there would be complexity and maintainability issues. Also if this were done, linting a role would yield different results depending on whether it was run inside an environment with installed collections, or standalone (at this point it may be violating the name “linter” by no longer being a static analysis tool)

Proposal

Keep using ModuleArgsParser, catch a “no action detected” exception and then apply custom logic to do a best-guess of the action - choose the first argument that does not match a known keyword

@willthames would you mind weighing in with your thoughts?

I would also find acceptable workaround to be able define list of modules to be ignored (whitelist), where no verification about their arguments would be performend. At this this would allow us to buy time.

@awcrosby that all seems very reasonable to me - I can't think of a better way to do it than what you suggest here

If you're actually calling ModuleArgsParser.parse(), that's exactly what the new skip_action_validation arg is for in 2.9- we use it internally for early parse passes when we won't necessarily have the collections paths fully configured yet.

https://github.com/ansible/ansible/blob/7d1a981b61eb996c64c406a92f73022d4d3a041b/lib/ansible/parsing/mod_args.py#L262

It more-or-less behaves the same way (along with fixing something that's bugged me for years- that we're banging on the PluginLoader for things we clearly know are task keywords). We now filter out everything we know are task keywords (or appear to be, like with_X and a couple other special cases that aren't represented declaratively in the play/block/task hierarchy), then assume what's left had better be a single action. skip_action_validation tells it not to consult the plugin loader to actually resolve the action.

With some testing using skip_action_validation and a my_ns.my_collection.my_module key, things works as expected. Raised #642

Note with this approach: with older keys that have been removed from ansible (i.e. always_run or sudo), ansible-lint throws an exception via ansible itself not handling them (AnsibleParserError: conflicting action statements). ansible-lint still “catches” the usage of these via a non-zero exit code… before they are removed ansible-lint can catch via a rule (i.e. SudoRule), and after they are removed ansible-lint throws an exception like ansible does.

I've just realized I can't lint my collections because of this—I wanted to run ansible-lint on the community Kubernetes collection (https://github.com/ansible-collections/kubernetes), but it fails to find modules in the collection itself, with the error message:

Couldn't parse task at /Users/jgeerling/Dropbox/VMs/ansible_collections/community/kubernetes/molecule/default/tasks/exec.yml:30 (couldn't resolve module/action 'k8s_exec'. This often indicates a misspelling, missing collection, or incorrect module path.

The error appears to be in '<unicode string>': line 30, column 7, but may
be elsewhere in the file depending on the exact syntax problem.

(could not open file to display line))
{ 'k8s_exec': { '__file__': '/Users/jgeerling/Dropbox/VMs/ansible_collections/community/kubernetes/molecule/default/tasks/exec.yml',
                '__line__': 32,
                'command': 'cat /etc/resolv.conf',
                'namespace': '{{ exec_namespace }}',
                'pod': '{{ pod }}'},
  'name': 'Execute a command',
  'register': 'output',
  'skipped_rules': []}

Steps to reproduce:

  1. git clone https://github.com/ansible-collections/kubernetes.git
  2. cd kubernetes
  3. ansible-lint

I'm sorry to report this still occurs in version 4.3.0. Otherwise, great work on the new version!

I've submitted #1046 based on the comments here. If people who have looked into can give some feedback, that would be great.