Skip to content

Commit

Permalink
Refactor CollectionVersion Upload to use pulpcore machinery
Browse files Browse the repository at this point in the history
fixes: #1175
  • Loading branch information
gerrod3 committed Aug 25, 2022
1 parent bde6984 commit fe2c1c7
Show file tree
Hide file tree
Showing 17 changed files with 502 additions and 204 deletions.
1 change: 1 addition & 0 deletions CHANGES/1175.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Properly return 400 error when trying to create/upload a duplicate Collection.
1 change: 1 addition & 0 deletions CHANGES/1175.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
An existing artifact or upload object can now be used to create a Collection.
5 changes: 5 additions & 0 deletions CHANGES/1176.removal
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Renamed CollectionVersion upload fields [namespace, name, version] to expected_[namespace, name, version].

Deprecated /ansible/collections/ upload endpoint. Use /pulp/api/v3/content/ansible/collection_versions/ instead.

Deprecated Galaxy V2 Collection upload endpoint. Use Galaxy V3 Collection Artifact upload endpoint instead.
8 changes: 8 additions & 0 deletions pulp_ansible/app/galaxy/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@ def _dispatch_import_collection_task(self, temp_file_pk, repository=None, **kwar
kwargs["repository_pk"] = repository.pk

return dispatch(import_collection, exclusive_resources=locks, kwargs=kwargs)

def init_content_data(self, serializer, request):
"""Add serializer's validated data to the task payload."""
# Honestly this seems like a bug in pulpcore
task_payload = {k: v for k, v in serializer.validated_data.items()}
task_payload.pop("file")
task_payload.update(super().init_content_data(serializer, request))
return task_payload
8 changes: 8 additions & 0 deletions pulp_ansible/app/galaxy/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from rest_framework.reverse import reverse
from rest_framework import serializers

from pulpcore.plugin.models import Artifact
from pulp_ansible.app.models import Collection, CollectionVersion, Role
from pulp_ansible.app.galaxy.v3.serializers import CollectionMetadataSerializer

Expand Down Expand Up @@ -187,3 +188,10 @@ class GalaxyCollectionUploadSerializer(serializers.Serializer):
file = serializers.FileField(
help_text=_("The file containing the Artifact binary data."), required=True
)

def validate(self, data):
"""Ensure duplicate artifact isn't uploaded."""
sha256 = data["file"].hashers["sha256"].hexdigest()
artifact = Artifact.objects.filter(sha256=sha256).first()
if artifact:
raise serializers.ValidationError(_("Artifact already exists"))
73 changes: 34 additions & 39 deletions pulp_ansible/app/galaxy/v3/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from django.views.generic.base import RedirectView
from django.conf import settings

from drf_spectacular.utils import OpenApiParameter, extend_schema
from drf_spectacular.utils import OpenApiParameter, extend_schema, inline_serializer
from jinja2 import Template
from rest_framework import mixins
from rest_framework.response import Response
Expand All @@ -26,10 +26,13 @@
from rest_framework.exceptions import NotFound
from rest_framework import status

from pulpcore.plugin.exceptions import DigestValidationError
from pulpcore.plugin.models import PulpTemporaryFile, Content
from pulpcore.plugin.models import Content
from pulpcore.plugin.serializers import AsyncOperationResponseSerializer
from pulpcore.plugin.viewsets import BaseFilterSet, OperationPostponedResponse
from pulpcore.plugin.viewsets import (
BaseFilterSet,
OperationPostponedResponse,
SingleArtifactContentUploadViewSet,
)
from pulpcore.plugin.tasking import add_and_remove, dispatch

from pulp_ansible.app.galaxy.v3.exceptions import ExceptionHandlerMixin
Expand All @@ -50,8 +53,8 @@
CollectionImport,
)
from pulp_ansible.app.serializers import (
CollectionOneShotSerializer,
CollectionImportDetailSerializer,
CollectionVersionUploadSerializer,
)

from pulp_ansible.app.galaxy.mixins import UploadGalaxyCollectionMixin
Expand Down Expand Up @@ -454,17 +457,30 @@ def urlpattern(*args, **kwargs):


class CollectionUploadViewSet(
ExceptionHandlerMixin, viewsets.GenericViewSet, UploadGalaxyCollectionMixin
ExceptionHandlerMixin, UploadGalaxyCollectionMixin, SingleArtifactContentUploadViewSet
):
"""
ViewSet for Collection Uploads.
"""

