diff --git a/CHANGES/362.feature b/CHANGES/362.feature new file mode 100644 index 000000000..1dd6ea874 --- /dev/null +++ b/CHANGES/362.feature @@ -0,0 +1 @@ +Added upload and show commands for Ansible Roles and Collection-Version content diff --git a/pulpcore/cli/ansible/content.py b/pulpcore/cli/ansible/content.py index 8a6b13871..560993f43 100644 --- a/pulpcore/cli/ansible/content.py +++ b/pulpcore/cli/ansible/content.py @@ -1,14 +1,29 @@ import gettext +from typing import IO, Any, Optional, Union import click from pulpcore.cli.ansible.context import PulpAnsibleCollectionVersionContext, PulpAnsibleRoleContext -from pulpcore.cli.common.context import PulpContext, pass_pulp_context -from pulpcore.cli.common.generic import list_command +from pulpcore.cli.common.context import PulpContext, pass_entity_context, pass_pulp_context +from pulpcore.cli.common.generic import ( + GroupOption, + chunk_size_option, + href_option, + list_command, + pulp_option, + show_command, +) +from pulpcore.cli.core.context import PulpArtifactContext _ = gettext.gettext +def _content_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any: + if value: + ctx.obj.entity = value # The context should be already set based on the type option + return value + + @click.group() @click.option( "-t", @@ -28,4 +43,96 @@ def content(ctx: click.Context, pulp_ctx: PulpContext, content_type: str) -> Non raise NotImplementedError() -content.add_command(list_command()) +list_options = [ + pulp_option("--name", help=_("Name of {entity}")), + pulp_option("--namespace", help=_("Namespace of {entity}")), + pulp_option("--version", help=_("Version of {entity}")), + pulp_option( + "--only-highest", + "is_highest", + is_flag=True, + default=None, + help=_( + "Only show highest version of collection version (only works for collection versions)" + ), + ), + pulp_option( + "--tags", + help=_( + "Comma separated list of tags that must all match (only works for collection-versions)" + ), + ), +] + +fields_options = [ + pulp_option("--fields", help=_("String list of fields to include in the result")), + pulp_option( + "--exclude-fields", + default="files,manifest,docs_blob", + help=_("String list of fields to exclude from result"), + ), +] + +lookup_options = [ + click.option( + "--name", + help=_("Name of {entity}"), + group=["namespace", "version"], + expose_value=False, + cls=GroupOption, + callback=_content_callback, + ), + click.option( + "--namespace", + help=_("Namespace of {entity}"), + group=["name", "version"], + expose_value=False, + cls=GroupOption, + ), + click.option( + "--version", + help=_("Version of {entity}"), + group=["namespace", "name"], + expose_value=False, + cls=GroupOption, + ), + href_option, +] + +content.add_command(list_command(decorators=list_options + fields_options)) +content.add_command(show_command(decorators=lookup_options)) + + +@content.command() +@click.option("--file", type=click.File("rb"), required=True) +@chunk_size_option +@pulp_option("--name", help=_("Name of {entity}, only required for Roles")) +@pulp_option("--namespace", help=_("Namespace of {entity}, only required for Roles")) +@pulp_option("--version", help=_("Version of {entity}, only required for Roles")) +@pass_entity_context +@pass_pulp_context +def upload( + pulp_ctx: PulpContext, + content_ctx: Union[PulpAnsibleRoleContext, PulpAnsibleCollectionVersionContext], + file: IO[bytes], + chunk_size: int, + name: Optional[str], + namespace: Optional[str], + version: Optional[str], +) -> None: + + if isinstance(content_ctx, PulpAnsibleRoleContext): + # Check that name, namespace, and version are supplied for Role upload + if not all((name, namespace, version)): + raise click.ClickException( + "Please specify name, namespace, and version when uploading a Role" + ) + artifact_href = PulpArtifactContext(pulp_ctx).upload(file, chunk_size) + body = {"artifact": artifact_href, "name": name, "namespace": namespace, "version": version} + result = content_ctx.create(body=body) + pulp_ctx.output_result(result) + else: + # Collection upload uses name, namespace, and version for extra validation + body = {"expected_name": name, "expected_namespace": namespace, "expected_version": version} + result = content_ctx.upload(file=file, body=body) + pulp_ctx.output_result(result) diff --git a/pulpcore/cli/ansible/context.py b/pulpcore/cli/ansible/context.py index dfa9f1700..8aad35c27 100644 --- a/pulpcore/cli/ansible/context.py +++ b/pulpcore/cli/ansible/context.py @@ -1,4 +1,5 @@ import gettext +from typing import IO, Any from pulpcore.cli.common.context import ( EntityDefinition, @@ -21,6 +22,11 @@ class PulpAnsibleCollectionVersionContext(PulpContentContext): LIST_ID = "content_ansible_collection_versions_list" READ_ID = "content_ansible_collection_versions_read" CREATE_ID = "content_ansible_collection_versions_create" + UPLOAD_ID = "upload_collection" + + def upload(self, file: IO[bytes], body: Any) -> Any: + body = self.preprocess_body(body) + return self.pulp_ctx.call(self.UPLOAD_ID, body=body, uploads={"file": file.read()}) class PulpAnsibleRoleContext(PulpContentContext): diff --git a/tests/scripts/pulp_ansible/test_content.sh b/tests/scripts/pulp_ansible/test_content.sh new file mode 100755 index 000000000..a68f95eee --- /dev/null +++ b/tests/scripts/pulp_ansible/test_content.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# shellcheck source=tests/scripts/config.source +. "$(dirname "$(dirname "$(realpath "$0")")")"/config.source + +pulp debug has-plugin --name "ansible" || exit 3 + +cleanup() { + pulp orphans delete || true +} +trap cleanup EXIT + +# Test ansible collection-version upload +wget "https://galaxy.ansible.com/download/ansible-posix-1.3.0.tar.gz" +sha256=$(sha256sum ansible-posix-1.3.0.tar.gz | cut -d' ' -f1) + +expect_succ pulp ansible content upload --file "ansible-posix-1.3.0.tar.gz" +expect_succ pulp artifact list --sha256 "$sha256" +test "$(echo "$OUTPUT" | jq -r length)" -eq "1" +expect_succ pulp ansible content list --name "posix" --namespace "ansible" --version "1.3.0" +test "$(echo "$OUTPUT" | jq -r length)" -eq "1" +content_href="$(echo "$OUTPUT" | jq -r .[0].pulp_href)" +expect_succ pulp ansible content show --href "$content_href" + +# Test ansible role upload +wget "https://github.com/ansible/ansible-kubernetes-modules/archive/v0.0.1.tar.gz" +sha2256=$(sha256sum v0.0.1.tar.gz | cut -d' ' -f1) + +expect_succ pulp ansible content --type "role" upload --file "v0.0.1.tar.gz" --name "kubernetes-modules" --namespace "ansible" --version "0.0.1" +expect_succ pulp artifact list --sha256 "$sha2256" +test "$(echo "$OUTPUT" | jq -r length)" -eq "1" +expect_succ pulp ansible content --type "role" list --name "kubernetes-modules" --namespace "ansible" --version "0.0.1" +test "$(echo "$OUTPUT" | jq -r length)" -eq "1" +content2_href="$(echo "$OUTPUT" | jq -r .[0].pulp_href)" +expect_succ pulp ansible content --type "role" show --href "$content2_href"