port label support to refactored v2 registry

This commit is contained in:
Jimmy Zelinskie 2016-09-20 22:09:25 -04:00
parent 3c8b87e086
commit ca883e5662
5 changed files with 66 additions and 11 deletions

View file

@ -957,7 +957,7 @@ class ServiceKey(BaseModel):
rotation_duration = IntegerField(null=True) rotation_duration = IntegerField(null=True)
approval = ForeignKeyField(ServiceKeyApproval, null=True) approval = ForeignKeyField(ServiceKeyApproval, null=True)
'''
class MediaType(BaseModel): class MediaType(BaseModel):
""" MediaType is an enumeration of the possible formats of various objects in the data model. """ """ MediaType is an enumeration of the possible formats of various objects in the data model. """
name = CharField(index=True, unique=True) name = CharField(index=True, unique=True)
@ -992,6 +992,21 @@ class TagManifestLabel(BaseModel):
(('annotated', 'label'), True), (('annotated', 'label'), True),
) )
'''
class ManifestLabel(BaseModel):
repository = ForeignKeyField(Repository, index=True)
annotated = ForeignKeyField(Manifest, index=True)
label = ForeignKeyField(Label)
class Meta:
database = db
read_slaves = (read_slave,)
indexes = (
(('repository', 'annotated', 'label'), True),
)
class Blob(BaseModel): class Blob(BaseModel):
""" Blob represents a content-addressable object stored outside of the database. """ """ Blob represents a content-addressable object stored outside of the database. """
digest = CharField(index=True, unique=True) digest = CharField(index=True, unique=True)

View file

