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

Commit

Permalink
Problem: Publications are not needed for Docker plugin
Browse files Browse the repository at this point in the history
Solution: Add 'repository_version' field to DockerDistribution

This patch allows users to create DockerDistributions that serve repository versions. A distribution can
be created with either a repository or repository version defined. If a repository is defined, the latest
version of that repository is always available at the distribution. If a repository version is specified,
then that repository version is always available at that distribution.

This patch updates an existing test and adds a new one. The two tests assert that a docker or podman pull
can be performed when a repository is used in a distribution and when a repository version is used.

This patch also fixes the tests to work with docker as the client.

closes: #4669
https://pulp.plan.io/issues/4669
  • Loading branch information
dkliban committed Apr 17, 2019
1 parent c86a5b5 commit 202d3cd
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 194 deletions.
24 changes: 8 additions & 16 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -178,24 +178,16 @@ Look at the new Repository Version created
}
Publish a Repository Version and create a Publication
-----------------------------------------------------
Add a Docker Distribution to serve the latest Repository Version
----------------------------------------------------------------

``$ http POST :24817/pulp/api/v3/docker/publish/ repository=$REPO_HREF``

.. code:: json
{
"task": "/pulp/api/v3/tasks/fd4cbecd-6c6a-4197-9cbe-4e45b0516309/"
}
``$ export PUBLICATION_HREF=$(http :24817/pulp/api/v3/publications/ | jq -r '.results[0] | ._href')``

Add a Docker Distribution to serve your publication
---------------------------------------------------

``$ http POST http://localhost:24817/pulp/api/v3/docker-distributions/ name='baz' base_path='foo' publication=$PUBLICATION_HREF``
The Docker Distribution will serve the latest version of a Repository if the repository is
specified during creation/update of a Docker Distribution. The Docker Distribution will serve
a specific repository version if repository_version is provided when creating a Docker
Distribution. Either repository or repository_version can be set on a Docker Distribution, but not
both.

``$ http POST http://localhost:24817/pulp/api/v3/docker-distributions/ name='baz' base_path='foo' repository=$REPO_HREF``

.. code:: json
Expand Down
15 changes: 14 additions & 1 deletion pulp_docker/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.db import models

from pulpcore.plugin.download import DownloaderFactory
from pulpcore.plugin.models import BaseDistribution, Content, Remote
from pulpcore.plugin.models import BaseDistribution, Content, Remote, RepositoryVersion

from . import downloaders

