Skip to content
This repository has been archived by the owner on Dec 7, 2022. It is now read-only.

Commit

Permalink
Combine manifest models, and manifest-tag models
Browse files Browse the repository at this point in the history
There was not a need to treat manifest lists differently than regular
manifests, so we simplify the code (and future 2to3 migration) by
combining them.

https://pulp.plan.io/issues/4994
fixes #4994
  • Loading branch information
asmacdo committed Jun 26, 2019
1 parent 0b838ab commit 9ad8b99
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 261 deletions.
1 change: 1 addition & 0 deletions CHANGES/4994.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Combine manifest-list and manifest models, as well as manifest-list-tag and manifest-tag models.
2 changes: 1 addition & 1 deletion docs/_static/api.json

Large diffs are not rendered by default.

91 changes: 25 additions & 66 deletions pulp_docker/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Meta:
unique_together = ('digest',)


class ImageManifest(Content):
class Manifest(Content):
"""
A docker manifest.
Expand All @@ -64,6 +64,12 @@ class ImageManifest(Content):
digest (models.CharField): The manifest digest.
schema_version (models.IntegerField): The docker schema version.
media_type (models.CharField): The manifest media type.
Relations:
blobs (models.ManyToManyField): Many-to-many relationship with ManifestBlob.
config_blob (models.ForeignKey): Blob that contains configuration for this Manifest.
listed_manifests (models.ManyToManyField): Many-to-many relationship with Manifest. This
field is used only for a manifest-list type Manifests.
"""

TYPE = 'manifest'
Expand All @@ -75,42 +81,20 @@ class ImageManifest(Content):
choices=(
(MEDIA_TYPE.MANIFEST_V1, MEDIA_TYPE.MANIFEST_V1),
(MEDIA_TYPE.MANIFEST_V2, MEDIA_TYPE.MANIFEST_V2),
(MEDIA_TYPE.MANIFEST_LIST, MEDIA_TYPE.MANIFEST_LIST),
))

blobs = models.ManyToManyField(ManifestBlob, through='BlobManifestBlob')
config_blob = models.ForeignKey(ManifestBlob, related_name='config_blob',
null=True, on_delete=models.CASCADE) # through table?

class Meta:
unique_together = ('digest',)
null=True, on_delete=models.CASCADE)


class ManifestList(Content):
"""
A manifest list.
This content has one artifact.
Fields:
digest (models.CharField): The manifest digest.
schema_version (models.IntegerField): The docker schema version.
media_type (models.CharField): The manifest media type.
Relations:
manifests (models.ManyToManyField): Many-to-many relationship with Manifest.
"""

TYPE = 'manifest-list'

digest = models.CharField(max_length=255)
schema_version = models.IntegerField()
media_type = models.CharField(
max_length=60,
choices=(
(MEDIA_TYPE.MANIFEST_LIST, MEDIA_TYPE.MANIFEST_LIST),
))

manifests = models.ManyToManyField(ImageManifest, through='ManifestListManifest')
# Order matters for through fields, (source, target)
listed_manifests = models.ManyToManyField(
"self",
through='ManifestListManifest',
symmetrical=False,
through_fields=('image_manifest', 'manifest_list')
)

class Meta:
unique_together = ('digest',)
Expand All @@ -122,7 +106,7 @@ class BlobManifestBlob(models.Model):
"""

manifest = models.ForeignKey(
ImageManifest, related_name='blob_manifests', on_delete=models.CASCADE)
Manifest, related_name='blob_manifests', on_delete=models.CASCADE)
manifest_blob = models.ForeignKey(
ManifestBlob, related_name='manifest_blobs', on_delete=models.CASCADE)

Expand Down Expand Up @@ -154,13 +138,13 @@ class ManifestListManifest(models.Model):
features = models.TextField(default='', blank=True)
variant = models.CharField(max_length=255)

manifest = models.ForeignKey(
ImageManifest, related_name='manifests', on_delete=models.CASCADE)
image_manifest = models.ForeignKey(
Manifest, related_name='image_manifests', on_delete=models.CASCADE)
manifest_list = models.ForeignKey(
ManifestList, related_name='manifest_lists', on_delete=models.CASCADE)
Manifest, related_name='manifest_lists', on_delete=models.CASCADE)

class Meta:
unique_together = ('manifest', 'manifest_list')
unique_together = ('image_manifest', 'manifest_list')


class ManifestTag(Content):
Expand All @@ -171,45 +155,20 @@ class ManifestTag(Content):
name (models.CharField): The tag name.
Relations:
manifest (models.ForeignKey): A referenced Manifest.
tagged_manifest (models.ForeignKey): A referenced Manifest.
"""

TYPE = 'manifest-tag'

name = models.CharField(max_length=255, db_index=True)

manifest = models.ForeignKey(
ImageManifest, null=True, related_name='manifest_tags', on_delete=models.CASCADE)

class Meta:
unique_together = (
('name', 'manifest'),
)


class ManifestListTag(Content):
"""
A tagged Manifest List.
Fields:
name (models.CharField): The tag name.
Relations:
manifest_list (models.ForeignKey): A referenced Manifest List.
"""

TYPE = 'manifest-list-tag'

name = models.CharField(max_length=255, db_index=True)

manifest_list = models.ForeignKey(
ManifestList, null=True, related_name='manifest_list_tags', on_delete=models.CASCADE)
tagged_manifest = models.ForeignKey(
Manifest, null=True, related_name='tagged_manifests', on_delete=models.CASCADE)

class Meta:
unique_together = (
('name', 'manifest_list'),
('name', 'tagged_manifest'),
)