@ -4,7 +4,7 @@ from namedlist import namedlist
from peewee import IntegrityError from peewee import IntegrityError
from data import model, database from data import model, database
from data.model import DataModelException from data.model import DataModelException, TagAlreadyCreatedException
from image.docker.v1 import DockerV1Metadata from image.docker.v1 import DockerV1Metadata
_MEDIA_TYPE = "application/vnd.docker.distribution.manifest.v1+prettyjws" _MEDIA_TYPE = "application/vnd.docker.distribution.manifest.v1+prettyjws"
@ -48,6 +48,11 @@ class RepositoryReference(namedtuple('RepositoryReference', ['id', 'name', 'name
RepositoryReference represents a reference to a Repository, without its full metadata. RepositoryReference represents a reference to a Repository, without its full metadata.
""" """
class Label(namedtuple('Label', ['key', 'value', 'source_type', 'media_type'])):
"""
Label represents a key-value pair that describes a particular Manifest.
"""
class DockerRegistryV2DataInterface(object): class DockerRegistryV2DataInterface(object):
""" """
@ -158,6 +163,8 @@ class DockerRegistryV2DataInterface(object):
""" """
Saves a manifest pointing to the given leaf image, with the given manifest, under the matching Saves a manifest pointing to the given leaf image, with the given manifest, under the matching
repository as a tag with the given name. repository as a tag with the given name.
Returns a boolean whether or not the tag was newly created or not.
""" """
raise NotImplementedError() raise NotImplementedError()
@ -246,6 +253,14 @@ class DockerRegistryV2DataInterface(object):
""" """
raise NotImplementedError() raise NotImplementedError()
@classmethod
def create_manifest_labels(cls, namespace_name, repo_name, manifest_digest, labels):
"""
Creates a new labels for the provided manifest.
"""
raise NotImplementedError()
@classmethod @classmethod
def get_blob_path(cls, blob): def get_blob_path(cls, blob):
""" """
@ -407,8 +422,10 @@ class PreOCIModel(DockerRegistryV2DataInterface):
@classmethod @classmethod
def save_manifest(cls, namespace_name, repo_name, tag_name, leaf_layer_docker_id, manifest_digest, def save_manifest(cls, namespace_name, repo_name, tag_name, leaf_layer_docker_id, manifest_digest,
manifest_bytes): manifest_bytes):
model.tag.store_tag_manifest(namespace_name, repo_name, tag_name, leaf_layer_docker_id, (_, newly_created) = model.tag.store_tag_manifest(namespace_name, repo_name, tag_name,
manifest_digest, manifest_bytes) leaf_layer_docker_id, manifest_digest,
manifest_bytes)
return newly_created
@classmethod @classmethod
def repository_tags(cls, namespace_name, repo_name, limit, offset): def repository_tags(cls, namespace_name, repo_name, limit, offset):
@ -540,6 +557,17 @@ class PreOCIModel(DockerRegistryV2DataInterface):
blob_record = model.storage.get_storage_by_uuid(blob.uuid) blob_record = model.storage.get_storage_by_uuid(blob.uuid)
model.storage.save_torrent_info(blob_record, piece_size, piece_bytes) model.storage.save_torrent_info(blob_record, piece_size, piece_bytes)
@classmethod
def create_manifest_labels(cls, namespace_name, repo_name, manifest_digest, labels):
if not labels:
# No point in doing anything more.
return
tag_manifest = model.tag.load_manifest_by_digest(namespace_name, repo_name, manifest_digest)
for label in labels:
model.label.create_manifest_label(tag_manifest, label.key, label.value, label.source_type,
label.media_type)
@classmethod @classmethod
def get_blob_path(cls, blob): def get_blob_path(cls, blob):
blob_record = model.storage.get_storage_by_uuid(blob.uuid) blob_record = model.storage.get_storage_by_uuid(blob.uuid)

View file

@ -1,11 +1,12 @@
import logging import logging
from cachetools import lru_cache
from data.database import Label, TagManifestLabel, MediaType, LabelSourceType, db_transaction from data.database import Label, TagManifestLabel, MediaType, LabelSourceType, db_transaction
from data.model import InvalidLabelKeyException, InvalidMediaTypeException, DataModelException from data.model import InvalidLabelKeyException, InvalidMediaTypeException, DataModelException
from data.model._basequery import prefix_search from data.model._basequery import prefix_search
from util.validation import validate_label_key from util.validation import validate_label_key
from util.validation import is_json from util.validation import is_json
from cachetools import lru_cache
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

@ -8,7 +8,7 @@ import features
from app import docker_v2_signing_key, app, metric_queue from app import docker_v2_signing_key, app, metric_queue
from auth.registry_jwt_auth import process_registry_jwt_auth from auth.registry_jwt_auth import process_registry_jwt_auth
from data.interfaces.v2 import PreOCIModel as model from data.interfaces.v2 import PreOCIModel as model, Label
from digest import digest_tools from digest import digest_tools
from endpoints.common import parse_repository_name from endpoints.common import parse_repository_name
from endpoints.decorators import anon_protect from endpoints.decorators import anon_protect
@ -20,8 +20,9 @@ from endpoints.notificationhelper import spawn_notification
from image.docker import ManifestException from image.docker import ManifestException
from image.docker.schema1 import DockerSchema1Manifest, DockerSchema1ManifestBuilder from image.docker.schema1 import DockerSchema1Manifest, DockerSchema1ManifestBuilder
from image.docker.schema2 import DOCKER_SCHEMA2_CONTENT_TYPES from image.docker.schema2 import DOCKER_SCHEMA2_CONTENT_TYPES
from util.registry.replication import queue_storage_replication
from util.names import VALID_TAG_PATTERN from util.names import VALID_TAG_PATTERN
from util.registry.replication import queue_storage_replication
from util.validation import is_json
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -175,8 +176,14 @@ def _write_manifest(namespace_name, repo_name, manifest):
# Store the manifest pointing to the tag. # Store the manifest pointing to the tag.
leaf_layer_id = rewritten_images[-1].image_id leaf_layer_id = rewritten_images[-1].image_id
model.save_manifest(namespace_name, repo_name, manifest.tag, leaf_layer_id, manifest.digest, newly_created = model.save_manifest(namespace_name, repo_name, manifest.tag, leaf_layer_id,
manifest.bytes) manifest.digest, manifest.bytes)
if newly_created:
labels = []
for key, value in manifest.layers[-1].v1_metadata.labels.iteritems():
media_type = 'application/json' if is_json(value) else 'text/plain'
labels.append(Label(key=key, value=value, source_type='manifest', media_type=media_type))
model.create_manifest_labels(namespace_name, repo_name, manifest.digest, labels)
return repo, storage_map return repo, storage_map
@ -257,3 +264,6 @@ def _generate_and_store_manifest(namespace_name, repo_name, tag_name):
model.create_manifest_and_update_tag(namespace_name, repo_name, tag_name, manifest.digest, model.create_manifest_and_update_tag(namespace_name, repo_name, tag_name, manifest.digest,
manifest.bytes) manifest.bytes)
return manifest return manifest
def _determine_media_type(value):
media_type_name = 'application/json' if is_json(value) else 'text/plain'

View file

@ -76,7 +76,7 @@ class Schema1Layer(namedtuple('Schema1Layer', ['digest', 'v1_metadata', 'raw_v1_
class Schema1V1Metadata(namedtuple('Schema1V1Metadata', ['image_id', 'parent_image_id', 'created', class Schema1V1Metadata(namedtuple('Schema1V1Metadata', ['image_id', 'parent_image_id', 'created',
'comment', 'command'])): 'comment', 'command', 'labels'])):
""" """
Represents the necessary data extracted from the v1 compatibility string in a given layer of a Represents the necessary data extracted from the v1 compatibility string in a given layer of a
Manifest. Manifest.
@ -207,9 +207,10 @@ class DockerSchema1Manifest(object):
if not 'id' in v1_metadata: if not 'id' in v1_metadata:
raise MalformedSchema1Manifest('id field missing from v1Compatibility JSON') raise MalformedSchema1Manifest('id field missing from v1Compatibility JSON')
labels = v1_metadata.get('config', {}).get('Labels', {}) or {}
extracted = Schema1V1Metadata(v1_metadata['id'], v1_metadata.get('parent'), extracted = Schema1V1Metadata(v1_metadata['id'], v1_metadata.get('parent'),
v1_metadata.get('created'), v1_metadata.get('comment'), v1_metadata.get('created'), v1_metadata.get('comment'),
command) command, labels)
yield Schema1Layer(image_digest, extracted, metadata_string) yield Schema1Layer(image_digest, extracted, metadata_string)
@property @property