From 93992b8c28bdaedc747c7f88cc64e177c3800cf9 Mon Sep 17 00:00:00 2001 From: Grant Gainey Date: Tue, 4 Jun 2024 16:54:59 -0400 Subject: [PATCH] Add support for new pulp_rpm "prune-packages" feature. closes #979. --- CHANGES/979.feature | 3 + pulp-glue/pulp_glue/rpm/context.py | 18 ++++++ pulpcore/cli/rpm/__init__.py | 2 + pulpcore/cli/rpm/prune.py | 90 ++++++++++++++++++++++++++++ tests/scripts/pulp_rpm/test_prune.sh | 37 ++++++++++++ 5 files changed, 150 insertions(+) create mode 100644 CHANGES/979.feature create mode 100644 pulpcore/cli/rpm/prune.py create mode 100755 tests/scripts/pulp_rpm/test_prune.sh diff --git a/CHANGES/979.feature b/CHANGES/979.feature new file mode 100644 index 000000000..6b31d5021 --- /dev/null +++ b/CHANGES/979.feature @@ -0,0 +1,3 @@ +Added the "pulp rpm prune-packages" command to support new RPM feature. + +See [2909](https://github.com/pulp/pulp_rpm/issues/2909) for details. diff --git a/pulp-glue/pulp_glue/rpm/context.py b/pulp-glue/pulp_glue/rpm/context.py index 8a6925765..f61367f36 100644 --- a/pulp-glue/pulp_glue/rpm/context.py +++ b/pulp-glue/pulp_glue/rpm/context.py @@ -14,6 +14,7 @@ PulpRemoteContext, PulpRepositoryContext, PulpRepositoryVersionContext, + PulpViewSetContext, ) from pulp_glue.common.i18n import get_translation @@ -356,3 +357,20 @@ def sync(self, body: t.Optional[EntityDefinition] = None) -> t.Any: ) return super().sync(body) + + +class PulpRpmPruneContext(PulpViewSetContext): + ID_PREFIX: t.ClassVar[str] = "rpm_prune" + NEEDS_PLUGINS = [PluginRequirement("rpm", specifier=">=3.27.0.dev")] + + def prune_packages( + self, + repo_hrefs: t.List[t.Union[str, PulpRpmRepositoryContext]], + keep_days: t.Optional[int], + dry_run: t.Optional[bool], + ) -> t.Any: + body: t.Dict[str, t.Any] = {} + body["repo_hrefs"] = repo_hrefs + body["keep_days"] = keep_days + body["dry_run"] = dry_run + return self.call("prune_packages", body=body) diff --git a/pulpcore/cli/rpm/__init__.py b/pulpcore/cli/rpm/__init__.py index a398316ca..3a273f8d2 100644 --- a/pulpcore/cli/rpm/__init__.py +++ b/pulpcore/cli/rpm/__init__.py @@ -9,6 +9,7 @@ from pulpcore.cli.rpm.comps import comps_upload from pulpcore.cli.rpm.content import content from pulpcore.cli.rpm.distribution import distribution +from pulpcore.cli.rpm.prune import prune_packages from pulpcore.cli.rpm.publication import publication from pulpcore.cli.rpm.remote import remote from pulpcore.cli.rpm.repository import repository @@ -35,4 +36,5 @@ def mount(main: click.Group, **kwargs: t.Any) -> None: ) ) rpm.add_command(comps_upload) + rpm.add_command(prune_packages) main.add_command(rpm) diff --git a/pulpcore/cli/rpm/prune.py b/pulpcore/cli/rpm/prune.py new file mode 100644 index 000000000..598e2abf0 --- /dev/null +++ b/pulpcore/cli/rpm/prune.py @@ -0,0 +1,90 @@ +import gettext +import typing as t + +import click +from pulp_glue.common.context import PulpException +from pulp_glue.rpm.context import PulpRpmPruneContext, PulpRpmRepositoryContext + +from pulpcore.cli.common.generic import ( + PulpCLIContext, + pass_pulp_context, + pulp_command, + resource_option, +) + +_ = gettext.gettext + +multi_repository_option = resource_option( + "--repository", + "repositories", + default_plugin="rpm", + default_type="rpm", + context_table={"rpm:rpm": PulpRpmRepositoryContext}, + multiple=True, + href_pattern=PulpRpmRepositoryContext.HREF_PATTERN, + help=_( + "RPM Repository to prune, in the form 'rpm:rpm:' or by href." + " Can be called multiple times." + ), +) + + +@pulp_command() +@multi_repository_option +@click.option( + "--all-repositories", + type=bool, + is_flag=True, + show_default=True, + default=False, + help=_("Prune *all* repositories accessible to the invoking user."), +) +@click.option( + "--keep-days", + type=int, + default=14, + help=_("Prune packages that were added to the specified repositories more than N days ago."), +) +@click.option( + "--dry-run", + type=bool, + is_flag=True, + show_default=True, + default=False, + help=_("Evaluate the prune-status of the specified repositories but DO NOT make any changes."), +) +@pass_pulp_context +def prune_packages( + pulp_ctx: PulpCLIContext, + repositories: t.Iterable[PulpRpmRepositoryContext], + all_repositories: t.Optional[bool], + keep_days: t.Optional[int], + dry_run: t.Optional[bool], +) -> None: + """ + Prune older Packages from the current-version of a repository/repositories. + + Repositories can be specified by repeated --repository arguments. + + "All" repositories can be specified by --all-repositories. + + At least one repository, or --all-repositories, must be specified. + + You may not specify --all-repositories *and* one or more specific repositories. + """ + prune_ctx = PulpRpmPruneContext(pulp_ctx) + if not (all_repositories or repositories): + raise PulpException( + _("at least one --repository, or --all-repositories, must be specified") + ) + elif all_repositories and repositories: + raise PulpException( + _("cannot specify --all-repositories and --repository at the same time") + ) + + repos_list: t.List[t.Union[str, PulpRpmRepositoryContext]] = ( + ["*"] if all_repositories else list(repositories) + ) + + result = prune_ctx.prune_packages(repos_list, keep_days, dry_run) + pulp_ctx.output_result(result) diff --git a/tests/scripts/pulp_rpm/test_prune.sh b/tests/scripts/pulp_rpm/test_prune.sh new file mode 100755 index 000000000..f0a441d95 --- /dev/null +++ b/tests/scripts/pulp_rpm/test_prune.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# shellcheck source=tests/scripts/config.source +. "$(dirname "$(dirname "$(realpath "$0")")")"/config.source + +pulp debug has-plugin --name "rpm" --specifier ">=3.27.0.dev" || exit 23 + +cleanup() { + pulp rpm repository destroy --name "cli_test_rpm_prune" || true + pulp rpm repository destroy --name "cli_test_rpm_prune_2" || true + pulp rpm remote destroy --name "cli_test_rpm_prune" || true +} +trap cleanup EXIT + +expect_succ pulp rpm remote create --name "cli_test_rpm_prune" --url "$RPM_REMOTE_URL" --policy on_demand +expect_succ pulp rpm repository create --name "cli_test_rpm_prune" --remote "cli_test_rpm_prune" --no-autopublish +repo_href="$(echo "$OUTPUT" | jq -r '.pulp_href')" +expect_succ pulp rpm repository create --name "cli_test_rpm_prune_2" --remote "cli_test_rpm_prune" --no-autopublish +repo_href_2="$(echo "$OUTPUT" | jq -r '.pulp_href')" +expect_succ pulp rpm repository sync --repository "cli_test_rpm_prune" +expect_succ pulp rpm repository sync --repository "cli_test_rpm_prune_2" + +expect_fail pulp rpm prune-packages +expect_fail pulp rpm prune-packages --repository "rpm:rpm:cli_test_rpm_prune" --all-repositories +expect_fail pulp rpm prune-packages --repository "rpm:rpm:cli_test_rpm_prune" --keep-days foo +expect_fail pulp rpm prune-packages --repository "rpm:rpm:cli_test_rpm_prune" --concurrency bar + +expect_succ pulp rpm prune-packages --repository "rpm:rpm:cli_test_rpm_prune" --keep-days 0 --dry-run +expect_succ pulp rpm prune-packages --repository "${repo_href}" --keep-days 0 --dry-run +expect_succ pulp rpm prune-packages --repository "${repo_href}" --repository "${repo_href_2}" --dry-run +expect_succ pulp rpm prune-packages --repository "rpm:rpm:cli_test_rpm_prune" --repository "rpm:rpm:cli_test_rpm_prune_2" --dry-run +expect_succ pulp rpm prune-packages --repository "rpm:rpm:cli_test_rpm_prune" --repository "${repo_href}" --repository "${repo_href_2}" --dry-run +expect_succ pulp rpm prune-packages --all-repositories --dry-run +expect_succ pulp rpm prune-packages --repository "rpm:rpm:cli_test_rpm_prune" --keep-days 0 + + +