From 0a37681df6ccb4bc467c3ba5f416a77ed2a6b80f Mon Sep 17 00:00:00 2001 From: Grant Gainey Date: Mon, 16 May 2022 08:32:45 -0400 Subject: [PATCH] Fleshed out rpm-content types. closes #505. --- CHANGES/505.feature | 1 + pulpcore/cli/rpm/content.py | 227 +++++++++++++++++----- pulpcore/cli/rpm/context.py | 63 ++++++ tests/conftest.py | 1 + tests/scripts/pulp_rpm/test_advisory.json | 33 ++++ tests/scripts/pulp_rpm/test_content.sh | 88 +++++++-- 6 files changed, 349 insertions(+), 64 deletions(-) create mode 100644 CHANGES/505.feature create mode 100644 tests/scripts/pulp_rpm/test_advisory.json diff --git a/CHANGES/505.feature b/CHANGES/505.feature new file mode 100644 index 000000000..9a55400d9 --- /dev/null +++ b/CHANGES/505.feature @@ -0,0 +1 @@ +Extended "rpm content" to cover all of the RPM content-types. diff --git a/pulpcore/cli/rpm/content.py b/pulpcore/cli/rpm/content.py index 556bb42f1..18e1b14a4 100644 --- a/pulpcore/cli/rpm/content.py +++ b/pulpcore/cli/rpm/content.py @@ -1,4 +1,4 @@ -from typing import IO, Optional, Union +from typing import Any, Optional, Union import click @@ -14,11 +14,24 @@ href_option, list_command, pulp_group, + pulp_option, show_command, + type_option, ) from pulpcore.cli.common.i18n import get_translation from pulpcore.cli.core.context import PulpArtifactContext -from pulpcore.cli.rpm.context import PulpRpmPackageContext +from pulpcore.cli.rpm.context import ( + PulpRpmAdvisoryContext, + PulpRpmDistributionTreeContext, + PulpRpmModulemdContext, + PulpRpmModulemdDefaultsContext, + PulpRpmPackageCategoryContext, + PulpRpmPackageContext, + PulpRpmPackageEnvironmentContext, + PulpRpmPackageGroupContext, + PulpRpmPackageLangpacksContext, + PulpRpmRepoMetadataFileContext, +) translation = get_translation(__name__) _ = translation.gettext @@ -52,74 +65,194 @@ def _sha256_artifact_callback( @pulp_group() -@click.option( - "-t", - "--type", - "content_type", - type=click.Choice(["package"], case_sensitive=False), +@type_option( + choices={ + "package": PulpRpmPackageContext, + "advisory": PulpRpmAdvisoryContext, + "distribution_tree": PulpRpmDistributionTreeContext, + "modulemd_defaults": PulpRpmModulemdDefaultsContext, + "modulemd": PulpRpmModulemdContext, + "package_category": PulpRpmPackageCategoryContext, + "package_environment": PulpRpmPackageEnvironmentContext, + "package_group": PulpRpmPackageGroupContext, + "package_langpack": PulpRpmPackageLangpacksContext, + "repo_metadata_file": PulpRpmRepoMetadataFileContext, + }, default="package", + case_sensitive=False, ) -@pass_pulp_context -@click.pass_context -def content(ctx: click.Context, pulp_ctx: PulpContext, content_type: str) -> None: - if content_type == "package": - ctx.obj = PulpRpmPackageContext(pulp_ctx) - else: - raise NotImplementedError() +def content() -> None: + pass list_options = [ - click.option("--arch"), - click.option("--arch-in", "arch__in"), - click.option("--epoch"), - click.option("--epoch-in", "epoch__in"), - click.option("--fields"), - click.option("--name"), - click.option("--name-in", "name__in"), - click.option("--package-href"), - click.option("--release"), - click.option("--release-in", "release__in"), - click.option("--repository-version"), - click.option("--version"), - click.option("--version-in", "version__in"), + pulp_option("--exclude_fields"), + pulp_option("--fields"), + pulp_option("--repository-version"), + pulp_option("--arch", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--arch-in", "arch__in", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--arch-ne", "arch__ne", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--epoch", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--epoch-in", "epoch__in", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--epoch-ne", "epoch__ne", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--id", allowed_with_contexts=(PulpRpmPackageContext, PulpRpmAdvisoryContext)), + pulp_option( + "--id-in", "id__in", allowed_with_contexts=(PulpRpmPackageContext, PulpRpmAdvisoryContext) + ), + pulp_option("--module", allowed_with_contexts=(PulpRpmModulemdDefaultsContext,)), + pulp_option( + "--module-in", "module__in", allowed_with_contexts=(PulpRpmModulemdDefaultsContext,) + ), + pulp_option("--name", allowed_with_contexts=(PulpRpmPackageContext, PulpRpmModulemdContext)), + pulp_option( + "--name-in", + "name__in", + allowed_with_contexts=(PulpRpmPackageContext, PulpRpmModulemdContext), + ), + pulp_option( + "--name-ne", + "name__ne", + allowed_with_contexts=(PulpRpmPackageContext, PulpRpmModulemdContext), + ), + pulp_option("--package-href", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--pkgId", allowed_with_contexts=(PulpRpmPackageContext, PulpRpmModulemdContext)), + pulp_option( + "--pkgId-in", + "pkgId__in", + allowed_with_contexts=(PulpRpmPackageContext, PulpRpmModulemdContext), + ), + pulp_option("--release", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--release-in", "release__in", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--release-ne", "release__ne", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--severity", allowed_with_contexts=(PulpRpmAdvisoryContext,)), + pulp_option("--severity-in", "severity__in", allowed_with_contexts=(PulpRpmAdvisoryContext,)), + pulp_option("--severity-ne", "severity__ne", allowed_with_contexts=(PulpRpmAdvisoryContext,)), + pulp_option( + "--sha256", + allowed_with_contexts=( + PulpRpmPackageContext, + PulpRpmModulemdDefaultsContext, + PulpRpmModulemdContext, + ), + ), + pulp_option("--status", allowed_with_contexts=(PulpRpmAdvisoryContext,)), + pulp_option("--status-in", "status__in", allowed_with_contexts=(PulpRpmAdvisoryContext,)), + pulp_option("--status-ne", "status__ne", allowed_with_contexts=(PulpRpmAdvisoryContext,)), + pulp_option( + "--stream", allowed_with_contexts=(PulpRpmModulemdDefaultsContext, PulpRpmModulemdContext) + ), + pulp_option( + "--stream-in", + "stream__in", + allowed_with_contexts=(PulpRpmModulemdDefaultsContext, PulpRpmModulemdContext), + ), + pulp_option("--type", allowed_with_contexts=(PulpRpmAdvisoryContext,)), + pulp_option("--type-in", "type__in", allowed_with_contexts=(PulpRpmAdvisoryContext,)), + pulp_option("--type-ne", "type__ne", allowed_with_contexts=(PulpRpmAdvisoryContext,)), + pulp_option("--version", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--version-in", "version__in", allowed_with_contexts=(PulpRpmPackageContext,)), + pulp_option("--version-ne", "version__ne", allowed_with_contexts=(PulpRpmPackageContext,)), ] lookup_options = [ href_option, - click.option("--relative-path", callback=_relative_path_callback, expose_value=False), - click.option("--sha256", callback=_sha256_callback, expose_value=False), + pulp_option( + "--relative-path", + callback=_relative_path_callback, + expose_value=False, + allowed_with_contexts=(PulpRpmPackageContext,), + ), + pulp_option( + "--sha256", + callback=_sha256_callback, + expose_value=False, + allowed_with_contexts=( + PulpRpmPackageContext, + PulpRpmModulemdDefaultsContext, + PulpRpmModulemdContext, + ), + ), ] + +content.add_command(list_command(decorators=list_options)) +content.add_command(show_command(decorators=lookup_options)) +# create assumes "there exists an Artifact..." +# create is defined for package, modulemd and modulemd_defaults. The implications for modulemd +# and modulemd_defaults are generally not what the user expects. Therefore, leaving those two +# endpoints hidden until we have a better handle on what we should really be doing. +# See https://github.com/pulp/pulp_rpm/issues/2229 and https://github.com/pulp/pulp_rpm/issues/2534 create_options = [ - click.option("--relative-path", required=True), - click.option( + pulp_option( + "--relative-path", + required=True, + allowed_with_contexts=(PulpRpmPackageContext,), + ), + pulp_option( "--sha256", "artifact", required=True, help=_("Digest of the artifact to use"), callback=_sha256_artifact_callback, + allowed_with_contexts=(PulpRpmPackageContext,), ), ] +content.add_command( + create_command( + decorators=create_options, + allowed_with_contexts=(PulpRpmPackageContext,), + ) +) -content.add_command(list_command(decorators=list_options)) -content.add_command(show_command(decorators=lookup_options)) -content.add_command(create_command(decorators=create_options)) - - -@content.command() -@click.option("--relative-path", required=True) -@click.option("--file", type=click.File("rb"), required=True) +# upload takes a file-argument and creates the entity from it. +# upload currently only works for advisory/package, +# see https://github.com/pulp/pulp_rpm/issues/2534 +@content.command( + allowed_with_contexts=( + PulpRpmPackageContext, + PulpRpmAdvisoryContext, + ) +) @chunk_size_option +@pulp_option( + "--relative-path", + required=True, + help=_("Relative path within a distribution of the entity"), + allowed_with_contexts=(PulpRpmPackageContext,), +) +@pulp_option( + "--file", + type=click.File("rb"), + required=True, + help=_("An RPM binary"), + allowed_with_contexts=(PulpRpmPackageContext,), +) +@pulp_option( + "--file", + type=click.File("rb"), + required=True, + help=_("An advisory JSON file."), + allowed_with_contexts=(PulpRpmAdvisoryContext,), +) @pass_entity_context @pass_pulp_context def upload( pulp_ctx: PulpContext, - entity_ctx: PulpRpmPackageContext, - relative_path: str, - file: IO[bytes], - chunk_size: int, + entity_ctx: Union[ + PulpRpmPackageContext, + PulpRpmAdvisoryContext, + ], + **kwargs: Any, ) -> None: - """Create an rpm package content unit by uploading a file""" - artifact_href = PulpArtifactContext(pulp_ctx).upload(file, chunk_size) - content = {"relative_path": relative_path, "artifact": artifact_href} - result = entity_ctx.create(body=content) + """Create a content unit by uploading a file""" + file = kwargs.pop("file") + body = {} + if isinstance(entity_ctx, PulpRpmPackageContext): + chunk_size = kwargs.pop("chunk_size") + artifact_ctx = PulpArtifactContext(pulp_ctx) + artifact_href = artifact_ctx.upload(file, chunk_size) + body["artifact"] = artifact_href + body.update(kwargs) + result = entity_ctx.create(body=body) + elif isinstance(entity_ctx, PulpRpmAdvisoryContext): + result = entity_ctx.create(body=body, uploads={"file": file.read()}) pulp_ctx.output_result(result) diff --git a/pulpcore/cli/rpm/context.py b/pulpcore/cli/rpm/context.py index 31515363c..08089c879 100644 --- a/pulpcore/cli/rpm/context.py +++ b/pulpcore/cli/rpm/context.py @@ -70,6 +70,69 @@ class PulpRpmPackageContext(PulpContentContext): ID_PREFIX = "content_rpm_packages" +class PulpRpmAdvisoryContext(PulpContentContext): + ENTITY = "rpm advisory" + ENTITIES = "rpm advisories" + HREF = "rpm_update_record_href" + ID_PREFIX = "content_rpm_advisories" + + +class PulpRpmDistributionTreeContext(PulpContentContext): + ENTITY = "rpm distribution tree" + ENTITIES = "rpm distribution trees" + HREF = "rpm_distribution_tree_href" + ID_PREFIX = "content_rpm_distribution_trees" + + +class PulpRpmModulemdDefaultsContext(PulpContentContext): + ENTITY = "rpm modulemd defaults" + ENTITIES = "rpm modulemd defaults" + HREF = "rpm_modulemd_defaults_href" + ID_PREFIX = "content_rpm_modulemd_defaults" + + +class PulpRpmModulemdContext(PulpContentContext): + ENTITY = "rpm modulemd" + ENTITIES = "rpm modulemds" + HREF = "rpm_modulemd_href" + ID_PREFIX = "content_rpm_modulemds" + + +class PulpRpmPackageCategoryContext(PulpContentContext): + ENTITY = "rpm package category" + ENTITIES = "rpm package categories" + HREF = "rpm_package_category_href" + ID_PREFIX = "content_rpm_packagecategories" + + +class PulpRpmPackageEnvironmentContext(PulpContentContext): + ENTITY = "rpm package environment" + ENTITIES = "rpm package environments" + HREF = "rpm_package_environment_href" + ID_PREFIX = "content_rpm_packageenvironments" + + +class PulpRpmPackageGroupContext(PulpContentContext): + ENTITY = "rpm package group" + ENTITIES = "rpm package groups" + HREF = "rpm_package_group_href" + ID_PREFIX = "content_rpm_packagegroups" + + +class PulpRpmPackageLangpacksContext(PulpContentContext): + ENTITY = "rpm package langpack" + ENTITIES = "rpm package langpacks" + HREF = "rpm_package_langpacks_href" + ID_PREFIX = "content_rpm_packagelangpacks" + + +class PulpRpmRepoMetadataFileContext(PulpContentContext): + ENTITY = "rpm repo metadata file" + ENTITIES = "rpm repo metadata files" + HREF = "rpm_repo_metadata_file_href" + ID_PREFIX = "content_rpm_repo_metadata_files" + + class PulpRpmPublicationContext(PulpEntityContext): ENTITY = _("rpm publication") ENTITIES = _("rpm publications") diff --git a/tests/conftest.py b/tests/conftest.py index 09539c6f6..9cbb15507 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,6 +17,7 @@ def pulp_cli_vars(pulp_cli_vars): "CONTAINER_REMOTE_URL": "https://quay.io", "CONTAINER_IMAGE": "libpod/alpine", "RPM_REMOTE_URL": urljoin(PULP_FIXTURES_URL, "/rpm-unsigned"), + "RPM_MODULES_REMOTE_URL": urljoin(PULP_FIXTURES_URL, "/rpm-with-modules"), "ANSIBLE_COLLECTION_REMOTE_URL": "https://galaxy.ansible.com/", "ANSIBLE_ROLE_REMOTE_URL": "https://galaxy.ansible.com/api/v1/roles/?namespace__name=elastic", # noqa "PYTHON_REMOTE_URL": PULP_FIXTURES_URL + "/python-pypi/", diff --git a/tests/scripts/pulp_rpm/test_advisory.json b/tests/scripts/pulp_rpm/test_advisory.json new file mode 100644 index 000000000..a8b13ec6e --- /dev/null +++ b/tests/scripts/pulp_rpm/test_advisory.json @@ -0,0 +1,33 @@ +{ + "issued": "2020-03-08 20:04:01", + "id": "CEBA-2022-124387", + "type": "Bug Fix Advisory", + "release": "1", + "version": "1", + "pkglist": [ + { + "packages": [ + { + "arch": "noarch", + "epoch": "0", + "filename": "bear-4.1-1.noarch.rpm", + "name": "bear", + "reboot_suggested": false, + "relogin_suggested": false, + "restart_suggested": false, + "release": "1", + "src": "http://www.fedoraproject.org", + "sum": "", + "sum_type": "", + "version": "4.1" + } + ] + } + ], + "severity": "", + "description": "Not available", + "reboot_suggested": false, + "updated": "2022-03-08 20:04:01", + "solution": "Not available", + "fromstr": "centos-announce@centos.org" +} diff --git a/tests/scripts/pulp_rpm/test_content.sh b/tests/scripts/pulp_rpm/test_content.sh index a71565077..37c6da7b0 100755 --- a/tests/scripts/pulp_rpm/test_content.sh +++ b/tests/scripts/pulp_rpm/test_content.sh @@ -8,48 +8,102 @@ pulp debug has-plugin --name "rpm" || exit 3 cleanup() { pulp rpm repository destroy --name "cli_test_rpm_repository" || true pulp rpm remote destroy --name "cli_test_rpm_remote" || true + pulp rpm repository destroy --name "cli_test_modular" || true + pulp rpm remote destroy --name "cli_test_modular" || true } trap cleanup EXIT -# Test rpm package upload +TEST_ADVISORY="$(dirname "$(realpath "$0")")"/test_advisory.json -wget "https://fixtures.pulpproject.org/rpm-modules-static-context/bear-4.1-1.noarch.rpm" +# Test rpm package upload +wget --no-check-certificate "${RPM_REMOTE_URL}/bear-4.1-1.noarch.rpm" expect_succ pulp rpm content upload --file "bear-4.1-1.noarch.rpm" --relative-path "bear-4.1-1.noarch.rpm" -PACKAGE_HREF=$(echo "$OUTPUT" | jq -r .pulp_href) -expect_succ pulp rpm content show --href "$PACKAGE_HREF" +PACKAGE_HREF=$(echo "${OUTPUT}" | jq -r .pulp_href) +expect_succ pulp rpm content show --href "${PACKAGE_HREF}" expect_succ pulp rpm remote create --name "cli_test_rpm_remote" --url "$RPM_REMOTE_URL" expect_succ pulp rpm remote show --name "cli_test_rpm_remote" -REMOTE_HREF=$(echo "$OUTPUT" | jq -r .pulp_href) -expect_succ pulp rpm repository create --name "cli_test_rpm_repository" --remote "$REMOTE_HREF" +REMOTE_HREF=$(echo "${OUTPUT}" | jq -r .pulp_href) +expect_succ pulp rpm repository create --name "cli_test_rpm_repository" --remote "${REMOTE_HREF}" expect_succ pulp rpm repository show --name "cli_test_rpm_repository" expect_succ pulp rpm repository content modify \ --repository "cli_test_rpm_repository" \ ---add-content "[{\"pulp_href\": \"$PACKAGE_HREF\"}]" +--add-content "[{\"pulp_href\": \"${PACKAGE_HREF}\"}]" expect_succ pulp rpm repository content list --repository "cli_test_rpm_repository" -test "$(echo "$OUTPUT" | jq -r '.[0].pulp_href')" = "$PACKAGE_HREF" +test "$(echo "${OUTPUT}" | jq -r '.[0].pulp_href')" = "${PACKAGE_HREF}" expect_succ pulp rpm repository content modify \ --repository "cli_test_rpm_repository" \ ---remove-content "[{\"pulp_href\": \"$PACKAGE_HREF\"}]" +--remove-content "[{\"pulp_href\": \"${PACKAGE_HREF}\"}]" expect_succ pulp rpm repository content list --repository "cli_test_rpm_repository" -test "$(echo "$OUTPUT" | jq -r length)" -eq "0" +test "$(echo "${OUTPUT}" | jq -r length)" -eq "0" expect_succ pulp rpm repository content add \ --repository "cli_test_rpm_repository" \ ---package-href "$PACKAGE_HREF" +--package-href "${PACKAGE_HREF}" expect_succ pulp rpm repository content list --repository "cli_test_rpm_repository" -test "$(echo "$OUTPUT" | jq -r '.[0].pulp_href')" = "$PACKAGE_HREF" +test "$(echo "${OUTPUT}" | jq -r '.[0].pulp_href')" = "${PACKAGE_HREF}" expect_succ pulp rpm repository content remove \ --repository "cli_test_rpm_repository" \ ---package-href "$PACKAGE_HREF" +--package-href "${PACKAGE_HREF}" expect_succ pulp rpm repository content list --repository "cli_test_rpm_repository" -test "$(echo "$OUTPUT" | jq -r length)" -eq "0" +test "$(echo "${OUTPUT}" | jq -r length)" -eq "0" expect_succ pulp rpm repository content modify \ --repository "cli_test_rpm_repository" \ ---remove-content "[{\"pulp_href\": \"$PACKAGE_HREF\"}]" -expect_succ pulp rpm repository destroy --name "cli_test_rpm_repository" -expect_succ pulp rpm remote destroy --name "cli_test_rpm_remote" +--remove-content "[{\"pulp_href\": \"${PACKAGE_HREF}\"}]" + +expect_succ pulp rpm remote create --name "cli_test_modular" --url "$RPM_MODULES_REMOTE_URL" +expect_succ pulp rpm repository create --name "cli_test_modular" --remote "cli_test_modular" +expect_succ pulp rpm repository sync --name "cli_test_modular" + +# test list and show for all types +for t in package advisory distribution_tree modulemd_defaults modulemd package_category package_environment package_group package_langpack repo_metadata_file +do + expect_succ pulp rpm content -t ${t} list --limit 100 + FOUND=$(echo "${OUTPUT}" | jq -r length) + case ${t} in + package) + test "${FOUND}" -eq "35" + ;; + advisory) + test "${FOUND}" -eq "6" + ;; + distribution_tree) + test "${FOUND}" -eq "0" + ;; + modulemd_defaults) + test "${FOUND}" -eq "3" + ;; + modulemd) + test "${FOUND}" -eq "10" + ;; + package_category) + test "${FOUND}" -eq "1" + ;; + package_environment) + test "${FOUND}" -eq "0" + ;; + package_group) + test "${FOUND}" -eq "2" + ;; + package_langpack) + test "${FOUND}" -eq "1" + ;; + repo_metadata_file) + test "${FOUND}" -eq "0" + ;; + *) + ;; + esac + if "${FOUND}" -gt "0" + then + ENTITY_HREF=$(echo "${OUTPUT}" | jq -r '.[0] | .pulp_href') + expect_succ pulp rpm content -t ${t} show --href "${ENTITY_HREF}" + fi +done + +# test upload for advisory, package-upload is tested at the start +expect_succ pulp rpm content -t advisory upload --file "${TEST_ADVISORY}"