[ansible-galaxy] Fix installing signed collections (#80661) (#80666)

* 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

(cherry picked from commit d5e2e7a0a8)
pull/80758/head
Sloane Hertel 2 years ago committed by GitHub
parent 4008335f41
commit 594f37dbb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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.

@ -914,7 +914,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,
)

@ -166,6 +166,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:
@ -180,6 +181,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]

@ -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("0.9.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)

Loading…
Cancel
Save