608ffd9663
Adds basic labels support to the registry code (V2), and the API. Note that this does not yet add any UI related support.
121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
import logging
|
|
|
|
from data.database import Label, TagManifestLabel, MediaType, LabelSourceType, db_transaction
|
|
from data.model import InvalidLabelKeyException, InvalidMediaTypeException, DataModelException
|
|
from data.model._basequery import prefix_search
|
|
from util.validation import validate_label_key
|
|
from util.validation import is_json
|
|
from cachetools import lru_cache
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def get_label_source_types():
|
|
source_type_map = {}
|
|
for kind in LabelSourceType.select():
|
|
source_type_map[kind.id] = kind.name
|
|
source_type_map[kind.name] = kind.id
|
|
|
|
return source_type_map
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def get_media_types():
|
|
media_type_map = {}
|
|
for kind in MediaType.select():
|
|
media_type_map[kind.id] = kind.name
|
|
media_type_map[kind.name] = kind.id
|
|
|
|
return media_type_map
|
|
|
|
|
|
def _get_label_source_type_id(name):
|
|
kinds = get_label_source_types()
|
|
return kinds[name]
|
|
|
|
|
|
def _get_media_type_id(name):
|
|
kinds = get_media_types()
|
|
return kinds[name]
|
|
|
|
|
|
def create_manifest_label(tag_manifest, key, value, source_type_name, media_type_name=None):
|
|
""" Creates a new manifest label on a specific tag manifest. """
|
|
if not key:
|
|
raise InvalidLabelKeyException()
|
|
|
|
# Note that we don't prevent invalid label names coming from the manifest to be stored, as Docker
|
|
# does not currently prevent them from being put into said manifests.
|
|
if not validate_label_key(key) and source_type_name != 'manifest':
|
|
raise InvalidLabelKeyException()
|
|
|
|
# Find the matching media type. If none specified, we infer.
|
|
if media_type_name is None:
|
|
media_type_name = 'text/plain'
|
|
if is_json(value):
|
|
media_type_name = 'application/json'
|
|
|
|
media_type_id = _get_media_type_id(media_type_name)
|
|
if media_type_id is None:
|
|
raise InvalidMediaTypeException
|
|
|
|
source_type_id = _get_label_source_type_id(source_type_name)
|
|
|
|
with db_transaction():
|
|
label = Label.create(key=key, value=value, source_type=source_type_id, media_type=media_type_id)
|
|
TagManifestLabel.create(annotated=tag_manifest, label=label,
|
|
repository=tag_manifest.tag.repository)
|
|
|
|
return label
|
|
|
|
|
|
def list_manifest_labels(tag_manifest, prefix_filter=None):
|
|
""" Lists all labels found on the given tag manifest. """
|
|
query = (Label.select(Label, MediaType)
|
|
.join(MediaType)
|
|
.switch(Label)
|
|
.join(LabelSourceType)
|
|
.switch(Label)
|
|
.join(TagManifestLabel)
|
|
.where(TagManifestLabel.annotated == tag_manifest))
|
|
|
|
if prefix_filter is not None:
|
|
query = query.where(prefix_search(Label.key, prefix_filter))
|
|
|
|
return query
|
|
|
|
|
|
def get_manifest_label(label_uuid, tag_manifest):
|
|
""" Retrieves the manifest label on the tag manifest with the given ID. """
|
|
try:
|
|
return (Label.select(Label, LabelSourceType)
|
|
.join(LabelSourceType)
|
|
.where(Label.uuid == label_uuid)
|
|
.switch(Label)
|
|
.join(TagManifestLabel)
|
|
.where(TagManifestLabel.annotated == tag_manifest)
|
|
.get())
|
|
except Label.DoesNotExist:
|
|
return None
|
|
|
|
|
|
def delete_manifest_label(label_uuid, tag_manifest):
|
|
""" Deletes the manifest label on the tag manifest with the given ID. """
|
|
|
|
# Find the label itself.
|
|
label = get_manifest_label(label_uuid, tag_manifest)
|
|
if label is None:
|
|
return None
|
|
|
|
if not label.source_type.mutable:
|
|
raise DataModelException('Cannot delete immutable label')
|
|
|
|
# Delete the mapping record and label.
|
|
deleted_count = TagManifestLabel.delete().where(TagManifestLabel.label == label).execute()
|
|
if deleted_count != 1:
|
|
logger.warning('More than a single label deleted for matching label %s', label_uuid)
|
|
|
|
label.delete_instance(recursive=False)
|
|
return label
|
|
|