From ef0d823fbfdac328bd3891623208b12af6b53933 Mon Sep 17 00:00:00 2001 From: Lubos Mjachky Date: Fri, 25 Aug 2023 20:32:48 +0200 Subject: [PATCH] Refactor the repository retrieval in get_dr_push closes #1275 --- CHANGES/1275.misc | 2 ++ pulp_container/app/registry_api.py | 56 ++++++++++++++++-------------- pulp_container/app/serializers.py | 2 +- 3 files changed, 32 insertions(+), 28 deletions(-) create mode 100644 CHANGES/1275.misc diff --git a/CHANGES/1275.misc b/CHANGES/1275.misc new file mode 100644 index 000000000..0048b1c62 --- /dev/null +++ b/CHANGES/1275.misc @@ -0,0 +1,2 @@ +Ensured repositories are correctly retrieved or initialized when handling concurrent requests +during the push operation. diff --git a/pulp_container/app/registry_api.py b/pulp_container/app/registry_api.py index cda1cbcf9..c76688e12 100644 --- a/pulp_container/app/registry_api.py +++ b/pulp_container/app/registry_api.py @@ -17,6 +17,7 @@ from django.core.files.storage import default_storage as storage from django.core.files.base import ContentFile, File +from django.core.exceptions import ObjectDoesNotExist from django.db import IntegrityError, transaction from django.shortcuts import get_object_or_404 @@ -25,7 +26,7 @@ from pulpcore.plugin.models import Artifact, ContentArtifact, UploadChunk from pulpcore.plugin.files import PulpTemporaryUploadedFile from pulpcore.plugin.tasking import add_and_remove, dispatch -from pulpcore.plugin.util import get_objects_for_user +from pulpcore.plugin.util import get_objects_for_user, get_url from rest_framework.exceptions import ( AuthenticationFailed, NotAuthenticated, @@ -291,32 +292,7 @@ def get_dr_push(self, request, path, create=False): distribution = models.ContainerDistribution.objects.get(base_path=path) except models.ContainerDistribution.DoesNotExist: if create: - try: - with transaction.atomic(): - repo_serializer = serializers.ContainerPushRepositorySerializer( - data={"name": path}, context={"request": request} - ) - repo_serializer.is_valid(raise_exception=True) - repository = repo_serializer.create(repo_serializer.validated_data) - repo_href = serializers.ContainerPushRepositorySerializer( - repository, context={"request": request} - ).data["pulp_href"] - - dist_serializer = serializers.ContainerDistributionSerializer( - data={"base_path": path, "name": path, "repository": repo_href} - ) - dist_serializer.is_valid(raise_exception=True) - distribution = dist_serializer.create(dist_serializer.validated_data) - except ValidationError: - raise RepositoryInvalid(name=path) - except IntegrityError: - # Seems like another process created our stuff already. Retry fetching it. - distribution = models.ContainerDistribution.objects.get(base_path=path) - repository = distribution.repository - if repository: - repository = repository.cast() - if not repository.PUSH_ENABLED: - raise RepositoryInvalid(name=path, message="Repository is read-only.") + distribution, repository = self.create_dr(path, request) else: raise RepositoryNotFound(name=path) else: @@ -349,6 +325,32 @@ def get_dr_push(self, request, path, create=False): raise RepositoryNotFound(name=path) return distribution, repository + @transaction.atomic + def create_dr(self, path, request): + try: + repository = serializers.ContainerPushRepositorySerializer.get_or_create({"name": path}) + except ObjectDoesNotExist: + # The repository of the push type could not be found or could not be created + raise RepositoryInvalid(name=path) + + dist_serializer = serializers.ContainerDistributionSerializer( + data={"base_path": path, "name": path, "repository": get_url(repository)}, + context={"request": request}, + ) + try: + dist_serializer.is_valid(raise_exception=True) + distribution = dist_serializer.create(dist_serializer.validated_data) + except (IntegrityError, ValidationError): + # seems like another process created our stuff already. Retry fetching it. + distribution = models.ContainerDistribution.objects.get(base_path=path) + repository = distribution.repository + if repository: + repository = repository.cast() + if not repository.PUSH_ENABLED: + raise RepositoryInvalid(name=path, message="Repository is read-only.") + + return distribution, repository + class BearerTokenView(APIView): """ diff --git a/pulp_container/app/serializers.py b/pulp_container/app/serializers.py index 87393a713..c10b42465 100644 --- a/pulp_container/app/serializers.py +++ b/pulp_container/app/serializers.py @@ -196,7 +196,7 @@ class Meta: model = models.ContainerRepository -class ContainerPushRepositorySerializer(RepositorySerializer): +class ContainerPushRepositorySerializer(RepositorySerializer, GetOrCreateSerializerMixin): """ Serializer for Container Push Repositories. """