diff --git a/pulp_docker/app/models.py b/pulp_docker/app/models.py index c9ebd1fd..9d2b9390 100644 --- a/pulp_docker/app/models.py +++ b/pulp_docker/app/models.py @@ -123,6 +123,9 @@ class BlobManifestBlob(models.Model): manifest_blob = models.ForeignKey( ManifestBlob, related_name='manifest_blobs', on_delete=models.CASCADE) + class Meta: + unique_together = ('manifest', 'manifest_blob') + class ManifestListManifest(models.Model): """ @@ -153,6 +156,9 @@ class ManifestListManifest(models.Model): manifest_list = models.ForeignKey( ManifestList, related_name='manifest_lists', on_delete=models.CASCADE) + class Meta: + unique_together = ('manifest', 'manifest_list') + class ManifestTag(Content): """ diff --git a/pulp_docker/app/serializers.py b/pulp_docker/app/serializers.py index 2e0f02a0..6813d594 100644 --- a/pulp_docker/app/serializers.py +++ b/pulp_docker/app/serializers.py @@ -8,11 +8,139 @@ from rest_framework.validators import UniqueValidator from pulpcore.plugin import serializers as platform -from pulpcore.plugin.models import Publication, Repository +from pulpcore.plugin.models import Artifact, Publication, Repository from . import models +class MinimalArtifactSerializer(platform.ArtifactSerializer): + """ + Serialize Artifacts associated with Docker Content. + + We overrided the platform serializer because it does not include size, and includes digest. + Since digest is a field on the content units, it would be redundant. + + Even though Docker content can only have 1 Artifact, this field should be used with + `many=True` because the Artifact is related with the ContentArtifact through table, and when + the same Artifact is served at multiple relative_paths, there are multiple associations of + between a given Content and Artifact. + """ + + class Meta: + fields = ('size', '_href') + model = Artifact + + +class ManifestListTagSerializer(platform.ContentSerializer): + """ + Serializer for ManifestListTags. + """ + + name = serializers.CharField(help_text="Tag name") + manifest_list = platform.DetailRelatedField( + many=False, + help_text="Manifest List that is tagged", + view_name='docker-manifest-lists-detail', + queryset=models.ManifestList.objects.all() + ) + artifacts = MinimalArtifactSerializer(many=True, help_text="File related to this content") + + class Meta: + fields = tuple( + set(platform.ContentSerializer.Meta.fields) - {'_artifacts'} + ) + ('name', 'manifest_list', 'artifacts') + model = models.ManifestListTag + + +class ManifestTagSerializer(platform.ContentSerializer): + """ + Serializer for ManifestTags. + """ + + name = serializers.CharField(help_text="Tag name") + manifest = platform.DetailRelatedField( + many=False, + help_text="Manifest that is tagged", + view_name='docker-manifests-detail', + queryset=models.ImageManifest.objects.all() + ) + artifacts = MinimalArtifactSerializer(many=True, help_text="File related to this content") + + class Meta: + fields = tuple( + set(platform.ContentSerializer.Meta.fields) - {'_artifacts'} + ) + ('name', 'manifest', 'artifacts') + model = models.ManifestTag + + +class ManifestListSerializer(platform.ContentSerializer): + """ + Serializer for ManifestLists. + """ + + digest = serializers.CharField(help_text="sha256 of the ManifestList file") + schema_version = serializers.IntegerField(help_text="Docker schema version") + media_type = serializers.CharField(help_text="Docker media type of the file") + manifests = platform.DetailRelatedField( + many=True, + help_text="Manifests that are referenced by this Manifest List", + view_name='docker-manifests-detail', + queryset=models.ImageManifest.objects.all() + ) + artifacts = MinimalArtifactSerializer(many=True, help_text="File related to this content") + + class Meta: + fields = tuple( + set(platform.ContentSerializer.Meta.fields) - {'_artifacts'} + ) + ('digest', 'schema_version', 'media_type', 'manifests', 'artifacts') + model = models.ManifestList + + +class ManifestSerializer(platform.ContentSerializer): + """ + Serializer for Manifests. + """ + + digest = serializers.CharField(help_text="sha256 of the Manifest file") + schema_version = serializers.IntegerField(help_text="Docker schema version") + media_type = serializers.CharField(help_text="Docker media type of the file") + blobs = platform.DetailRelatedField( + many=True, + help_text="Blobs that are referenced by this Manifest", + view_name='docker-blobs-detail', + queryset=models.ManifestBlob.objects.all() + ) + config_blob = platform.DetailRelatedField( + many=False, + help_text="Blob that contains configuration for this Manifest", + view_name='docker-blobs-detail', + queryset=models.ManifestBlob.objects.all() + ) + artifacts = MinimalArtifactSerializer(many=True, help_text="File related to this content") + + class Meta: + fields = tuple( + set(platform.ContentSerializer.Meta.fields) - {'_artifacts'} + ) + ('digest', 'schema_version', 'media_type', 'blobs', 'config_blob', 'artifacts') + model = models.ImageManifest + + +class BlobSerializer(platform.ContentSerializer): + """ + Serializer for Blobs. + """ + + digest = serializers.CharField(help_text="sha256 of the Blob file") + media_type = serializers.CharField(help_text="Docker media type of the file") + artifacts = MinimalArtifactSerializer(many=True, help_text="File related to this content") + + class Meta: + fields = tuple( + set(platform.ContentSerializer.Meta.fields) - {'_artifacts'} + ) + ('digest', 'media_type', 'artifacts') + model = models.ManifestBlob + + class RegistryPathField(serializers.CharField): """ Serializer Field for the registry_path field of the DockerDistribution. diff --git a/pulp_docker/app/viewsets.py b/pulp_docker/app/viewsets.py index 70a4b719..f495057d 100644 --- a/pulp_docker/app/viewsets.py +++ b/pulp_docker/app/viewsets.py @@ -5,6 +5,7 @@ http://docs.pulpproject.org/en/3.0/nightly/plugins/plugin-writer/index.html """ +from django.db import transaction from drf_yasg.utils import swagger_auto_schema from pulpcore.plugin.serializers import ( @@ -14,6 +15,7 @@ ) from pulpcore.plugin.tasking import enqueue_with_reservation from pulpcore.plugin.viewsets import ( + ContentViewSet, NamedModelViewSet, RemoteViewSet, OperationPostponedResponse, @@ -24,6 +26,91 @@ from . import models, serializers, tasks +class ManifestListTagViewSet(ContentViewSet): + """ + ViewSet for ManifestListTag. + """ + + endpoint_name = 'docker/manifest-list-tags' + queryset = models.ManifestListTag.objects.all() + serializer_class = serializers.ManifestListTagSerializer + + @transaction.atomic + def create(self, request): + """ + Create a new ManifestListTag from a request. + """ + raise NotImplementedError() + + +class ManifestTagViewSet(ContentViewSet): + """ + ViewSet for ManifestTag. + """ + + endpoint_name = 'docker/manifest-tags' + queryset = models.ManifestTag.objects.all() + serializer_class = serializers.ManifestTagSerializer + + @transaction.atomic + def create(self, request): + """ + Create a new ManifestTag from a request. + """ + raise NotImplementedError() + + +class ManifestListViewSet(ContentViewSet): + """ + ViewSet for ManifestList. + """ + + endpoint_name = 'docker/manifest-lists' + queryset = models.ManifestList.objects.all() + serializer_class = serializers.ManifestListSerializer + + @transaction.atomic + def create(self, request): + """ + Create a new ManifestList from a request. + """ + raise NotImplementedError() + + +class ManifestViewSet(ContentViewSet): + """ + ViewSet for Manifest. + """ + + endpoint_name = 'docker/manifests' + queryset = models.ImageManifest.objects.all() + serializer_class = serializers.ManifestSerializer + + @transaction.atomic + def create(self, request): + """ + Create a new Manifest from a request. + """ + raise NotImplementedError() + + +class BlobViewSet(ContentViewSet): + """ + ViewSet for ManifestBlobs. + """ + + endpoint_name = 'docker/blobs' + queryset = models.ManifestBlob.objects.all() + serializer_class = serializers.BlobSerializer + + @transaction.atomic + def create(self, request): + """ + Create a new ManifestBlob from a request. + """ + raise NotImplementedError() + + class DockerRemoteViewSet(RemoteViewSet): """ A ViewSet for DockerRemote.