diff --git a/endpoints/api/image.py b/endpoints/api/image.py index 522fdf951..f126ed89e 100644 --- a/endpoints/api/image.py +++ b/endpoints/api/image.py @@ -1,11 +1,36 @@ """ List and lookup repository images. """ +import json +from data.registry_model import registry_model from endpoints.api import (resource, nickname, require_repo_read, RepositoryParamResource, - path_param, disallow_for_app_repositories) -from endpoints.api.image_models_pre_oci import pre_oci_model as model + path_param, disallow_for_app_repositories, format_date) from endpoints.exception import NotFound +def _image_dict(image, with_history=False, with_tags=False): + image_data = { + 'id': image.docker_image_id, + 'created': format_date(image.created), + 'comment': image.comment, + 'command': json.loads(image.command) if image.command else None, + 'size': image.image_size, + 'uploading': image.uploading, + 'sort_index': len(image.parents), + } + + if with_tags: + image_data['tags'] = [tag.name for tag in image.tags] + + if with_history: + image_data['history'] = [_image_dict(parent, with_history, with_tags) + for parent in image.parents] + + # Calculate the ancestors string, with the DBID's replaced with the docker IDs. + parent_docker_ids = [parent_image.docker_image_id for parent_image in image.parents] + image_data['ancestors'] = '/{0}/'.format('/'.join(parent_docker_ids)) + return image_data + + @resource('/v1/repository//image/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryImageList(RepositoryParamResource): @@ -16,11 +41,12 @@ class RepositoryImageList(RepositoryParamResource): @disallow_for_app_repositories def get(self, namespace, repository): """ List the images for the specified repository. """ - images = model.get_repository_images(namespace, repository) - if images is None: + repo_ref = registry_model.lookup_repository(namespace, repository) + if repo_ref is None: raise NotFound() - return {'images': [image.to_dict() for image in images]} + images = registry_model.get_legacy_images(repo_ref) + return {'images': [_image_dict(image, with_tags=True) for image in images]} @resource('/v1/repository//image/') @@ -34,8 +60,12 @@ class RepositoryImage(RepositoryParamResource): @disallow_for_app_repositories def get(self, namespace, repository, image_id): """ Get the information available for the specified image. """ - image = model.get_repository_image(namespace, repository, image_id) + repo_ref = registry_model.lookup_repository(namespace, repository) + if repo_ref is None: + raise NotFound() + + image = registry_model.get_legacy_image(repo_ref, image_id, include_parents=True) if image is None: raise NotFound() - return image.to_dict() + return _image_dict(image, with_history=True) diff --git a/endpoints/api/image_models_interface.py b/endpoints/api/image_models_interface.py deleted file mode 100644 index 0cd5c0a8f..000000000 --- a/endpoints/api/image_models_interface.py +++ /dev/null @@ -1,77 +0,0 @@ -import json - -from endpoints.api import format_date - -from abc import ABCMeta, abstractmethod -from collections import namedtuple -from six import add_metaclass - -class Image(namedtuple('Image', ['docker_image_id', 'created', 'comment', 'command', 'image_size', - 'uploading', 'parents'])): - """ - Image represents an image. - :type name: string - """ - - def to_dict(self): - image_data = { - 'id': self.docker_image_id, - 'created': format_date(self.created), - 'comment': self.comment, - 'command': json.loads(self.command) if self.command else None, - 'size': self.image_size, - 'uploading': self.uploading, - 'sort_index': len(self.parents), - } - - # Calculate the ancestors string, with the DBID's replaced with the docker IDs. - parent_docker_ids = [parent_image.docker_image_id for parent_image in self.parents] - image_data['ancestors'] = '/{0}/'.format('/'.join(parent_docker_ids)) - - return image_data - -class ImageWithTags(namedtuple('ImageWithTags', ['image', 'tag_names'])): - """ - ImageWithTags represents an image, along with the tags that point to it. - :type image: Image - :type tag_names: list of string - """ - - def to_dict(self): - image_dict = self.image.to_dict() - image_dict['tags'] = self.tag_names - return image_dict - - -class ImageWithHistory(namedtuple('ImageWithHistory', ['image'])): - """ - ImageWithHistory represents an image, along with its full parent image dictionaries. - :type image: Image - :type history: list of Image parents (name is old and must be kept for compat) - """ - - def to_dict(self): - image_dict = self.image.to_dict() - image_dict['history'] = [parent_image.to_dict() for parent_image in self.image.parents] - return image_dict - - -@add_metaclass(ABCMeta) -class ImageInterface(object): - """ - Interface that represents all data store interactions required by the image API endpoint. - """ - - @abstractmethod - def get_repository_images(self, namespace_name, repo_name): - """ - Returns an iterator of all the ImageWithTag's defined in the matching repository. If the - repository doesn't exist, returns None. - """ - - @abstractmethod - def get_repository_image(self, namespace_name, repo_name, docker_image_id): - """ - Returns the matching ImageWithHistory under the matching repository, if any. If none, - returns None. - """ diff --git a/endpoints/api/image_models_pre_oci.py b/endpoints/api/image_models_pre_oci.py deleted file mode 100644 index e29c294b0..000000000 --- a/endpoints/api/image_models_pre_oci.py +++ /dev/null @@ -1,56 +0,0 @@ -from collections import defaultdict -from data import model -from endpoints.api.image_models_interface import (ImageInterface, ImageWithHistory, ImageWithTags, - Image) - -def _image(namespace_name, repo_name, image, all_images, include_parents=True): - parent_image_tuples = [] - if include_parents: - parent_images = [all_images[ancestor_id] for ancestor_id in image.ancestor_id_list() - if all_images.get(ancestor_id)] - parent_image_tuples = [_image(namespace_name, repo_name, parent_image, all_images, False) - for parent_image in parent_images] - - return Image(image.docker_image_id, image.created, image.comment, image.command, - image.storage.image_size, image.storage.uploading, parent_image_tuples) - -def _tag_names(image, tags_by_docker_id): - return [tag.name for tag in tags_by_docker_id.get(image.docker_image_id, [])] - - -class PreOCIModel(ImageInterface): - """ - PreOCIModel implements the data model for the Image API using a database schema - before it was changed to support the OCI specification. - """ - def get_repository_images(self, namespace_name, repo_name): - repo = model.repository.get_repository(namespace_name, repo_name) - if not repo: - return None - - all_images = model.image.get_repository_images_without_placements(repo) - all_images_map = {image.id: image for image in all_images} - - all_tags = list(model.tag.list_repository_tags(namespace_name, repo_name)) - - tags_by_docker_id = defaultdict(list) - for tag in all_tags: - tags_by_docker_id[tag.image.docker_image_id].append(tag) - - def _build_image(image): - image_itself = _image(namespace_name, repo_name, image, all_images_map) - return ImageWithTags(image_itself, tag_names=_tag_names(image, tags_by_docker_id)) - - return [_build_image(image) for image in all_images] - - def get_repository_image(self, namespace_name, repo_name, docker_image_id): - image = model.image.get_repo_image_and_storage(namespace_name, repo_name, docker_image_id) - if not image: - return None - - parent_images = model.image.get_parent_images(namespace_name, repo_name, image) - all_images_map = {image.id: image for image in parent_images} - return ImageWithHistory(_image(namespace_name, repo_name, image, all_images_map)) - - -pre_oci_model = PreOCIModel()