From 5207f0640583ac2b3ded125c82e88b89b3cf8dab Mon Sep 17 00:00:00 2001 From: Robert Cerven Date: Tue, 19 Nov 2024 21:00:56 +0100 Subject: [PATCH] pinning operator digest plugin now supports konflux images with oci.index * STONEBLD-2986 Signed-off-by: Robert Cerven --- atomic_reactor/plugins/pin_operator_digest.py | 12 +++- atomic_reactor/util.py | 12 ++++ tests/plugins/test_pin_operator_digests.py | 63 +++++++++++++++++-- 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/atomic_reactor/plugins/pin_operator_digest.py b/atomic_reactor/plugins/pin_operator_digest.py index 88d070e95..0a8714dfe 100644 --- a/atomic_reactor/plugins/pin_operator_digest.py +++ b/atomic_reactor/plugins/pin_operator_digest.py @@ -602,9 +602,17 @@ def pin_digest(self, image): if image.tag.startswith("sha256:"): self.log.debug("%s looks like a digest, skipping query", image.tag) return image - self.log.debug("Querying %s for manifest list digest", image.registry) + registry_client = self._get_registry_client(image.registry) - digest = registry_client.get_manifest_list_digest(image) + + self.log.debug("Querying %s for manifest list digest", image.registry) + try: + digest = registry_client.get_manifest_list_digest(image) + except RuntimeError: + self.log.debug("manifest list not found trying image index") + self.log.debug("Querying %s for manifest index digest", image.registry) + digest = registry_client.get_manifest_index_digest(image) + return self._replace(image, tag=digest) def replace_registry(self, image): diff --git a/atomic_reactor/util.py b/atomic_reactor/util.py index 4c4e0eb7a..791864173 100644 --- a/atomic_reactor/util.py +++ b/atomic_reactor/util.py @@ -785,6 +785,18 @@ def get_manifest_list_digest(self, image): digest_dict = get_checksums(io.BytesIO(response.content), ['sha256']) return 'sha256:{}'.format(digest_dict['sha256sum']) + def get_manifest_index_digest(self, image): + """Return manifest index digest for image + + :param image: + :return: + """ + response = self.get_manifest_index(image) + if response is None: + raise RuntimeError('Unable to fetch oci.index for {}'.format(image.to_str())) + digest_dict = get_checksums(io.BytesIO(response.content), ['sha256']) + return 'sha256:{}'.format(digest_dict['sha256sum']) + def get_all_manifests( self, image: ImageName, versions: Sequence[str] = ('v1', 'v2', 'v2_list', 'oci_index') ) -> Dict[str, requests.Response]: diff --git a/tests/plugins/test_pin_operator_digests.py b/tests/plugins/test_pin_operator_digests.py index 945dcc4af..908e818a1 100644 --- a/tests/plugins/test_pin_operator_digests.py +++ b/tests/plugins/test_pin_operator_digests.py @@ -209,7 +209,7 @@ def mock_package_mapping_files(repo_replacements): return repo_replacements -def mock_digest_query(image_digest_map): +def mock_digest_query(image_digest_map, manifest_list_raises=False, manifest_index_raises=False): updated_map = { ImageName.parse(pullspec).to_str(): digest @@ -219,9 +219,23 @@ def mock_digest_query(image_digest_map): def mocked_get_manifest_list_digest(image): return updated_map[image.to_str()] - (flexmock(atomic_reactor.util.RegistryClient) - .should_receive('get_manifest_list_digest') - .replace_with(mocked_get_manifest_list_digest)) + if manifest_list_raises: + (flexmock(atomic_reactor.util.RegistryClient) + .should_receive('get_manifest_list_digest') + .and_raise(RuntimeError, "Unable to fetch v2.manifest_list for")) + else: + (flexmock(atomic_reactor.util.RegistryClient) + .should_receive('get_manifest_list_digest') + .replace_with(mocked_get_manifest_list_digest)) + + if manifest_index_raises: + (flexmock(atomic_reactor.util.RegistryClient) + .should_receive('get_manifest_index_digest') + .and_raise(RuntimeError, "Unable to fetch oci.index for {}")) + else: + (flexmock(atomic_reactor.util.RegistryClient) + .should_receive('get_manifest_index_digest') + .replace_with(mocked_get_manifest_list_digest)) def mock_inspect_query(pullspec, labels, times=1): @@ -424,9 +438,46 @@ def test_raise_error_if_csv_has_both_related_images_and_related_env_vars( ) assert expected in str(exc_info.value) + @responses.activate + def test_raise_when_manifest_list_and_index_not_found(self, workflow, repo_dir): + pullspecs = [ + 'old-registry/ns/spam@sha256:4', # -> new-registry/new-ns/new-spam@sha256:4 + 'old-registry/ns/spam:1', # -> new-registry/new-ns/new-spam@sha256:4 + ] + replacement_pullspecs = { + 'old-registry/ns/spam@sha256:4': 'new-registry/new-ns/new-spam@sha256:4', + 'old-registry/ns/spam:1': 'new-registry/new-ns/new-spam@sha256:4', + } + + mock_digest_query({ + 'old-registry/ns/spam:1': 'sha256:4', + }, manifest_list_raises=True, manifest_index_raises=True) + + manifests_dir = repo_dir.joinpath(OPERATOR_MANIFESTS_DIR) + manifests_dir.mkdir() + mock_operator_csv(manifests_dir, 'csv.yaml', pullspecs) + + user_config = get_user_config(OPERATOR_MANIFESTS_DIR) + site_config = get_site_config() + + pull_registries = {'pull_registries': [ + {'url': 'https://old-registry'}, + ]} + + runner = mock_env(workflow, repo_dir, site_config=site_config, + add_to_config=pull_registries, user_config=user_config, + replacement_pullspecs=replacement_pullspecs) + + with pytest.raises(PluginFailedException) as exc_info: + runner.run() + + expected_error = "Unable to fetch oci.index for" + assert expected_error in str(exc_info.value) + @pytest.mark.parametrize('ocp_44', [True, False]) + @pytest.mark.parametrize('manifest_list_raises', [True, False]) @responses.activate - def test_pin_operator_digest(self, ocp_44, workflow, repo_dir, caplog): + def test_pin_operator_digest(self, ocp_44, manifest_list_raises, workflow, repo_dir, caplog): pullspecs = [ # registry.private.example.com: do not replace registry or repos 'registry.private.example.com/ns/foo@sha256:1', # -> no change @@ -485,7 +536,7 @@ def test_pin_operator_digest(self, ocp_44, workflow, repo_dir, caplog): 'weird-registry/ns/bar:1': 'sha256:2', 'private-registry/ns/baz:1': 'sha256:3', 'old-registry/ns/spam:1': 'sha256:4', - }) + }, manifest_list_raises=manifest_list_raises) # there should be no queries for the pullspecs which already contain a digest # images should be inspected after their digests are pinned