Skip to content

Commit

Permalink
Improve module loading logic
Browse files Browse the repository at this point in the history
- move module loading code in utils module
- mimic core behavior and fallback on legacy modules

Fixes: #3696
  • Loading branch information
ssbarnea committed Nov 27, 2023
1 parent fa9d1c7 commit e3c42bb
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 16 deletions.
11 changes: 2 additions & 9 deletions src/ansiblelint/rules/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import logging
import re
import sys
from functools import lru_cache
from typing import TYPE_CHECKING, Any

# pylint: disable=preferred-module
Expand All @@ -18,11 +17,11 @@
# pylint: disable=reimported
import ansible.module_utils.basic as mock_ansible_module
from ansible.module_utils import basic
from ansible.plugins.loader import PluginLoadContext, module_loader

from ansiblelint.constants import LINE_NUMBER_KEY
from ansiblelint.rules import AnsibleLintRule, RulesCollection
from ansiblelint.text import has_jinja
from ansiblelint.utils import load_plugin
from ansiblelint.yaml_utils import clean_json

if TYPE_CHECKING:
Expand Down Expand Up @@ -66,12 +65,6 @@
}


@lru_cache
def load_module(module_name: str) -> PluginLoadContext:
"""Load plugin from module name and cache it."""
return module_loader.find_plugin_with_context(module_name)


class ValidationPassedError(Exception):
"""Exception to be raised when validation passes."""

Expand Down Expand Up @@ -111,7 +104,7 @@ def matchtask(
if module_name in self.module_aliases:
return []

loaded_module = load_module(module_name)
loaded_module = load_plugin(module_name)

# https://github.com/ansible/ansible-lint/issues/3200
# since "ps1" modules cannot be executed on POSIX platforms, we will
Expand Down
18 changes: 13 additions & 5 deletions src/ansiblelint/rules/fqcn.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
import sys
from typing import TYPE_CHECKING, Any

from ansible.plugins.loader import module_loader

from ansiblelint.constants import LINE_NUMBER_KEY
from ansiblelint.rules import AnsibleLintRule, TransformMixin
from ansiblelint.utils import load_plugin

if TYPE_CHECKING:
from ruamel.yaml.comments import CommentedMap, CommentedSeq
Expand Down Expand Up @@ -116,9 +115,12 @@ def matchtask(
) -> list[MatchError]:
result = []
module = task["action"]["__ansible_module_original__"]
if not isinstance(module, str):
msg = "Invalid data for module."
raise RuntimeError(msg)

if module not in self.module_aliases:
loaded_module = module_loader.find_plugin_with_context(module)
loaded_module = load_plugin(module)
target = loaded_module.resolved_fqcn
self.module_aliases[module] = target
if target is None:
Expand All @@ -137,10 +139,16 @@ def matchtask(
1,
)
if module != legacy_module:
if module == "ansible.builtin.include":
message = f"Avoid deprecated module ({module})"
details = "Use `ansible.builtin.include_task` or `ansible.builtin.import_tasks` instead."
else:
message = f"Use FQCN for builtin module actions ({module})."
details = f"Use `{module_alias}` or `{legacy_module}` instead."
result.append(
self.create_matcherror(
message=f"Use FQCN for builtin module actions ({module}).",
details=f"Use `{module_alias}` or `{legacy_module}` instead.",
message=message,
details=details,
filename=file,
lineno=task["__line__"],
tag="fqcn[action-core]",
Expand Down
22 changes: 20 additions & 2 deletions src/ansiblelint/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import re
from collections.abc import Generator, ItemsView, Iterator, Mapping, Sequence
from dataclasses import _MISSING_TYPE, dataclass, field
from functools import cache
from functools import cache, lru_cache
from pathlib import Path
from typing import Any

Expand All @@ -44,7 +44,7 @@
from ansible.parsing.yaml.constructor import AnsibleConstructor, AnsibleMapping
from ansible.parsing.yaml.loader import AnsibleLoader
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject, AnsibleSequence
from ansible.plugins.loader import add_all_plugin_dirs
from ansible.plugins.loader import PluginLoadContext, add_all_plugin_dirs, module_loader
from ansible.template import Templar
from ansible.utils.collection_loader import AnsibleCollectionConfig
from yaml.composer import Composer
Expand Down Expand Up @@ -1056,3 +1056,21 @@ def parse_examples_from_plugin(lintable: Lintable) -> tuple[int, str]:
# Ignore the leading newline and lack of document start
# as including those in EXAMPLES would be weird.
return offset, (f"---{examples}" if examples else "")


@lru_cache
def load_plugin(name: str) -> PluginLoadContext:
"""Return loaded ansible plugin/module."""
loaded_module = module_loader.find_plugin_with_context(
name,
ignore_deprecated=True,
check_aliases=True,
)
if not loaded_module.resolved and name.startswith("ansible.builtin."):
# fallback to core behavior of using legacy
loaded_module = module_loader.find_plugin_with_context(
name.replace("ansible.builtin.", "ansible.legacy."),
ignore_deprecated=True,
check_aliases=True,
)
return loaded_module

0 comments on commit e3c42bb

Please sign in to comment.