serializer_class = CollectionOneShotSerializer
queryset = CollectionVersion.objects.all()
serializer_class = CollectionVersionUploadSerializer
pulp_tag_name = "Pulp_Ansible: Artifacts Collections V3"

DEFAULT_ACCESS_POLICY = _PERMISSIVE_ACCESS_POLICY

_declared_fields = serializer_class._declared_fields
INLINE_SERIALIZER = inline_serializer(
"GalaxyCollectionUpload",
{
"file": serializers.FileField(required=True),
"expected_sha256": _declared_fields["expected_sha256"],
"expected_namespace": _declared_fields["expected_namespace"],
"expected_name": _declared_fields["expected_name"],
"expected_version": _declared_fields["expected_version"],
},
)

def urlpattern(*args, **kwargs):
"""Return url pattern for RBAC."""
return "pulp_ansible/v3/collections/upload"
Expand All @@ -473,52 +489,31 @@ def urlpattern(*args, **kwargs):
description="Create an artifact and trigger an asynchronous task to create "
"Collection content from it.",
summary="Upload a collection",
request=CollectionOneShotSerializer,
request=INLINE_SERIALIZER,
responses={202: AsyncOperationResponseSerializer},
)
def create(self, request, distro_base_path):
"""
Dispatch a Collection creation task.
"""
distro = get_object_or_404(AnsibleDistribution, base_path=distro_base_path)
serializer = self.get_serializer(data=request.data, context={"request": request})
serializer.is_valid(raise_exception=True)

expected_digests = {}
if serializer.validated_data["sha256"]:
expected_digests["sha256"] = serializer.validated_data["sha256"]
try:
temp_file = PulpTemporaryFile.init_and_validate(
serializer.validated_data["file"],
expected_digests=expected_digests,
)
except DigestValidationError:
repo = distro.repository or distro.repository_version.repository
if repo is None:
raise serializers.ValidationError(
_("The provided sha256 value does not match the sha256 of the uploaded file.")
_("Distribution must have either repository or repository_version set")
)

temp_file.save()
request.data["repository"] = reverse("repositories-ansible/ansible-detail", args=[repo.pk])
async_response = super().create(request)
# Is there a href-to-pk util somewhere?
task = async_response.data["task"].rstrip("/").rsplit("/", maxsplit=1)[-1]

kwargs = {}

if serializer.validated_data["expected_namespace"]:
kwargs["expected_namespace"] = serializer.validated_data["expected_namespace"]

if serializer.validated_data["expected_name"]:
kwargs["expected_name"] = serializer.validated_data["expected_name"]

if serializer.validated_data["expected_version"]:
kwargs["expected_version"] = serializer.validated_data["expected_version"]

async_result = self._dispatch_import_collection_task(
temp_file.pk, distro.repository, **kwargs
)
CollectionImport.objects.create(task_id=async_result.pk)
CollectionImport.objects.create(task_id=task)

data = {
"task": reverse(
settings.ANSIBLE_URL_NAMESPACE + "collection-imports-detail",
kwargs={"pk": async_result.pk},
kwargs={"pk": task},
request=None,
)
}
Expand All @@ -534,7 +529,7 @@ class LegacyCollectionUploadViewSet(CollectionUploadViewSet):
description="Create an artifact and trigger an asynchronous task to create "
"Collection content from it.",
summary="Upload a collection",
request=CollectionOneShotSerializer,
request=CollectionUploadViewSet.INLINE_SERIALIZER,
responses={202: AsyncOperationResponseSerializer},
deprecated=True,
)
Expand Down
2 changes: 2 additions & 0 deletions pulp_ansible/app/galaxy/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.conf import settings
from django.shortcuts import get_object_or_404, HttpResponse
from drf_spectacular.utils import extend_schema
from rest_framework import generics, pagination, response, views

from pulpcore.plugin.models import PulpTemporaryFile
Expand Down Expand Up @@ -167,6 +168,7 @@ def get_queryset(self):
"""
return Collection.objects.filter(versions__pk__in=self._distro_content).distinct()

@extend_schema(deprecated=True)
def post(self, request, path):
"""
Queues a task that creates a new Collection from an uploaded artifact.
Expand Down
Loading

0 comments on commit fe2c1c7

Please sign in to comment.