Expand Down Expand Up @@ -287,5 +287,18 @@ class DockerDistribution(BaseDistribution):
A docker distribution defines how a publication is distributed by Pulp's webserver.
"""

repository_version = models.ForeignKey(RepositoryVersion, null=True, on_delete=models.CASCADE)

class Meta:
default_related_name = 'docker_distributions'

def get_repository_version(self):
"""
Returns the repository version that is supposed to be served by this DockerDistribution.
"""
if self.repository:
return RepositoryVersion.latest(self.repository)
elif self.repository_version:
return self.repository_version
else:
return None
14 changes: 8 additions & 6 deletions pulp_docker/app/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ async def tags_list(request):
path = request.match_info['path']
distribution = await Registry.match_distribution(path)
tags = {'name': path, 'tags': set()}
for c in distribution.publication.repository_version.content:
repository_version = distribution.get_repository_version()
for c in repository_version.content:
c = c.cast()
if isinstance(c, ManifestTag) or isinstance(c, ManifestListTag):
tags['tags'].add(c.name)
Expand All @@ -149,11 +150,12 @@ async def get_tag(request):
path = request.match_info['path']
tag_name = request.match_info['tag_name']
distribution = await Registry.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=distribution.publication.repository_version.content,
pk__in=repository_version.content,
name=tag_name
)
# If there is no manifest list tag, try again with manifest tag.
Expand All @@ -166,7 +168,7 @@ async def get_tag(request):
if MEDIA_TYPE.MANIFEST_V2 in accepted_media_types:
try:
tag = ManifestTag.objects.get(
pk__in=distribution.publication.repository_version.content,
pk__in=repository_version.content,
name=tag_name
)
except ObjectDoesNotExist:
Expand Down Expand Up @@ -211,11 +213,11 @@ async def get_by_digest(request):
path = request.match_info['path']
digest = "sha256:{digest}".format(digest=request.match_info['digest'])
distribution = await Registry.match_distribution(path)
repository_version = distribution.get_repository_version()
log.info(digest)
try:
ca = ContentArtifact.objects.get(
content__in=distribution.publication.repository_version.content,
relative_path=digest)
ca = ContentArtifact.objects.get(content__in=repository_version.content,
relative_path=digest)
headers = {'Content-Type': ca.content.cast().media_type}
except ObjectDoesNotExist:
raise PathNotResolved(path)
Expand Down
67 changes: 64 additions & 3 deletions pulp_docker/app/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
BaseDistributionSerializer,
DetailRelatedField,
IdentityField,
NestedRelatedField,
RemoteSerializer,
SingleArtifactContentSerializer,
)
Expand Down Expand Up @@ -182,8 +183,7 @@ class DockerDistributionSerializer(BaseDistributionSerializer):
view_name='docker-distributions-detail'
)
base_path = serializers.CharField(
help_text=_('The base (relative) path component of the published url. Avoid paths that \
overlap with other distribution base paths (e.g. "foo" and "foo/bar")'),
help_text=_('The base (relative) path that identifies the registry path.'),
validators=[validators.MaxLengthValidator(
models.DockerDistribution._meta.get_field('base_path').max_length,
message=_('Distribution base_path length must be less than {} characters').format(
Expand All @@ -197,7 +197,68 @@ class DockerDistributionSerializer(BaseDistributionSerializer):
help_text=_('The Registry hostame:port/name/ to use with docker pull command defined by '
'this distribution.')
)
repository_version = NestedRelatedField(
help_text=_('A URI of the repository version to be served by the Docker Distribution.'),
required=False,
label=_('Repository Version'),
queryset=models.RepositoryVersion.objects.all(),
view_name='versions-detail',
lookup_field='number',
parent_lookup_kwargs={'repository_pk': 'repository__pk'},
)

def validate(self, data):
"""
Validate the parameters for creating or updating Docker Distribution.
This method makes sure that only repository or a repository version is associated with a
Docker Distribution. It also validates that the base_path is a relative path.
Args:
data (dict): Dictionary of parameter value to validate
Returns:
Dict of validated data
Raises:
ValidationError if any of the validations fail.
"""
super().validate(data)
if 'repository' in data:
repository = data['repository']
elif self.instance:
repository = self.instance.repository
else:
repository = None

if 'repository_version' in data:
repository_version = data['repository_version']
elif self.instance:
repository_version = self.instance.repository_version
else:
repository_version = None

if repository and repository_version:
raise serializers.ValidationError({'repository': _("Repository can't be set if "
"repository_version is set also.")})
if 'publication' in data and data['publication']:
raise serializers.ValidationError({'publication': _("DockerDistributions don't serve "
"publications. A repository or "
"repository version should be "
"specified.")})
if 'publisher' in data and data['publisher']:
raise serializers.ValidationError({'publication': _("DockerDistributions don't work "
"with publishers. A repository or "
"a repository version should be "
"specified.")})
if 'base_path' in data and data['base_path']:
self._validate_relative_path(data['base_path'])

return data

class Meta:
model = models.DockerDistribution
fields = BaseDistributionSerializer.Meta.fields + ('base_path', 'registry_path')
fields = BaseDistributionSerializer.Meta.fields + ('base_path',
'registry_path',
'repository_version')
1 change: 0 additions & 1 deletion pulp_docker/app/tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
from .distribution import create, delete, update # noqa
from .publishing import publish # noqa
from .synchronize import synchronize # noqa
25 changes: 0 additions & 25 deletions pulp_docker/app/tasks/publishing.py

This file was deleted.

40 changes: 0 additions & 40 deletions pulp_docker/app/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@

from pulpcore.plugin.serializers import (
AsyncOperationResponseSerializer,
RepositoryPublishURLSerializer,
RepositorySyncURLSerializer,
)

from pulpcore.plugin.models import RepositoryVersion, Publication
from pulpcore.plugin.tasking import enqueue_with_reservation
from pulpcore.plugin.viewsets import (
ContentViewSet,
Expand Down Expand Up @@ -149,44 +147,6 @@ def sync(self, request, pk):
return OperationPostponedResponse(result, request)


class DockerPublicationViewSet(NamedModelViewSet, mixins.CreateModelMixin):
"""
A ViewSet for Docker Publication.
"""

endpoint_name = 'docker/publish'
queryset = Publication.objects.all()
serializer_class = RepositoryPublishURLSerializer

@swagger_auto_schema(
operation_description="Trigger an asynchronous task to create a docker publication",
responses={202: AsyncOperationResponseSerializer}
)
def create(self, request):
"""
Queues a task that publishes a new Docker Publication.
"""
serializer = RepositoryPublishURLSerializer(
data=request.data,
context={'request': request}
)
serializer.is_valid(raise_exception=True)
repository_version = serializer.validated_data.get('repository_version')

# Safe because version OR repository is enforced by serializer.
if not repository_version:
repository = serializer.validated_data.get('repository')
repository_version = RepositoryVersion.latest(repository)

result = enqueue_with_reservation(
tasks.publish,
[repository_version.repository],
kwargs={'repository_version_pk': str(repository_version.pk)}
)
return OperationPostponedResponse(result, request)


class DockerDistributionViewSet(NamedModelViewSet,
mixins.UpdateModelMixin,
mixins.RetrieveModelMixin,
Expand Down
80 changes: 0 additions & 80 deletions pulp_docker/tests/functional/api/test_publish.py

This file was deleted.

Loading

0 comments on commit 202d3cd

Please sign in to comment.