summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSloane Hertel <19572925+s-hertel@users.noreply.github.com>2023-04-27 16:11:17 -0400
committerGitHub <noreply@github.com>2023-04-27 16:11:17 -0400
commitd5e2e7a0a8ca9017a091922648430374539f878b (patch)
tree185ceb28e93712da10f12c9a7913d92bb5e6a976
parent71f6e10dae7c862f1e7f02063d4def18f0d44e44 (diff)
downloadansible-d5e2e7a0a8ca9017a091922648430374539f878b.tar.gz
[ansible-galaxy] Fix installing signed collections (#80661)
* Fix installing signed collections by using the fqcn, version, source, and type as a unique identifier. Define __hash__ and __eq__ methods to handle Candidate/Requirement comparison excluding signatures which aren't fully populated until install time. * Remove PinnedCandidateRequests since it is redundant now. * Fix verifying against a signed remote when the keyring isn't configured
-rw-r--r--changelogs/fragments/80648-fix-ansible-galaxy-cache-signatures-bug.yml3
-rw-r--r--lib/ansible/galaxy/collection/__init__.py2
-rw-r--r--lib/ansible/galaxy/dependency_resolution/dataclasses.py7
-rw-r--r--lib/ansible/galaxy/dependency_resolution/providers.py32
4 files changed, 12 insertions, 32 deletions
diff --git a/changelogs/fragments/80648-fix-ansible-galaxy-cache-signatures-bug.yml b/changelogs/fragments/80648-fix-ansible-galaxy-cache-signatures-bug.yml
new file mode 100644
index 0000000000..eda4eb62f9
--- /dev/null
+++ b/changelogs/fragments/80648-fix-ansible-galaxy-cache-signatures-bug.yml
@@ -0,0 +1,3 @@
+bugfixes:
+ - ansible-galaxy - fix installing signed collections (https://github.com/ansible/ansible/issues/80648).
+ - ansible-galaxy collection verify - fix verifying signed collections when the keyring is not configured.
diff --git a/lib/ansible/galaxy/collection/__init__.py b/lib/ansible/galaxy/collection/__init__.py
index 07a81eee32..feea9da2e2 100644
--- a/lib/ansible/galaxy/collection/__init__.py
+++ b/lib/ansible/galaxy/collection/__init__.py
@@ -916,7 +916,7 @@ def verify_collections(
# NOTE: If there are no Galaxy server signatures, only user-provided signature URLs,
# NOTE: those alone validate the MANIFEST.json and the remote collection is not downloaded.
# NOTE: The remote MANIFEST.json is only used in verification if there are no signatures.
- if not signatures and not collection.signature_sources:
+ if artifacts_manager.keyring is None or not signatures:
api_proxy.get_collection_version_metadata(
remote_collection,
)
diff --git a/lib/ansible/galaxy/dependency_resolution/dataclasses.py b/lib/ansible/galaxy/dependency_resolution/dataclasses.py
index 0a3f8429a4..46b4e2fffc 100644
--- a/lib/ansible/galaxy/dependency_resolution/dataclasses.py
+++ b/lib/ansible/galaxy/dependency_resolution/dataclasses.py
@@ -167,6 +167,7 @@ def _is_concrete_artifact_pointer(tested_str):
class _ComputedReqKindsMixin:
+ UNIQUE_ATTRS = ('fqcn', 'ver', 'src', 'type')
def __init__(self, *args, **kwargs):
if not self.may_have_offline_galaxy_info:
@@ -181,6 +182,12 @@ class _ComputedReqKindsMixin:
self.ver
)
+ def __hash__(self):
+ return hash(tuple(getattr(self, attr) for attr in _ComputedReqKindsMixin.UNIQUE_ATTRS))
+
+ def __eq__(self, candidate):
+ return hash(self) == hash(candidate)
+
@classmethod
def from_dir_path_as_unknown( # type: ignore[misc]
cls, # type: t.Type[Collection]
diff --git a/lib/ansible/galaxy/dependency_resolution/providers.py b/lib/ansible/galaxy/dependency_resolution/providers.py
index 93f0e7a00b..19fb2acae5 100644
--- a/lib/ansible/galaxy/dependency_resolution/providers.py
+++ b/lib/ansible/galaxy/dependency_resolution/providers.py
@@ -28,8 +28,6 @@ from ansible.galaxy.dependency_resolution.versioning import (
from ansible.module_utils.six import string_types
from ansible.utils.version import SemanticVersion, LooseVersion
-from collections.abc import Set
-
try:
from resolvelib import AbstractProvider
from resolvelib import __version__ as resolvelib_version
@@ -46,34 +44,6 @@ RESOLVELIB_UPPERBOUND = SemanticVersion("1.1.0")
RESOLVELIB_VERSION = SemanticVersion.from_loose_version(LooseVersion(resolvelib_version))
-class PinnedCandidateRequests(Set):
- """Custom set class to store Candidate objects. Excludes the 'signatures' attribute when determining if a Candidate instance is in the set."""
- CANDIDATE_ATTRS = ('fqcn', 'ver', 'src', 'type')
-
- def __init__(self, candidates):
- self._candidates = set(candidates)
-
- def __iter__(self):
- return iter(self._candidates)
-
- def __contains__(self, value):
- if not isinstance(value, Candidate):
- raise ValueError(f"Expected a Candidate object but got {value!r}")
- for candidate in self._candidates:
- # Compare Candidate attributes excluding "signatures" since it is
- # unrelated to whether or not a matching Candidate is user-requested.
- # Candidate objects in the set are not expected to have signatures.
- for attr in PinnedCandidateRequests.CANDIDATE_ATTRS:
- if getattr(value, attr) != getattr(candidate, attr):
- break
- else:
- return True
- return False
-
- def __len__(self):
- return len(self._candidates)
-
-
class CollectionDependencyProviderBase(AbstractProvider):
"""Delegate providing a requirement interface for the resolver."""
@@ -117,7 +87,7 @@ class CollectionDependencyProviderBase(AbstractProvider):
Requirement.from_requirement_dict,
art_mgr=concrete_artifacts_manager,
)
- self._pinned_candidate_requests = PinnedCandidateRequests(
+ self._pinned_candidate_requests = set(
# NOTE: User-provided signatures are supplemental, so signatures
# NOTE: are not used to determine if a candidate is user-requested
Candidate(req.fqcn, req.ver, req.src, req.type, None)