Change manifest API endpoints to use new registry data interface
This commit is contained in:
parent
6c494f4917
commit
a0a6a3d67d
7 changed files with 200 additions and 247 deletions
|
@ -1,23 +1,43 @@
|
|||
""" Manage the manifests of a repository. """
|
||||
import json
|
||||
from flask import request
|
||||
|
||||
from app import label_validator
|
||||
from flask import request
|
||||
from data.model import InvalidLabelKeyException, InvalidMediaTypeException
|
||||
from data.registry_model import registry_model
|
||||
from digest import digest_tools
|
||||
from endpoints.api import (resource, nickname, require_repo_read, require_repo_write,
|
||||
RepositoryParamResource, log_action, validate_json_request,
|
||||
path_param, parse_args, query_param, abort, api,
|
||||
disallow_for_app_repositories)
|
||||
from endpoints.api.image import image_dict
|
||||
from endpoints.exception import NotFound
|
||||
from manifest_models_pre_oci import pre_oci_model as model
|
||||
from data.model import InvalidLabelKeyException, InvalidMediaTypeException
|
||||
|
||||
from digest import digest_tools
|
||||
from util.validation import VALID_LABEL_KEY_REGEX
|
||||
|
||||
|
||||
BASE_MANIFEST_ROUTE = '/v1/repository/<apirepopath:repository>/manifest/<regex("{0}"):manifestref>'
|
||||
MANIFEST_DIGEST_ROUTE = BASE_MANIFEST_ROUTE.format(digest_tools.DIGEST_PATTERN)
|
||||
ALLOWED_LABEL_MEDIA_TYPES = ['text/plain', 'application/json']
|
||||
|
||||
def _label_dict(label):
|
||||
return {
|
||||
'id': label.uuid,
|
||||
'key': label.key,
|
||||
'value': label.value,
|
||||
'source_type': label.source_type_name,
|
||||
'media_type': label.media_type_name,
|
||||
}
|
||||
|
||||
def _manifest_dict(manifest):
|
||||
image = None
|
||||
if manifest.legacy_image is not None:
|
||||
image = image_dict(manifest.legacy_image, with_history=True)
|
||||
|
||||
return {
|
||||
'digest': manifest.digest,
|
||||
'manifest_data': manifest.manifest_bytes,
|
||||
'image': image,
|
||||
}
|
||||
|
||||
|
||||
@resource(MANIFEST_DIGEST_ROUTE)
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
|
@ -28,11 +48,16 @@ class RepositoryManifest(RepositoryParamResource):
|
|||
@nickname('getRepoManifest')
|
||||
@disallow_for_app_repositories
|
||||
def get(self, namespace_name, repository_name, manifestref):
|
||||
manifest = model.get_repository_manifest(namespace_name, repository_name, manifestref)
|
||||
repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
|
||||
if repo_ref is None:
|
||||
raise NotFound()
|
||||
|
||||
manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref,
|
||||
include_legacy_image=True)
|
||||
if manifest is None:
|
||||
raise NotFound()
|
||||
|
||||
return manifest.to_dict()
|
||||
return _manifest_dict(manifest)
|
||||
|
||||
|
||||
@resource(MANIFEST_DIGEST_ROUTE + '/labels')
|
||||
|
@ -74,11 +99,20 @@ class RepositoryManifestLabels(RepositoryParamResource):
|
|||
@query_param('filter', 'If specified, only labels matching the given prefix will be returned',
|
||||
type=str, default=None)
|
||||
def get(self, namespace_name, repository_name, manifestref, parsed_args):
|
||||
labels = model.get_manifest_labels(namespace_name, repository_name, manifestref, filter=parsed_args['filter'])
|
||||
repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
|
||||
if repo_ref is None:
|
||||
raise NotFound()
|
||||
|
||||
manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref)
|
||||
if manifest is None:
|
||||
raise NotFound()
|
||||
|
||||
labels = registry_model.list_manifest_labels(manifest, parsed_args['filter'])
|
||||
if labels is None:
|
||||
raise NotFound()
|
||||
|
||||
return {
|
||||
'labels': [label.to_dict() for label in labels]
|
||||
'labels': [_label_dict(label) for label in labels]
|
||||
}
|
||||
|
||||
@require_repo_write
|
||||
|
@ -93,24 +127,32 @@ class RepositoryManifestLabels(RepositoryParamResource):
|
|||
if label_validator.has_reserved_prefix(label_data['key']):
|
||||
abort(400, message='Label has a reserved prefix')
|
||||
|
||||
repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
|
||||
if repo_ref is None:
|
||||
raise NotFound()
|
||||
|
||||
manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref)
|
||||
if manifest is None:
|
||||
raise NotFound()
|
||||
|
||||
label = None
|
||||
try:
|
||||
label = model.create_manifest_label(namespace_name,
|
||||
repository_name,
|
||||
manifestref,
|
||||
label_data['key'],
|
||||
label_data['value'],
|
||||
'api',
|
||||
label_data['media_type'])
|
||||
label = registry_model.create_manifest_label(manifest,
|
||||
label_data['key'],
|
||||
label_data['value'],
|
||||
'api',
|
||||
label_data['media_type'])
|
||||
except InvalidLabelKeyException:
|
||||
abort(400, message='Label is of an invalid format or missing please use %s format for labels'.format(
|
||||
VALID_LABEL_KEY_REGEX))
|
||||
message = ('Label is of an invalid format or missing please ' +
|
||||
'use %s format for labels' % VALID_LABEL_KEY_REGEX)
|
||||
abort(400, message=message)
|
||||
except InvalidMediaTypeException:
|
||||
abort(400, message='Media type is invalid please use a valid media type of text/plain or application/json')
|
||||
message = 'Media type is invalid please use a valid media type: text/plain, application/json'
|
||||
abort(400, message=message)
|
||||
|
||||
if label is None:
|
||||
raise NotFound()
|
||||
|
||||
|
||||
metadata = {
|
||||
'id': label.uuid,
|
||||
'key': label.key,
|
||||
|
@ -123,7 +165,7 @@ class RepositoryManifestLabels(RepositoryParamResource):
|
|||
|
||||
log_action('manifest_label_add', namespace_name, metadata, repo_name=repository_name)
|
||||
|
||||
resp = {'label': label.to_dict()}
|
||||
resp = {'label': _label_dict(label)}
|
||||
repo_string = '%s/%s' % (namespace_name, repository_name)
|
||||
headers = {
|
||||
'Location': api.url_for(ManageRepositoryManifestLabel, repository=repo_string,
|
||||
|
@ -143,11 +185,19 @@ class ManageRepositoryManifestLabel(RepositoryParamResource):
|
|||
@disallow_for_app_repositories
|
||||
def get(self, namespace_name, repository_name, manifestref, labelid):
|
||||
""" Retrieves the label with the specific ID under the manifest. """
|
||||
label = model.get_manifest_label(namespace_name, repository_name, manifestref, labelid)
|
||||
repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
|
||||
if repo_ref is None:
|
||||
raise NotFound()
|
||||
|
||||
manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref)
|
||||
if manifest is None:
|
||||
raise NotFound()
|
||||
|
||||
label = registry_model.get_manifest_label(manifest, labelid)
|
||||
if label is None:
|
||||
raise NotFound()
|
||||
|
||||
return label.to_dict()
|
||||
return _label_dict(label)
|
||||
|
||||
|
||||
@require_repo_write
|
||||
|
@ -155,7 +205,15 @@ class ManageRepositoryManifestLabel(RepositoryParamResource):
|
|||
@disallow_for_app_repositories
|
||||
def delete(self, namespace_name, repository_name, manifestref, labelid):
|
||||
""" Deletes an existing label from a manifest. """
|
||||
deleted = model.delete_manifest_label(namespace_name, repository_name, manifestref, labelid)
|
||||
repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
|
||||
if repo_ref is None:
|
||||
raise NotFound()
|
||||
|
||||
manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref)
|
||||
if manifest is None:
|
||||
raise NotFound()
|
||||
|
||||
deleted = registry_model.delete_manifest_label(manifest, labelid)
|
||||
if deleted is None:
|
||||
raise NotFound()
|
||||
|
||||
|
@ -170,4 +228,3 @@ class ManageRepositoryManifestLabel(RepositoryParamResource):
|
|||
|
||||
log_action('manifest_label_delete', namespace_name, metadata, repo_name=repository_name)
|
||||
return '', 204
|
||||
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
from collections import namedtuple
|
||||
|
||||
from endpoints.api.image import image_dict
|
||||
from six import add_metaclass
|
||||
|
||||
|
||||
class ManifestLabel(
|
||||
namedtuple('ManifestLabel', [
|
||||
'uuid',
|
||||
'key',
|
||||
'value',
|
||||
'source_type_name',
|
||||
'media_type_name',
|
||||
])):
|
||||
"""
|
||||
ManifestLabel represents a label on a manifest
|
||||
:type uuid: string
|
||||
:type key: string
|
||||
:type value: string
|
||||
:type source_type_name: string
|
||||
:type media_type_name: string
|
||||
"""
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.uuid,
|
||||
'key': self.key,
|
||||
'value': self.value,
|
||||
'source_type': self.source_type_name,
|
||||
'media_type': self.media_type_name,
|
||||
}
|
||||
|
||||
|
||||
class ManifestAndImage(
|
||||
namedtuple('ManifestAndImage', [
|
||||
'digest',
|
||||
'manifest_data',
|
||||
'image',
|
||||
])):
|
||||
def to_dict(self):
|
||||
return {
|
||||
'digest': self.digest,
|
||||
'manifest_data': self.manifest_data,
|
||||
'image': image_dict(self.image),
|
||||
}
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class ManifestLabelInterface(object):
|
||||
"""
|
||||
Data interface that the manifest labels API uses
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_manifest_labels(self, namespace_name, repository_name, manifestref, filter=None):
|
||||
"""
|
||||
|
||||
Args:
|
||||
namespace_name: string
|
||||
repository_name: string
|
||||
manifestref: string
|
||||
filter: string
|
||||
|
||||
Returns:
|
||||
list(ManifestLabel) or None
|
||||
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def create_manifest_label(self, namespace_name, repository_name, manifestref, key, value, source_type_name, media_type_name):
|
||||
"""
|
||||
|
||||
Args:
|
||||
namespace_name: string
|
||||
repository_name: string
|
||||
manifestref: string
|
||||
key: string
|
||||
value: string
|
||||
source_type_name: string
|
||||
media_type_name: string
|
||||
|
||||
Returns:
|
||||
ManifestLabel or None
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_manifest_label(self, namespace_name, repository_name, manifestref, label_uuid):
|
||||
"""
|
||||
|
||||
Args:
|
||||
namespace_name: string
|
||||
repository_name: string
|
||||
manifestref: string
|
||||
label_uuid: string
|
||||
|
||||
Returns:
|
||||
ManifestLabel or None
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def delete_manifest_label(self, namespace_name, repository_name, manifestref, label_uuid):
|
||||
"""
|
||||
|
||||
Args:
|
||||
namespace_name: string
|
||||
repository_name: string
|
||||
manifestref: string
|
||||
label_uuid: string
|
||||
|
||||
Returns:
|
||||
ManifestLabel or None
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_repository_manifest(self, namespace_name, repository_name, digest):
|
||||
"""
|
||||
Returns the manifest and image for the manifest with the specified digest, if any.
|
||||
"""
|
|
@ -1,74 +0,0 @@
|
|||
import json
|
||||
|
||||
from manifest_models_interface import ManifestLabel, ManifestLabelInterface, ManifestAndImage
|
||||
from data import model
|
||||
from data.registry_model import registry_model
|
||||
|
||||
|
||||
class ManifestLabelPreOCI(ManifestLabelInterface):
|
||||
def get_manifest_labels(self, namespace_name, repository_name, manifestref, filter=None):
|
||||
try:
|
||||
tag_manifest = model.tag.load_manifest_by_digest(namespace_name, repository_name, manifestref)
|
||||
except model.DataModelException:
|
||||
return None
|
||||
|
||||
labels = model.label.list_manifest_labels(tag_manifest, prefix_filter=filter)
|
||||
return [self._label(l) for l in labels]
|
||||
|
||||
def create_manifest_label(self, namespace_name, repository_name, manifestref, key, value, source_type_name, media_type_name):
|
||||
try:
|
||||
tag_manifest = model.tag.load_manifest_by_digest(namespace_name, repository_name, manifestref)
|
||||
except model.DataModelException:
|
||||
return None
|
||||
|
||||
return self._label(model.label.create_manifest_label(tag_manifest, key, value, source_type_name, media_type_name))
|
||||
|
||||
def get_manifest_label(self, namespace_name, repository_name, manifestref, label_uuid):
|
||||
try:
|
||||
tag_manifest = model.tag.load_manifest_by_digest(namespace_name, repository_name, manifestref)
|
||||
except model.DataModelException:
|
||||
return None
|
||||
|
||||
return self._label(model.label.get_manifest_label(label_uuid, tag_manifest))
|
||||
|
||||
def delete_manifest_label(self, namespace_name, repository_name, manifestref, label_uuid):
|
||||
try:
|
||||
tag_manifest = model.tag.load_manifest_by_digest(namespace_name, repository_name, manifestref)
|
||||
except model.DataModelException:
|
||||
return None
|
||||
|
||||
return self._label(model.label.delete_manifest_label(label_uuid, tag_manifest))
|
||||
|
||||
def get_repository_manifest(self, namespace_name, repository_name, digest):
|
||||
try:
|
||||
tag_manifest = model.tag.load_manifest_by_digest(namespace_name, repository_name, digest,
|
||||
allow_dead=True)
|
||||
except model.DataModelException:
|
||||
return None
|
||||
|
||||
# TODO: remove this dependency on image once we've moved to the new data model.
|
||||
repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
|
||||
if repo_ref is None:
|
||||
return None
|
||||
|
||||
image = registry_model.get_legacy_image(repo_ref, tag_manifest.tag.image.docker_image_id,
|
||||
include_parents=True)
|
||||
if image is None:
|
||||
return None
|
||||
|
||||
manifest_data = json.loads(tag_manifest.json_data)
|
||||
return ManifestAndImage(digest=digest, manifest_data=manifest_data, image=image)
|
||||
|
||||
|
||||
def _label(self, label_obj):
|
||||
if not label_obj:
|
||||
return None
|
||||
return ManifestLabel(
|
||||
uuid=label_obj.uuid,
|
||||
key=label_obj.key,
|
||||
value=label_obj.value,
|
||||
source_type_name=label_obj.source_type.name,
|
||||
media_type_name=label_obj.media_type.name,
|
||||
)
|
||||
|
||||
pre_oci_model = ManifestLabelPreOCI()
|
Reference in a new issue