update interfaces to use ABC

This commit is contained in:
Jimmy Zelinskie 2016-09-23 17:50:09 -04:00
parent a1a930b833
commit 44eca10c05
14 changed files with 467 additions and 508 deletions

View file

@ -1,8 +1,13 @@
import json
from abc import ABCMeta, abstractmethod
from collections import namedtuple
from six import add_metaclass
from data import model
from image.docker.v1 import DockerV1Metadata
import json
class DerivedImage(namedtuple('DerivedImage', ['ref', 'blob', 'internal_source_image_db_id'])):
"""
@ -31,105 +36,106 @@ class TorrentInfo(namedtuple('TorrentInfo', ['piece_length', 'pieces'])):
"""
@add_metaclass(ABCMeta)
class VerbsDataInterface(object):
"""
Interface that represents all data store interactions required by the registry's custom HTTP
verbs.
"""
@classmethod
def repository_is_public(cls, namespace_name, repo_name):
@abstractmethod
def repository_is_public(self, namespace_name, repo_name):
"""
Returns a boolean for whether the repository with the given name and namespace is public.
"""
raise NotImplementedError()
pass
@classmethod
def get_manifest_layers_with_blobs(cls, repo_image):
@abstractmethod
def get_manifest_layers_with_blobs(self, repo_image):
"""
Returns the full set of manifest layers and their associated blobs starting at the given
repository image and working upwards to the root image.
"""
raise NotImplementedError()
pass
@classmethod
def get_blob_path(cls, blob):
@abstractmethod
def get_blob_path(self, blob):
"""
Returns the storage path for the given blob.
"""
raise NotImplementedError()
pass
@classmethod
def get_derived_image_signature(cls, derived_image, signer_name):
@abstractmethod
def get_derived_image_signature(self, derived_image, signer_name):
"""
Returns the signature associated with the derived image and a specific signer or None if none.
"""
raise NotImplementedError()
pass
@classmethod
def set_derived_image_signature(cls, derived_image, signer_name, signature):
@abstractmethod
def set_derived_image_signature(self, derived_image, signer_name, signature):
"""
Sets the calculated signature for the given derived image and signer to that specified.
"""
raise NotImplementedError()
pass
@classmethod
def delete_derived_image(cls, derived_image):
@abstractmethod
def delete_derived_image(self, derived_image):
"""
Deletes a derived image and all of its storage.
"""
raise NotImplementedError()
pass
@classmethod
def set_blob_size(cls, blob, size):
@abstractmethod
def set_blob_size(self, blob, size):
"""
Sets the size field on a blob to the value specified.
"""
raise NotImplementedError()
pass
@classmethod
def get_repo_blob_by_digest(cls, namespace_name, repo_name, digest):
@abstractmethod
def get_repo_blob_by_digest(self, namespace_name, repo_name, digest):
"""
Returns the blob with the given digest under the matching repository or None if none.
"""
raise NotImplementedError()
pass
@classmethod
def get_torrent_info(cls, blob):
@abstractmethod
def get_torrent_info(self, blob):
"""
Returns the torrent information associated with the given blob or None if none.
"""
raise NotImplementedError()
pass
@classmethod
def set_torrent_info(cls, blob, piece_length, pieces):
@abstractmethod
def set_torrent_info(self, blob, piece_length, pieces):
"""
Sets the torrent infomation associated with the given blob to that specified.
"""
raise NotImplementedError()
pass
@classmethod
def lookup_derived_image(cls, repo_image, verb, varying_metadata=None):
@abstractmethod
def lookup_derived_image(self, repo_image, verb, varying_metadata=None):
"""
Looks up the derived image for the given repository image, verb and optional varying metadata
and returns it or None if none.
"""
raise NotImplementedError()
pass
@classmethod
def lookup_or_create_derived_image(cls, repo_image, verb, location, varying_metadata=None):
@abstractmethod
def lookup_or_create_derived_image(self, repo_image, verb, location, varying_metadata=None):
"""
Looks up the derived image for the given repository image, verb and optional varying metadata
and returns it. If none exists, a new derived image is created.
"""
raise NotImplementedError()
pass
@classmethod
def get_tag_image(cls, namespace_name, repo_name, tag_name):
@abstractmethod
def get_tag_image(self, namespace_name, repo_name, tag_name):
"""
Returns the image associated with the live tag with the given name under the matching repository
or None if none.
"""
raise NotImplementedError()
pass
class PreOCIModel(VerbsDataInterface):
@ -138,35 +144,10 @@ class PreOCIModel(VerbsDataInterface):
before it was changed to support the OCI specification.
"""
@classmethod
def repository_is_public(cls, namespace_name, repo_name):
def repository_is_public(self, namespace_name, repo_name):
return model.repository.repository_is_public(namespace_name, repo_name)
@classmethod
def _docker_v1_metadata(cls, namespace_name, repo_name, repo_image):
"""
Returns a DockerV1Metadata object for the given image under the repository with the given
namespace and name. Note that the namespace and name are passed here as an optimization, and are
*not checked* against the image. Also note that we only fill in the localized data needed by
verbs.
"""
return DockerV1Metadata(
namespace_name=namespace_name,
repo_name=repo_name,
image_id=repo_image.docker_image_id,
checksum=repo_image.v1_checksum,
compat_json=repo_image.v1_json_metadata,
created=repo_image.created,
comment=repo_image.comment,
command=repo_image.command,
# Note: These are not needed in verbs and are expensive to load, so we just skip them.
content_checksum=None,
parent_image_id=None,
)
@classmethod
def get_manifest_layers_with_blobs(cls, repo_image):
def get_manifest_layers_with_blobs(self, repo_image):
repo_image_record = model.image.get_image_by_id(repo_image.repository.namespace_name,
repo_image.repository.name,
repo_image.image_id)
@ -186,16 +167,15 @@ class PreOCIModel(VerbsDataInterface):
yield ImageWithBlob(
image_id=parent.docker_image_id,
blob=cls._blob(parent.storage),
blob=_blob(parent.storage),
repository=repo_image.repository,
compat_metadata=metadata,
v1_metadata=cls._docker_v1_metadata(repo_image.repository.namespace_name,
repo_image.repository.name, parent),
v1_metadata=_docker_v1_metadata(repo_image.repository.namespace_name,
repo_image.repository.name, parent),
internal_db_id=parent.id,
)
@classmethod
def get_derived_image_signature(cls, derived_image, signer_name):
def get_derived_image_signature(self, derived_image, signer_name):
storage = model.storage.get_storage_by_uuid(derived_image.blob.uuid)
signature_entry = model.storage.lookup_storage_signature(storage, signer_name)
if signature_entry is None:
@ -203,41 +183,35 @@ class PreOCIModel(VerbsDataInterface):
return signature_entry.signature
@classmethod
def set_derived_image_signature(cls, derived_image, signer_name, signature):
def set_derived_image_signature(self, derived_image, signer_name, signature):
storage = model.storage.get_storage_by_uuid(derived_image.blob.uuid)
signature_entry = model.storage.find_or_create_storage_signature(storage, signer_name)
signature_entry.signature = signature
signature_entry.uploading = False
signature_entry.save()
@classmethod
def delete_derived_image(cls, derived_image):
def delete_derived_image(self, derived_image):
model.image.delete_derived_storage_by_uuid(derived_image.blob.uuid)
@classmethod
def set_blob_size(cls, blob, size):
def set_blob_size(self, blob, size):
storage_entry = model.storage.get_storage_by_uuid(blob.uuid)
storage_entry.image_size = size
storage_entry.uploading = False
storage_entry.save()
@classmethod
def get_blob_path(cls, blob):
def get_blob_path(self, blob):
blob_record = model.storage.get_storage_by_uuid(blob.uuid)
return model.storage.get_layer_path(blob_record)
@classmethod
def get_repo_blob_by_digest(cls, namespace_name, repo_name, digest):
def get_repo_blob_by_digest(self, namespace_name, repo_name, digest):
try:
blob_record = model.blob.get_repo_blob_by_digest(namespace_name, repo_name, digest)
except model.BlobDoesNotExist:
return None
return cls._blob(blob_record)
return _blob(blob_record)
@classmethod
def get_torrent_info(cls, blob):
def get_torrent_info(self, blob):
blob_record = model.storage.get_storage_by_uuid(blob.uuid)
try:
@ -250,51 +224,24 @@ class PreOCIModel(VerbsDataInterface):
piece_length=torrent_info.piece_length,
)
@classmethod
def set_torrent_info(cls, blob, piece_length, pieces):
def set_torrent_info(self, blob, piece_length, pieces):
blob_record = model.storage.get_storage_by_uuid(blob.uuid)
model.storage.save_torrent_info(blob_record, piece_length, pieces)
@classmethod
def lookup_derived_image(cls, repo_image, verb, varying_metadata=None):
def lookup_derived_image(self, repo_image, verb, varying_metadata=None):
blob_record = model.image.find_derived_storage_for_image(repo_image.internal_db_id, verb,
varying_metadata)
if blob_record is None:
return None
return cls._derived_image(blob_record, repo_image)
return _derived_image(blob_record, repo_image)
@classmethod
def _derived_image(cls, blob_record, repo_image):
return DerivedImage(
ref=repo_image.internal_db_id,
blob=cls._blob(blob_record),
internal_source_image_db_id=repo_image.internal_db_id,
)
def lookup_or_create_derived_image(self, repo_image, verb, location, varying_metadata=None):
blob_record = model.image.find_or_create_derived_storage(repo_image.internal_db_id, verb,
location, varying_metadata)
return _derived_image(blob_record, repo_image)
@classmethod
def _blob(cls, blob_record):
if hasattr(blob_record, 'locations'):
locations = blob_record.locations
else:
locations = model.storage.get_storage_locations(blob_record.uuid)
return Blob(
uuid=blob_record.uuid,
size=blob_record.image_size,
uncompressed_size=blob_record.uncompressed_size,
uploading=blob_record.uploading,
locations=locations,
)
@classmethod
def lookup_or_create_derived_image(cls, repo_image, verb, location, varying_metadata=None):
blob_record = model.image.find_or_create_derived_storage(repo_image.internal_db_id, verb, location,
varying_metadata)
return cls._derived_image(blob_record, repo_image)
@classmethod
def get_tag_image(cls, namespace_name, repo_name, tag_name):
def get_tag_image(self, namespace_name, repo_name, tag_name):
try:
found = model.tag.get_tag_image(namespace_name, repo_name, tag_name, include_storage=True)
except model.DataModelException:
@ -308,15 +255,68 @@ class PreOCIModel(VerbsDataInterface):
return ImageWithBlob(
image_id=found.docker_image_id,
blob=cls._blob(found.storage),
blob=_blob(found.storage),
repository=RepositoryReference(
namespace_name=namespace_name,
name=repo_name,
id=found.repository_id,
),
compat_metadata=metadata,
v1_metadata=cls._docker_v1_metadata(namespace_name, repo_name, found),
v1_metadata=_docker_v1_metadata(namespace_name, repo_name, found),
internal_db_id=found.id,
)
pre_oci_model = PreOCIModel()
def _docker_v1_metadata(namespace_name, repo_name, repo_image):
"""
Returns a DockerV1Metadata object for the given Pre-OCI repo_image under the
repository with the given namespace and name. Note that the namespace and
name are passed here as an optimization, and are *not checked* against the
image.
"""
return DockerV1Metadata(
namespace_name=namespace_name,
repo_name=repo_name,
image_id=repo_image.docker_image_id,
checksum=repo_image.v1_checksum,
compat_json=repo_image.v1_json_metadata,
created=repo_image.created,
comment=repo_image.comment,
command=repo_image.command,
# Note: These are not needed in verbs and are expensive to load, so we just skip them.
content_checksum=None,
parent_image_id=None,
)
def _derived_image(blob_record, repo_image):
"""
Returns a DerivedImage object for the given Pre-OCI data model blob and repo_image instance.
"""
return DerivedImage(
ref=repo_image.internal_db_id,
blob=_blob(blob_record),
internal_source_image_db_id=repo_image.internal_db_id,
)
def _blob(blob_record):
"""
Returns a Blob object for the given Pre-OCI data model blob instance.
"""
if hasattr(blob_record, 'locations'):
locations = blob_record.locations
else:
locations = model.storage.get_storage_locations(blob_record.uuid)
return Blob(
uuid=blob_record.uuid,
size=blob_record.image_size,
uncompressed_size=blob_record.uncompressed_size,
uploading=blob_record.uploading,
locations=locations,
)