Expand Down
53 changes: 21 additions & 32 deletions pulp_docker/app/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from pulpcore.plugin.content import Handler, PathNotResolved
from pulpcore.plugin.models import ContentArtifact
from pulp_docker.app.models import DockerDistribution, ManifestTag, ManifestListTag, MEDIA_TYPE
from pulp_docker.app.models import DockerDistribution, ManifestTag, MEDIA_TYPE


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -105,7 +105,7 @@ async def tags_list(self, request):
repository_version = distribution.get_repository_version()
for c in repository_version.content:
c = c.cast()
if isinstance(c, ManifestTag) or isinstance(c, ManifestListTag):
if isinstance(c, ManifestTag):
tags['tags'].add(c.name)
tags['tags'] = list(tags['tags'])
return web.json_response(tags)
Expand All @@ -131,45 +131,34 @@ async def get_tag(self, request):
distribution = self._match_distribution(path)
repository_version = distribution.get_repository_version()
accepted_media_types = await Registry.get_accepted_media_types(request)
if MEDIA_TYPE.MANIFEST_LIST in accepted_media_types:
try:
tag = ManifestListTag.objects.get(
pk__in=repository_version.content,
name=tag_name
)
# If there is no manifest list tag, try again with manifest tag.
except ObjectDoesNotExist:
pass
else:
response_headers = {'Content-Type': MEDIA_TYPE.MANIFEST_LIST}
return await Registry.dispatch_tag(tag, response_headers)

if MEDIA_TYPE.MANIFEST_V2 in accepted_media_types:
try:
tag = ManifestTag.objects.get(
pk__in=repository_version.content,
name=tag_name,
manifest__schema_version=2
)
except ObjectDoesNotExist:
pass
else:
response_headers = {'Content-Type': MEDIA_TYPE.MANIFEST_V2}
return await Registry.dispatch_tag(tag, response_headers)

try:
tag = ManifestTag.objects.get(
pk__in=repository_version.content,
name=tag_name,
manifest__schema_version=1
)
except ObjectDoesNotExist:
# This is where we could eventually support on-the-fly conversion to schema 1.
log.warn("Client does not accept Docker V2 Schema 2 and is not currently supported.")
raise PathNotResolved(tag_name)

if tag.tagged_manifest.media_type == MEDIA_TYPE.MANIFEST_V1:
return_media_type = MEDIA_TYPE.MANIFEST_V1_SIGNED

elif tag.tagged_manifest.media_type in accepted_media_types:
return_media_type = tag.tagged_manifest.media_type
else:
response_headers = {'Content-Type': MEDIA_TYPE.MANIFEST_V1_SIGNED}
return await Registry.dispatch_tag(tag, response_headers)
# This is where we could eventually support on-the-fly conversion to schema 1.
log.warn(
"The requested tag `{name}` is of type {media_type}, but the client only accepts "
"{accepted_media_types}.".format(
name=tag.name,
media_type=tag.tagged_manifest.media_type,
accepted_media_types=accepted_media_types
)
)
raise PathNotResolved(tag_name)

response_headers = {'Content-Type': return_media_type}
return await Registry.dispatch_tag(tag, response_headers)

@staticmethod
async def dispatch_tag(tag, response_headers):
Expand Down
61 changes: 11 additions & 50 deletions pulp_docker/app/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,81 +14,41 @@
from . import models


class ManifestListTagSerializer(SingleArtifactContentSerializer):
"""
Serializer for ManifestListTags.
"""

name = serializers.CharField(help_text="Tag name")
manifest_list = DetailRelatedField(
many=False,
help_text="Manifest List that is tagged",
view_name='docker-manifest-lists-detail',
queryset=models.ManifestList.objects.all()
)

class Meta:
fields = SingleArtifactContentSerializer.Meta.fields + (
'name',
'manifest_list',
)
model = models.ManifestListTag


class ManifestTagSerializer(SingleArtifactContentSerializer):
"""
Serializer for ManifestTags.
"""

name = serializers.CharField(help_text="Tag name")
manifest = DetailRelatedField(
tagged_manifest = DetailRelatedField(
many=False,
help_text="Manifest that is tagged",
view_name='docker-manifests-detail',
queryset=models.ImageManifest.objects.all()
queryset=models.Manifest.objects.all()
)

class Meta:
fields = SingleArtifactContentSerializer.Meta.fields + (
'name',
'manifest',
'tagged_manifest',
)
model = models.ManifestTag


class ManifestListSerializer(SingleArtifactContentSerializer):
class ManifestSerializer(SingleArtifactContentSerializer):
"""
Serializer for ManifestLists.
Serializer for Manifests.
"""

digest = serializers.CharField(help_text="sha256 of the ManifestList file")
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")
manifests = DetailRelatedField(
listed_manifests = DetailRelatedField(
many=True,
help_text="Manifests that are referenced by this Manifest List",
view_name='docker-manifests-detail',
queryset=models.ImageManifest.objects.all()
queryset=models.Manifest.objects.all()
)

class Meta:
fields = SingleArtifactContentSerializer.Meta.fields + (
'digest',
'schema_version',
'media_type',
'manifests',
)
model = models.ManifestList


class ManifestSerializer(SingleArtifactContentSerializer):
"""
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 = DetailRelatedField(
many=True,
help_text="Blobs that are referenced by this Manifest",
Expand All @@ -107,10 +67,11 @@ class Meta:
'digest',
'schema_version',
'media_type',
'blobs',
'listed_manifests',
'config_blob',
'blobs',
)
model = models.ImageManifest
model = models.Manifest


class BlobSerializer(SingleArtifactContentSerializer):
Expand Down
Loading

0 comments on commit 9ad8b99

Please sign in to comment.