Fix support for pulling manifest lists via Docker V1 protocol where applicable
This commit is contained in:
parent
37b20010aa
commit
276d0d571d
6 changed files with 48 additions and 9 deletions
|
@ -12,6 +12,12 @@ class RegistryDataInterface(object):
|
||||||
""" Returns whether the implementation of the data interface supports schema 2 format
|
""" Returns whether the implementation of the data interface supports schema 2 format
|
||||||
manifests. """
|
manifests. """
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_legacy_tags_map(self, repository_ref, storage):
|
||||||
|
""" Returns a map from tag name to its legacy image ID, for all tags with legacy images in
|
||||||
|
the repository. Note that this can be a *very* heavy operation.
|
||||||
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def find_matching_tag(self, repository_ref, tag_names):
|
def find_matching_tag(self, repository_ref, tag_names):
|
||||||
""" Finds an alive tag in the repository matching one of the given tag names and returns it
|
""" Finds an alive tag in the repository matching one of the given tag names and returns it
|
||||||
|
|
|
@ -13,6 +13,7 @@ from data.registry_model.datatypes import (Tag, Manifest, LegacyImage, Label, Se
|
||||||
from data.registry_model.shared import SharedModel
|
from data.registry_model.shared import SharedModel
|
||||||
from data.registry_model.label_handlers import apply_label_to_manifest
|
from data.registry_model.label_handlers import apply_label_to_manifest
|
||||||
from image.docker import ManifestException
|
from image.docker import ManifestException
|
||||||
|
from image.docker.schema2 import DOCKER_SCHEMA2_MANIFESTLIST_CONTENT_TYPE
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -28,6 +29,31 @@ class OCIModel(SharedModel, RegistryDataInterface):
|
||||||
manifests. """
|
manifests. """
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_legacy_tags_map(self, repository_ref, storage):
|
||||||
|
""" Returns a map from tag name to its legacy image ID, for all tags with legacy images in
|
||||||
|
the repository. Note that this can be a *very* heavy operation.
|
||||||
|
"""
|
||||||
|
tags = oci.tag.list_alive_tags(repository_ref._db_id)
|
||||||
|
legacy_images_map = oci.tag.get_legacy_images_for_tags(tags)
|
||||||
|
|
||||||
|
tags_map = {}
|
||||||
|
for tag in tags:
|
||||||
|
legacy_image = legacy_images_map.get(tag.id)
|
||||||
|
if legacy_image is not None:
|
||||||
|
tags_map[tag.name] = legacy_image.docker_image_id
|
||||||
|
else:
|
||||||
|
manifest = Manifest.for_manifest(tag.manifest, None)
|
||||||
|
if legacy_image is None and manifest.media_type == DOCKER_SCHEMA2_MANIFESTLIST_CONTENT_TYPE:
|
||||||
|
# See if we can lookup a schema1 legacy image.
|
||||||
|
v1_compatible = self.get_schema1_parsed_manifest(manifest, '', '', '', storage)
|
||||||
|
if v1_compatible is not None:
|
||||||
|
v1_id = v1_compatible.leaf_layer_v1_image_id
|
||||||
|
if v1_id is not None:
|
||||||
|
tags_map[tag.name] = v1_id
|
||||||
|
|
||||||
|
|
||||||
|
return tags_map
|
||||||
|
|
||||||
def find_matching_tag(self, repository_ref, tag_names):
|
def find_matching_tag(self, repository_ref, tag_names):
|
||||||
""" Finds an alive tag in the repository matching one of the given tag names and returns it
|
""" Finds an alive tag in the repository matching one of the given tag names and returns it
|
||||||
or None if none.
|
or None if none.
|
||||||
|
|
|
@ -32,6 +32,13 @@ class PreOCIModel(SharedModel, RegistryDataInterface):
|
||||||
manifests. """
|
manifests. """
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_legacy_tags_map(self, repository_ref, storage):
|
||||||
|
""" Returns a map from tag name to its legacy image, for all tags with legacy images in
|
||||||
|
the repository.
|
||||||
|
"""
|
||||||
|
tags = self.list_repository_tags(repository_ref, include_legacy_images=True)
|
||||||
|
return {tag.name: tag.legacy_image.docker_image_id for tag in tags}
|
||||||
|
|
||||||
def find_matching_tag(self, repository_ref, tag_names):
|
def find_matching_tag(self, repository_ref, tag_names):
|
||||||
""" Finds an alive tag in the repository matching one of the given tag names and returns it
|
""" Finds an alive tag in the repository matching one of the given tag names and returns it
|
||||||
or None if none.
|
or None if none.
|
||||||
|
|
|
@ -231,6 +231,8 @@ def test_repository_tags(repo_namespace, repo_name, registry_model):
|
||||||
tags = registry_model.list_repository_tags(repository_ref, include_legacy_images=True)
|
tags = registry_model.list_repository_tags(repository_ref, include_legacy_images=True)
|
||||||
assert len(tags)
|
assert len(tags)
|
||||||
|
|
||||||
|
tags_map = registry_model.get_legacy_tags_map(repository_ref, storage)
|
||||||
|
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
found_tag = registry_model.get_repo_tag(repository_ref, tag.name, include_legacy_image=True)
|
found_tag = registry_model.get_repo_tag(repository_ref, tag.name, include_legacy_image=True)
|
||||||
assert found_tag == tag
|
assert found_tag == tag
|
||||||
|
@ -241,6 +243,8 @@ def test_repository_tags(repo_namespace, repo_name, registry_model):
|
||||||
found_image = registry_model.get_legacy_image(repository_ref,
|
found_image = registry_model.get_legacy_image(repository_ref,
|
||||||
found_tag.legacy_image.docker_image_id)
|
found_tag.legacy_image.docker_image_id)
|
||||||
assert found_image == found_tag.legacy_image
|
assert found_image == found_tag.legacy_image
|
||||||
|
assert tag.name in tags_map
|
||||||
|
assert tags_map[tag.name] == found_image.docker_image_id
|
||||||
|
|
||||||
|
|
||||||
def test_repository_tag_history(registry_model):
|
def test_repository_tag_history(registry_model):
|
||||||
|
|
|
@ -27,11 +27,7 @@ def get_tags(namespace_name, repo_name):
|
||||||
if repository_ref is None:
|
if repository_ref is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
# TODO(jschorr): Change this to normalize manifest lists back to their legacy image
|
tag_map = registry_model.get_legacy_tags_map(repository_ref, storage)
|
||||||
# (if applicable).
|
|
||||||
tags = registry_model.list_repository_tags(repository_ref, include_legacy_images=True)
|
|
||||||
tag_map = {tag.name: tag.legacy_image.docker_image_id
|
|
||||||
for tag in tags if tag.legacy_image_if_present}
|
|
||||||
return jsonify(tag_map)
|
return jsonify(tag_map)
|
||||||
|
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
|
@ -1368,7 +1368,7 @@ def test_push_tag_existing_image(v1_protocol, puller, basic_images, liveserver_s
|
||||||
True,
|
True,
|
||||||
False
|
False
|
||||||
])
|
])
|
||||||
def test_push_pull_manifest_list_back_compat(v22_protocol, v2_protocol, basic_images,
|
def test_push_pull_manifest_list_back_compat(v22_protocol, legacy_puller, basic_images,
|
||||||
different_images, liveserver_session, app_reloader,
|
different_images, liveserver_session, app_reloader,
|
||||||
schema_version, data_model, is_amd):
|
schema_version, data_model, is_amd):
|
||||||
""" Test: Push a new tag with a manifest list containing two manifests, one (possibly) legacy
|
""" Test: Push a new tag with a manifest list containing two manifests, one (possibly) legacy
|
||||||
|
@ -1405,9 +1405,9 @@ def test_push_pull_manifest_list_back_compat(v22_protocol, v2_protocol, basic_im
|
||||||
|
|
||||||
# Pull the tag and ensure we (don't) get back the basic images, since they are(n't) part of the
|
# Pull the tag and ensure we (don't) get back the basic images, since they are(n't) part of the
|
||||||
# amd64+linux manifest.
|
# amd64+linux manifest.
|
||||||
v2_protocol.pull(liveserver_session, 'devtable', 'newrepo', 'latest', basic_images,
|
legacy_puller.pull(liveserver_session, 'devtable', 'newrepo', 'latest', basic_images,
|
||||||
credentials=credentials,
|
credentials=credentials,
|
||||||
expected_failure=Failures.UNKNOWN_TAG if not is_amd else None)
|
expected_failure=Failures.UNKNOWN_TAG if not is_amd else None)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('schema_version', [
|
@pytest.mark.parametrize('schema_version', [
|
||||||
|
|
Reference in a new issue