Skip to content

Commit

Permalink
Re-init plugin loader after collections installation (#411) (#412)
Browse files Browse the repository at this point in the history
* Re-init plugin loader after collections installation (#411)

* Add plugin loader test (#411)

* chore: auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Ирина Розет <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sorin Sbarnea <[email protected]>
  • Loading branch information
4 people authored Oct 17, 2024
1 parent 18c671e commit e247f3f
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
env:
FORCE_COLOR: 1
PYTEST_REQPASS: 108
PYTEST_REQPASS: 109
steps:
- uses: actions/checkout@v4
with:
Expand Down
18 changes: 13 additions & 5 deletions src/ansible_compat/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ class Runtime:
initialized: bool = False
plugins: Plugins
_has_playbook_cache: dict[tuple[str, Path | None], bool] = {}
require_module: bool = False

def __init__(
self,
Expand Down Expand Up @@ -218,6 +219,7 @@ def __init__(
msg = f"Found incompatible version of ansible runtime {self.version}, instead of {min_required_version} or newer."
raise RuntimeError(msg)
if require_module:
self.require_module = True
self._ensure_module_available()

# pylint: disable=import-outside-toplevel
Expand Down Expand Up @@ -339,17 +341,20 @@ def _ensure_module_available(self) -> None:
# https://github.com/ansible/ansible-lint/issues/2945
if not Runtime.initialized:
col_path = [f"{self.cache_dir}/collections"]
# noinspection PyProtectedMember
from ansible.utils.collection_loader._collection_finder import ( # pylint: disable=import-outside-toplevel
_AnsibleCollectionFinder,
)

if self.version >= Version("2.15.0.dev0"):
# pylint: disable=import-outside-toplevel,no-name-in-module
from ansible.plugins.loader import init_plugin_loader

_AnsibleCollectionFinder( # noqa: SLF001
paths=col_path,
)._remove() # pylint: disable=protected-access
init_plugin_loader(col_path)
else:
# noinspection PyProtectedMember
from ansible.utils.collection_loader._collection_finder import ( # pylint: disable=import-outside-toplevel
_AnsibleCollectionFinder,
)

# noinspection PyProtectedMember
# pylint: disable=protected-access
col_path += self.config.collections_paths
Expand Down Expand Up @@ -622,6 +627,9 @@ def install_requirements( # noqa: C901
if result.returncode != 0:
_logger.error(result.stderr)
raise AnsibleCommandError(result)
if self.require_module:
Runtime.initialized = False
self._ensure_module_available()

# pylint: disable=too-many-locals
def prepare_environment( # noqa: C901
Expand Down
38 changes: 38 additions & 0 deletions test/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from typing import TYPE_CHECKING, Any

import pytest
from ansible.plugins.loader import module_loader
from packaging.version import Version

from ansible_compat.constants import INVALID_PREREQUISITES_RC
Expand Down Expand Up @@ -742,6 +743,43 @@ def test_install_collection_from_disk(
runtime.clean()


@pytest.mark.parametrize(
("path", "expected_plugins"),
(
pytest.param(
"test/collections/acme.goodies",
[
"ansible.posix.patch", # from tests/requirements.yml
"community.crypto.acme_account", # from galaxy.yml as a git dependency
],
id="modules",
),
),
)
def test_load_plugins(
path: str,
expected_plugins: list[str],
) -> None:
"""Tests ability to load plugin from a collection installed by requirement."""
with cwd(Path(path)):
from ansible_compat.prerun import get_cache_dir

rmtree(get_cache_dir(Path.cwd()), ignore_errors=True)
runtime = Runtime(isolated=True, require_module=True)
runtime.prepare_environment(install_local=True)
for plugin_name in expected_plugins:
loaded_module = module_loader.find_plugin_with_context(
plugin_name,
ignore_deprecated=True,
check_aliases=True,
)
assert (
loaded_module.resolved_fqcn is not None
), f"Unable to load module {plugin_name}"

runtime.clean()


def test_install_collection_from_disk_fail() -> None:
"""Tests that we fail to install a broken collection."""
with cwd(Path("test/collections/acme.broken")):
Expand Down

0 comments on commit e247f3f

Please sign in to comment.