diff --git a/.gitignore b/.gitignore index 8e47259f379..cdd3941dee0 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ credentials.yml results.xml coverage.xml /test/units/cover-html +/test/integration/inventory /test/integration/targets/*/backup/ /test/cache/* # Development diff --git a/changelogs/fragments/fix_doc_symlinks.yml b/changelogs/fragments/fix_doc_symlinks.yml new file mode 100644 index 00000000000..8164be04bd9 --- /dev/null +++ b/changelogs/fragments/fix_doc_symlinks.yml @@ -0,0 +1,2 @@ +bugfixes: + - fix issue in which symlinked collection cannot be listed, though the docs/plugins can be loaded if referenced directly. diff --git a/lib/ansible/utils/collection_loader.py b/lib/ansible/utils/collection_loader.py index 7d452cf2259..560a5c60b10 100644 --- a/lib/ansible/utils/collection_loader.py +++ b/lib/ansible/utils/collection_loader.py @@ -550,9 +550,9 @@ def get_collection_name_from_path(path): :param n_path: native-string path to evaluate for collection containment :return: collection name or None """ - n_collection_paths = [to_native(os.path.realpath(to_bytes(p))) for p in AnsibleCollectionLoader().n_collection_paths] + n_collection_paths = [to_native(os.path.abspath(to_bytes(p))) for p in AnsibleCollectionLoader().n_collection_paths] - b_path = os.path.realpath(to_bytes(path)) + b_path = os.path.abspath(to_bytes(path)) n_path = to_native(b_path) for coll_path in n_collection_paths: @@ -575,7 +575,7 @@ def get_collection_name_from_path(path): return None # ensure we're using the canonical real path, with the bogus __synthetic__ stripped off - b_loaded_collection_path = os.path.dirname(os.path.realpath(to_bytes(loaded_collection_path))) + b_loaded_collection_path = os.path.dirname(os.path.abspath(to_bytes(loaded_collection_path))) # if the collection path prefix matches the path prefix we were passed, it's the same collection that's loaded if os.path.commonprefix([b_path, b_loaded_collection_path]) == b_loaded_collection_path: diff --git a/test/integration/targets/collections/runme.sh b/test/integration/targets/collections/runme.sh index 5aea771bdaf..98471b2761e 100755 --- a/test/integration/targets/collections/runme.sh +++ b/test/integration/targets/collections/runme.sh @@ -9,7 +9,7 @@ export ANSIBLE_HOST_PATTERN_MISMATCH=error # FUTURE: just use INVENTORY_PATH as-is once ansible-test sets the right dir -ipath=../../$(basename "${INVENTORY_PATH}") +ipath=../../$(basename "${INVENTORY_PATH:-../../inventory}") export INVENTORY_PATH="$ipath" # test callback @@ -17,6 +17,11 @@ ANSIBLE_CALLBACK_WHITELIST=testns.testcoll.usercallback ansible localhost -m pin # test documentation ansible-doc testns.testcoll.testmodule -vvv | grep -- "- normal_doc_frag" +# same with symlink +ln -s "${PWD}/testcoll2" ./collection_root_sys/ansible_collections/testns/testcoll2 +ansible-doc testns.testcoll2.testmodule2 -vvv | grep "Test module" +# now test we can list with symlink +ansible-doc -l -vvv| grep "testns.testcoll2.testmodule2" # test adhoc default collection resolution (use unqualified collection module with playbook dir under its collection) echo "testing adhoc default collection support with explicit playbook dir" diff --git a/test/integration/targets/collections/testcoll2/MANIFEST.json b/test/integration/targets/collections/testcoll2/MANIFEST.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/integration/targets/collections/testcoll2/plugins/modules/testmodule2.py b/test/integration/targets/collections/testcoll2/plugins/modules/testmodule2.py new file mode 100644 index 00000000000..7f6eb0247b1 --- /dev/null +++ b/test/integration/targets/collections/testcoll2/plugins/modules/testmodule2.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['stableinterface'], + 'supported_by': 'core'} + +DOCUMENTATION = ''' +--- +module: testmodule2 +short_description: Test module +description: + - Test module +author: + - Ansible Core Team +''' + +EXAMPLES = ''' +''' + +RETURN = ''' +''' + +import json + + +def main(): + print(json.dumps(dict(changed=False, source='sys'))) + + +if __name__ == '__main__': + main()