diff --git a/changelogs/fragments/ansible-test-validate-modules-collection-loader.yml b/changelogs/fragments/ansible-test-validate-modules-collection-loader.yml new file mode 100644 index 00000000000..13f153a05eb --- /dev/null +++ b/changelogs/fragments/ansible-test-validate-modules-collection-loader.yml @@ -0,0 +1,2 @@ +bugfixes: + - ansible-test - Fix the ``validate-modules`` sanity test to avoid double-loading the collection loader and possibly failing on import of the ``packaging`` module. diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/meta/runtime.yml b/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/meta/runtime.yml new file mode 100644 index 00000000000..1602a2556f7 --- /dev/null +++ b/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/meta/runtime.yml @@ -0,0 +1 @@ +requires_ansible: '>=2.9' diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/plugins/module_utils/validate.psm1 b/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/plugins/module_utils/validate.psm1 new file mode 100644 index 00000000000..7072b311d3f --- /dev/null +++ b/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/plugins/module_utils/validate.psm1 @@ -0,0 +1,8 @@ +function Validate { + <# + .SYNOPSIS + validate + #> +} + +Export-ModuleMember -Function "Validate" diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/plugins/modules/validate.ps1 b/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/plugins/modules/validate.ps1 new file mode 100644 index 00000000000..a587af80575 --- /dev/null +++ b/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/plugins/modules/validate.ps1 @@ -0,0 +1,8 @@ +#!powershell +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ..module_utils.validate + +$module = [Ansible.Basic.AnsibleModule]::Create($args, @{}) +$module.ExitJson() diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/plugins/modules/validate.py b/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/plugins/modules/validate.py new file mode 100644 index 00000000000..ee1fb13840e --- /dev/null +++ b/test/integration/targets/ansible-test/ansible_collections/ns/ps_only/plugins/modules/validate.py @@ -0,0 +1,14 @@ +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +module: validate +short_description: validate +description: validate +author: "validate (@validate)" +''' + +EXAMPLES = r''' +''' + +RETURN = r''' +''' diff --git a/test/integration/targets/ansible-test/collection-tests/validate-modules-collection-loader.sh b/test/integration/targets/ansible-test/collection-tests/validate-modules-collection-loader.sh new file mode 100755 index 00000000000..3f77a6af989 --- /dev/null +++ b/test/integration/targets/ansible-test/collection-tests/validate-modules-collection-loader.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -eux -o pipefail + +cp -a "${TEST_DIR}/ansible_collections" "${WORK_DIR}" +cd "${WORK_DIR}/ansible_collections/ns/ps_only" + +if ! command -V pwsh; then + echo "skipping test since pwsh is not available" + exit 0 +fi + +# Use a PowerShell-only collection to verify that validate-modules does not load the collection loader multiple times. +ansible-test sanity --test validate-modules --color --truncate 0 "${@}" diff --git a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py index 22416be990f..067a5da64f5 100644 --- a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +++ b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py @@ -29,32 +29,57 @@ import subprocess import sys import tempfile import traceback +import warnings from collections import OrderedDict from contextlib import contextmanager -from ansible.module_utils.compat.version import StrictVersion, LooseVersion from fnmatch import fnmatch import yaml +from voluptuous.humanize import humanize_error + + +def setup_collection_loader(): + """ + Configure the collection loader if a collection is being tested. + This must be done before the plugin loader is imported. + """ + if '--collection' not in sys.argv: + return + + # noinspection PyProtectedMember + from ansible.utils.collection_loader._collection_finder import _AnsibleCollectionFinder + + collections_paths = os.environ.get('ANSIBLE_COLLECTIONS_PATH', '').split(os.pathsep) + collection_loader = _AnsibleCollectionFinder(collections_paths) + # noinspection PyProtectedMember + collection_loader._install() # pylint: disable=protected-access + + warnings.filterwarnings( + "ignore", + "AnsibleCollectionFinder has already been configured") + + +setup_collection_loader() + from ansible import __version__ as ansible_version from ansible.executor.module_common import REPLACER_WINDOWS, NEW_STYLE_PYTHON_MODULE_RE from ansible.module_utils.common._collections_compat import Mapping from ansible.module_utils.common.parameters import DEFAULT_TYPE_VALIDATORS +from ansible.module_utils.compat.version import StrictVersion, LooseVersion +from ansible.module_utils.basic import to_bytes +from ansible.module_utils.six import PY3, with_metaclass, string_types from ansible.plugins.loader import fragment_loader -from ansible.utils.collection_loader._collection_finder import _AnsibleCollectionFinder from ansible.utils.plugin_docs import REJECTLIST, add_collection_to_versions_and_dates, add_fragments, get_docstring from ansible.utils.version import SemanticVersion -from ansible.module_utils.basic import to_bytes from .module_args import AnsibleModuleImportError, AnsibleModuleNotInitialized, get_argument_spec from .schema import ansible_module_kwargs_schema, doc_schema, return_schema from .utils import CaptureStd, NoArgsAnsibleModule, compare_unordered_lists, is_empty, parse_yaml, parse_isodate -from voluptuous.humanize import humanize_error -from ansible.module_utils.six import PY3, with_metaclass, string_types if PY3: # Because there is no ast.TryExcept in Python 3 ast module @@ -2246,11 +2271,6 @@ class PythonPackageValidator(Validator): ) -def setup_collection_loader(): - collections_paths = os.environ.get('ANSIBLE_COLLECTIONS_PATH', '').split(os.pathsep) - _AnsibleCollectionFinder(collections_paths) - - def re_compile(value): """ Argparse expects things to raise TypeError, re.compile raises an re.error @@ -2303,7 +2323,6 @@ def run(): routing = None if args.collection: - setup_collection_loader() routing_file = 'meta/runtime.yml' # Load meta/runtime.yml if it exists, as it may contain deprecation information if os.path.isfile(routing_file):