summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fontein <felix@fontein.de>2023-09-11 15:31:52 +0200
committerGitHub <noreply@github.com>2023-09-11 15:31:52 +0200
commitb1b029c6b57239e294f50fd0985e55bf492b068b (patch)
tree63be41d8916cd97b070c46c101a0144b10d1d3fd
parentOnly mark a role as complete once a task in it executes for the target host (... (diff)
downloadansible-b1b029c6b57239e294f50fd0985e55bf492b068b.tar.xz
ansible-b1b029c6b57239e294f50fd0985e55bf492b068b.zip
ansible-doc: allow to filter by more than one collection (#81450)
Make collection filters more flexible for listing collections. Co-authored-by: Maxwell G <maxwell@gtmx.me>
-rw-r--r--changelogs/fragments/81450-list-filters.yml4
-rwxr-xr-xlib/ansible/cli/doc.py15
-rw-r--r--lib/ansible/collections/list.py29
-rw-r--r--lib/ansible/galaxy/collection/__init__.py8
-rw-r--r--lib/ansible/plugins/list.py36
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/galaxy.yml6
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/plugins/modules/test1.py27
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/galaxy.yml6
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/plugins/modules/test2.py27
-rwxr-xr-xtest/integration/targets/ansible-doc/runme.sh13
10 files changed, 138 insertions, 33 deletions
diff --git a/changelogs/fragments/81450-list-filters.yml b/changelogs/fragments/81450-list-filters.yml
new file mode 100644
index 0000000000..f307f92c45
--- /dev/null
+++ b/changelogs/fragments/81450-list-filters.yml
@@ -0,0 +1,4 @@
+bugfixes:
+ - "ansible-console - fix filtering by collection names when a collection search path was set (https://github.com/ansible/ansible/pull/81450)."
+minor_changes:
+ - "ansible-doc - allow to filter listing of collections and metadata dump by more than one collection (https://github.com/ansible/ansible/pull/81450)."
diff --git a/lib/ansible/cli/doc.py b/lib/ansible/cli/doc.py
index 06c931272b..4a5c892816 100755
--- a/lib/ansible/cli/doc.py
+++ b/lib/ansible/cli/doc.py
@@ -163,8 +163,8 @@ class RoleMixin(object):
might be fully qualified with the collection name (e.g., community.general.roleA)
or not (e.g., roleA).
- :param collection_filter: A string containing the FQCN of a collection which will be
- used to limit results. This filter will take precedence over the name_filters.
+ :param collection_filter: A list of strings containing the FQCN of a collection which will
+ be used to limit results. This filter will take precedence over the name_filters.
:returns: A set of tuples consisting of: role name, collection name, collection path
"""
@@ -678,12 +678,11 @@ class DocCLI(CLI, RoleMixin):
def _get_collection_filter(self):
coll_filter = None
- if len(context.CLIARGS['args']) == 1:
- coll_filter = context.CLIARGS['args'][0]
- if not AnsibleCollectionRef.is_valid_collection_name(coll_filter):
- raise AnsibleError('Invalid collection name (must be of the form namespace.collection): {0}'.format(coll_filter))
- elif len(context.CLIARGS['args']) > 1:
- raise AnsibleOptionsError("Only a single collection filter is supported.")
+ if len(context.CLIARGS['args']) >= 1:
+ coll_filter = context.CLIARGS['args']
+ for coll_name in coll_filter:
+ if not AnsibleCollectionRef.is_valid_collection_name(coll_name):
+ raise AnsibleError('Invalid collection name (must be of the form namespace.collection): {0}'.format(coll_name))
return coll_filter
diff --git a/lib/ansible/collections/list.py b/lib/ansible/collections/list.py
index dd428e11f8..ef858ae999 100644
--- a/lib/ansible/collections/list.py
+++ b/lib/ansible/collections/list.py
@@ -32,16 +32,31 @@ def list_collection_dirs(search_paths=None, coll_filter=None, artifacts_manager=
namespace_filter = None
collection_filter = None
+ has_pure_namespace_filter = False # whether at least one coll_filter is a namespace-only filter
if coll_filter is not None:
- if '.' in coll_filter:
- try:
- namespace_filter, collection_filter = coll_filter.split('.')
- except ValueError:
- raise AnsibleError("Invalid collection pattern supplied: %s" % coll_filter)
- else:
- namespace_filter = coll_filter
+ if isinstance(coll_filter, str):
+ coll_filter = [coll_filter]
+ namespace_filter = set()
+ for coll_name in coll_filter:
+ if '.' in coll_name:
+ try:
+ namespace, collection = coll_name.split('.')
+ except ValueError:
+ raise AnsibleError("Invalid collection pattern supplied: %s" % coll_name)
+ namespace_filter.add(namespace)
+ if not has_pure_namespace_filter:
+ if collection_filter is None:
+ collection_filter = []
+ collection_filter.append(collection)
+ else:
+ namespace_filter.add(coll_name)
+ has_pure_namespace_filter = True
+ collection_filter = None
+ namespace_filter = sorted(namespace_filter)
for req in find_existing_collections(search_paths, artifacts_manager, namespace_filter=namespace_filter,
collection_filter=collection_filter, dedupe=dedupe):
+ if not has_pure_namespace_filter and coll_filter is not None and req.fqcn not in coll_filter:
+ continue
yield to_bytes(req.src)
diff --git a/lib/ansible/galaxy/collection/__init__.py b/lib/ansible/galaxy/collection/__init__.py
index cf72653875..88718e0fe5 100644
--- a/lib/ansible/galaxy/collection/__init__.py
+++ b/lib/ansible/galaxy/collection/__init__.py
@@ -1420,6 +1420,10 @@ def find_existing_collections(path_filter, artifacts_manager, namespace_filter=N
if path_filter and not is_sequence(path_filter):
path_filter = [path_filter]
+ if namespace_filter and not is_sequence(namespace_filter):
+ namespace_filter = [namespace_filter]
+ if collection_filter and not is_sequence(collection_filter):
+ collection_filter = [collection_filter]
paths = set()
for path in files('ansible_collections').glob('*/*/'):
@@ -1441,9 +1445,9 @@ def find_existing_collections(path_filter, artifacts_manager, namespace_filter=N
for path in paths:
namespace = path.parent.name
name = path.name
- if namespace_filter and namespace != namespace_filter:
+ if namespace_filter and namespace not in namespace_filter:
continue
- if collection_filter and name != collection_filter:
+ if collection_filter and name not in collection_filter:
continue
if dedupe:
diff --git a/lib/ansible/plugins/list.py b/lib/ansible/plugins/list.py
index bd05b1a003..cd4d51f583 100644
--- a/lib/ansible/plugins/list.py
+++ b/lib/ansible/plugins/list.py
@@ -171,28 +171,32 @@ def list_collection_plugins(ptype, collections, search_paths=None):
return plugins
-def list_plugins(ptype, collection=None, search_paths=None):
+def list_plugins(ptype, collections=None, search_paths=None):
+ if isinstance(collections, str):
+ collections = [collections]
# {plugin_name: (filepath, class), ...}
plugins = {}
- collections = {}
- if collection is None:
+ plugin_collections = {}
+ if collections is None:
# list all collections, add synthetic ones
- collections['ansible.builtin'] = b''
- collections['ansible.legacy'] = b''
- collections.update(list_collections(search_paths=search_paths, dedupe=True))
- elif collection == 'ansible.legacy':
- # add builtin, since legacy also resolves to these
- collections[collection] = b''
- collections['ansible.builtin'] = b''
+ plugin_collections['ansible.builtin'] = b''
+ plugin_collections['ansible.legacy'] = b''
+ plugin_collections.update(list_collections(search_paths=search_paths, dedupe=True))
else:
- try:
- collections[collection] = to_bytes(_get_collection_path(collection))
- except ValueError as e:
- raise AnsibleError("Cannot use supplied collection {0}: {1}".format(collection, to_native(e)), orig_exc=e)
+ for collection in collections:
+ if collection == 'ansible.legacy':
+ # add builtin, since legacy also resolves to these
+ plugin_collections[collection] = b''
+ plugin_collections['ansible.builtin'] = b''
+ else:
+ try:
+ plugin_collections[collection] = to_bytes(_get_collection_path(collection))
+ except ValueError as e:
+ raise AnsibleError("Cannot use supplied collection {0}: {1}".format(collection, to_native(e)), orig_exc=e)
- if collections:
- plugins.update(list_collection_plugins(ptype, collections))
+ if plugin_collections:
+ plugins.update(list_collection_plugins(ptype, plugin_collections))
return plugins
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/galaxy.yml b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/galaxy.yml
new file mode 100644
index 0000000000..bd6c15a449
--- /dev/null
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/galaxy.yml
@@ -0,0 +1,6 @@
+namespace: testns
+name: testcol3
+version: 0.1.0
+readme: README.md
+authors:
+ - me
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/plugins/modules/test1.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/plugins/modules/test1.py
new file mode 100644
index 0000000000..02dfb89d56
--- /dev/null
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/plugins/modules/test1.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: test1
+short_description: Foo module in testcol3
+description:
+ - This is a foo module.
+author:
+ - me
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(),
+ )
+
+ module.exit_json()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/galaxy.yml b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/galaxy.yml
new file mode 100644
index 0000000000..7894d393ca
--- /dev/null
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/galaxy.yml
@@ -0,0 +1,6 @@
+namespace: testns
+name: testcol4
+version: 1.0.0
+readme: README.md
+authors:
+ - me
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/plugins/modules/test2.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/plugins/modules/test2.py
new file mode 100644
index 0000000000..ddb0c114fa
--- /dev/null
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/plugins/modules/test2.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: test2
+short_description: Foo module in testcol4
+description:
+ - This is a foo module.
+author:
+ - me
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(),
+ )
+
+ module.exit_json()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/ansible-doc/runme.sh b/test/integration/targets/ansible-doc/runme.sh
index 14f746c60b..b525766cfa 100755
--- a/test/integration/targets/ansible-doc/runme.sh
+++ b/test/integration/targets/ansible-doc/runme.sh
@@ -60,6 +60,14 @@ ansible-doc --list testns.testcol --playbook-dir ./ 2>&1 | grep $GREP_OPTS -v "I
echo "ensure we dont break on invalid collection name for list"
ansible-doc --list testns.testcol.fakemodule --playbook-dir ./ 2>&1 | grep $GREP_OPTS "Invalid collection name"
+echo "filter list with more than one collection (1/2)"
+output=$(ansible-doc --list testns.testcol3 testns.testcol4 --playbook-dir ./ 2>&1 | wc -l)
+test "$output" -eq 2
+
+echo "filter list with more than one collection (2/2)"
+output=$(ansible-doc --list testns.testcol testns.testcol4 --playbook-dir ./ 2>&1 | wc -l)
+test "$output" -eq 5
+
echo "testing ansible-doc output for various plugin types"
for ptype in cache inventory lookup vars filter module
do
@@ -114,6 +122,11 @@ echo "testing multiple role entrypoints"
output=$(ansible-doc -t role -l --playbook-dir . testns.testcol | wc -l)
test "$output" -eq 2
+echo "test listing roles with multiple collection filters"
+# Two collection roles are defined, but only 1 has a role arg spec with 2 entry points
+output=$(ansible-doc -t role -l --playbook-dir . testns.testcol2 testns.testcol | wc -l)
+test "$output" -eq 2
+
echo "testing standalone roles"
# Include normal roles (no collection filter)
output=$(ansible-doc -t role -l --playbook-dir . | wc -l)