From 2b6fc72f185961d4c092f30acb1dad268954fc64 Mon Sep 17 00:00:00 2001 From: Tobias Grigo Date: Thu, 14 Nov 2024 16:31:42 +0100 Subject: [PATCH] Fixed PRC not getting removed when modifying repositories [noissue] --- pulp_deb/app/viewsets/repository.py | 38 +++++++++- pulp_deb/tests/functional/api/test_publish.py | 75 +++++++++++++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/pulp_deb/app/viewsets/repository.py b/pulp_deb/app/viewsets/repository.py index 477b07e9d..057d7866f 100644 --- a/pulp_deb/app/viewsets/repository.py +++ b/pulp_deb/app/viewsets/repository.py @@ -1,15 +1,21 @@ from gettext import gettext as _ # noqa +from django.conf import settings from drf_spectacular.utils import extend_schema from rest_framework.decorators import action from rest_framework import viewsets from rest_framework.serializers import ValidationError as DRFValidationError +from pulp_deb.app.models.content.content import Package +from pulp_deb.app.models.content.structure_content import PackageReleaseComponent from pulp_deb.app.serializers import AptRepositorySyncURLSerializer from pulpcore.plugin.util import extract_pk from pulpcore.plugin.actions import ModifyRepositoryActionMixin -from pulpcore.plugin.serializers import AsyncOperationResponseSerializer +from pulpcore.plugin.serializers import ( + AsyncOperationResponseSerializer, + RepositoryAddRemoveContentSerializer, +) from pulpcore.plugin.models import RepositoryVersion from pulpcore.plugin.tasking import dispatch from pulpcore.plugin.viewsets import ( @@ -22,7 +28,35 @@ from pulp_deb.app import models, serializers, tasks -class AptRepositoryViewSet(RepositoryViewSet, ModifyRepositoryActionMixin): +class AptModifyRepositoryActionMixin(ModifyRepositoryActionMixin): + @extend_schema( + description="Trigger an asynchronous task to create a new repository version.", + summary="Modify Repository Content", + responses={202: AsyncOperationResponseSerializer}, + ) + @action(detail=True, methods=["post"], serializer_class=RepositoryAddRemoveContentSerializer) + def modify(self, request, pk): + remove_content_units = request.data.get("remove_content_units", []) + package_hrefs = [href for href in remove_content_units if "/packages/" in href] + + if package_hrefs: + prc_hrefs = self._get_matching_prc_hrefs(package_hrefs) + remove_content_units.extend(prc_hrefs) + request.data["remove_content_units"] = remove_content_units + + return super().modify(request, pk) + + def _get_matching_prc_hrefs(self, package_hrefs): + # The serializer expects URIs so this is more of a hackish workaround. + base_url = f"{settings.V3_API_ROOT}content/deb/package_release_components/" + package_ids = [href.split("/")[-2] for href in package_hrefs] + matching_packages = Package.objects.filter(pulp_id__in=package_ids) + matching_components = PackageReleaseComponent.objects.filter(package__in=matching_packages) + prc_href = [f"{base_url}{component.pulp_id}/" for component in matching_components] + return prc_href + + +class AptRepositoryViewSet(AptModifyRepositoryActionMixin, RepositoryViewSet): # The doc string is a top level element of the user facing REST API documentation: """ An AptRepository is the locally stored, Pulp-internal representation of a APT repository. diff --git a/pulp_deb/tests/functional/api/test_publish.py b/pulp_deb/tests/functional/api/test_publish.py index 9108e7082..acb372549 100644 --- a/pulp_deb/tests/functional/api/test_publish.py +++ b/pulp_deb/tests/functional/api/test_publish.py @@ -12,6 +12,7 @@ DEB_FIXTURE_SINGLE_DIST, DEB_PACKAGE_INDEX_NAME, DEB_PACKAGE_NAME, + DEB_PACKAGE_RELEASE_COMPONENT_NAME, DEB_PUBLICATION_ARGS_ALL, DEB_PUBLICATION_ARGS_ONLY_SIMPLE, DEB_PUBLICATION_ARGS_ONLY_STRUCTURED, @@ -475,6 +476,80 @@ def test_publish_complex_dists( assert_equal_package_index(remote, published) +@pytest.mark.parallel +def test_remove_package_from_repository( + create_publication_and_verify_repo_version, + deb_get_content_types, + deb_modify_repository, + deb_get_repository_by_href, +): + """Test whether removing content in a structured publication removes all relevant content.""" + remote_args = {"distributions": DEB_FIXTURE_DISTRIBUTIONS} + _, repo, _, _ = create_publication_and_verify_repo_version( + remote_args, + publication_args=DEB_PUBLICATION_ARGS_ONLY_STRUCTURED, + is_modified=False, + ) + + package = deb_get_content_types( + "apt_package_api", DEB_PACKAGE_NAME, repo, repo.latest_version_href + )[0] + prcs = deb_get_content_types( + "apt_package_release_components_api", + DEB_PACKAGE_RELEASE_COMPONENT_NAME, + repo, + repo.latest_version_href, + ) + deb_modify_repository( + repo, + {"remove_content_units": [package.pulp_href], "base_version": repo.latest_version_href}, + ) + repo = deb_get_repository_by_href(repo.pulp_href) + prcs_new = deb_get_content_types( + "apt_package_release_components_api", + DEB_PACKAGE_RELEASE_COMPONENT_NAME, + repo, + repo.latest_version_href, + ) + + assert not any(package.pulp_href == prc.package for prc in prcs_new) + assert len(prcs_new) == len(prcs) - 1 + + +@pytest.mark.parallel +def test_remove_all_content_from_repository( + create_publication_and_verify_repo_version, + deb_get_content_types, + deb_modify_repository, + deb_get_repository_by_href, +): + """Test whether removing all content from a structured publication removes relevant content.""" + remote_args = {"distributions": DEB_FIXTURE_DISTRIBUTIONS} + _, repo, _, _ = create_publication_and_verify_repo_version( + remote_args, + publication_args=DEB_PUBLICATION_ARGS_ONLY_STRUCTURED, + is_modified=False, + ) + + package = deb_get_content_types( + "apt_package_api", DEB_PACKAGE_NAME, repo, repo.latest_version_href + )[0] + deb_modify_repository( + repo, + {"remove_content_units": ["*"]}, + ) + repo = deb_get_repository_by_href(repo.pulp_href) + prcs = deb_get_content_types( + "apt_package_release_components_api", + DEB_PACKAGE_RELEASE_COMPONENT_NAME, + repo, + repo.latest_version_href, + ) + + assert not any(package.pulp_href == prc.package for prc in prcs) + assert len(prcs) == 0 + + def assert_equal_package_index(orig, new): """In-detail check of two PackageIndex file-strings""" parsed_orig = parse_package_index(orig)