diff --git a/CHANGES/315.feature b/CHANGES/315.feature new file mode 100644 index 000000000..62531fb43 --- /dev/null +++ b/CHANGES/315.feature @@ -0,0 +1 @@ +Added the ability to pass an href to a resource option. diff --git a/pulpcore/cli/ansible/repository.py b/pulpcore/cli/ansible/repository.py index f9843e9fe..b21aa2f6f 100644 --- a/pulpcore/cli/ansible/repository.py +++ b/pulpcore/cli/ansible/repository.py @@ -11,6 +11,7 @@ EntityFieldDefinition, PulpContext, PulpEntityContext, + PulpRemoteContext, PulpRepositoryContext, pass_pulp_context, pass_repository_context, @@ -41,6 +42,10 @@ "ansible:collection": PulpAnsibleCollectionRemoteContext, "ansible:role": PulpAnsibleRoleRemoteContext, }, + href_pattern=PulpRemoteContext.HREF_PATTERN, + help=_( + "Remote used for synching in the form '[[:]:]' or by href." + ), ) diff --git a/pulpcore/cli/common/context.py b/pulpcore/cli/common/context.py index 341f8292a..b041e9119 100644 --- a/pulpcore/cli/common/context.py +++ b/pulpcore/cli/common/context.py @@ -455,6 +455,7 @@ class PulpRemoteContext(PulpEntityContext): ENTITY = "remote" ENTITIES = "remotes" + HREF_PATTERN = r"^/pulp/api/v3/remotes/(?P\w+)/(?P\w+)/" NULLABLES = { "ca_cert", "client_cert", @@ -525,6 +526,7 @@ class PulpRepositoryContext(PulpEntityContext): ENTITY = "repository" ENTITIES = "repositories" + HREF_PATTERN = r"^/pulp/api/v3/repositories/(?P\w+)/(?P\w+)/" LIST_ID = "repositories_list" SYNC_ID: ClassVar[str] MODIFY_ID: ClassVar[str] diff --git a/pulpcore/cli/common/generic.py b/pulpcore/cli/common/generic.py index f74b8a5e1..8f31b4a9a 100644 --- a/pulpcore/cli/common/generic.py +++ b/pulpcore/cli/common/generic.py @@ -1,5 +1,6 @@ import gettext import json +import re from typing import ( Any, Callable, @@ -248,6 +249,7 @@ def resource_option(*args: Any, **kwargs: Any) -> Callable[[_FC], _FC]: lookup_key: str = kwargs.pop("lookup_key", "name") context_table: Dict[str, Type[PulpEntityContext]] = kwargs.pop("context_table") capabilities: Optional[List[str]] = kwargs.pop("capabilities", None) + href_pattern: Optional[str] = kwargs.pop("href_pattern", None) def _option_callback( ctx: click.Context, param: click.Parameter, value: Optional[str] @@ -256,10 +258,35 @@ def _option_callback( if not value: return value - split_value = value.split(":", maxsplit=2) - while len(split_value) < 3: - split_value.insert(0, "") - plugin, resource_type, identifier = split_value + pulp_href: Optional[str] = None + entity: Optional[EntityDefinition] = None + + if value.startswith("/"): + # An href was passed + if href_pattern is None: + raise click.ClickException( + _("The option {option_name} does not support href.").format( + option_name=param.name + ) + ) + match = re.match(href_pattern, value) + if match is None: + raise click.ClickException( + _("'{value}' is not a valid href for {option_name}.").format( + value=value, option_name=param.name + ) + ) + match_groups = match.groupdict(default="") + plugin = match_groups.get("plugin", "") + resource_type = match_groups.get("resource_type", "") + pulp_href = value + else: + # A natural key identifier was passed + split_value = value.split(":", maxsplit=2) + while len(split_value) < 3: + split_value.insert(0, "") + plugin, resource_type, identifier = split_value + entity = {lookup_key: identifier} if resource_type == "": if default_type is None: @@ -288,7 +315,7 @@ def _option_callback( ) pulp_ctx = ctx.find_object(PulpContext) assert pulp_ctx is not None - entity_ctx: PulpEntityContext = context_class(pulp_ctx, entity={lookup_key: identifier}) + entity_ctx: PulpEntityContext = context_class(pulp_ctx, pulp_href=pulp_href, entity=entity) if capabilities is not None: for capability in capabilities: diff --git a/pulpcore/cli/container/repository.py b/pulpcore/cli/container/repository.py index a0aebdd14..891bb80a2 100644 --- a/pulpcore/cli/container/repository.py +++ b/pulpcore/cli/container/repository.py @@ -7,6 +7,7 @@ EntityFieldDefinition, PulpContext, PulpEntityContext, + PulpRemoteContext, PulpRepositoryContext, pass_pulp_context, pass_repository_context, @@ -42,6 +43,10 @@ default_plugin="container", default_type="container", context_table={"container:container": PulpContainerRemoteContext}, + href_pattern=PulpRemoteContext.HREF_PATTERN, + help=_( + "Remote used for synching in the form '[[:]:]' or by href." + ), ) diff --git a/pulpcore/cli/core/export.py b/pulpcore/cli/core/export.py index 022dd433b..d72501c2f 100644 --- a/pulpcore/cli/core/export.py +++ b/pulpcore/cli/core/export.py @@ -1,10 +1,12 @@ import gettext -from typing import Any, Dict, Iterable, Tuple +import re +from typing import Any, Dict, Iterable, Optional, Tuple import click from pulpcore.cli.common.context import ( DEFAULT_LIMIT, + EntityDefinition, PulpContext, PulpRepositoryContext, PulpRepositoryVersionContext, @@ -23,9 +25,26 @@ def _version_list_callback( ) -> Iterable[PulpRepositoryVersionContext]: result = [] for item in value: - plugin, resource_type, identifier = item[0].split(":", maxsplit=2) - if not identifier: - raise click.ClickException(_("Repositories must be specified with plugin and type")) + pulp_href: Optional[str] = None + entity: Optional[EntityDefinition] = None + + if item[0].startswith("/"): + match = re.match(PulpRepositoryContext.HREF_PATTERN, item[0]) + if match is None: + raise click.ClickException( + _("'{value}' is not a valid href for {option_name}.").format( + value=value, option_name=param.name + ) + ) + match_groups = match.groupdict(default="") + plugin = match_groups.get("plugin", "") + resource_type = match_groups.get("resource_type", "") + pulp_href = item[0] + else: + plugin, resource_type, identifier = item[0].split(":", maxsplit=2) + if not identifier: + raise click.ClickException(_("Repositories must be specified with plugin and type")) + entity = {"name": identifier} context_class = registered_repository_contexts.get(plugin + ":" + resource_type) if context_class is None: raise click.ClickException( @@ -36,7 +55,9 @@ def _version_list_callback( ) pulp_ctx = ctx.find_object(PulpContext) assert pulp_ctx is not None - repository_ctx: PulpRepositoryContext = context_class(pulp_ctx, entity={"name": identifier}) + repository_ctx: PulpRepositoryContext = context_class( + pulp_ctx, pulp_href=pulp_href, entity=entity + ) if not repository_ctx.capable("pulpexport"): raise click.ClickException( diff --git a/pulpcore/cli/core/exporter.py b/pulpcore/cli/core/exporter.py index 2fe23fbad..76d01bd5d 100644 --- a/pulpcore/cli/core/exporter.py +++ b/pulpcore/cli/core/exporter.py @@ -7,6 +7,7 @@ EntityFieldDefinition, PulpContext, PulpEntityContext, + PulpRepositoryContext, pass_entity_context, pass_pulp_context, registered_repository_contexts, @@ -29,6 +30,11 @@ context_table=registered_repository_contexts, capabilities=["pulpexport"], multiple=True, + href_pattern=PulpRepositoryContext.HREF_PATTERN, + help=_( + "Repository to export from in the form '[[:]:]' or by href." + " Can be called multiple times." + ), ) @@ -83,7 +89,7 @@ def create( @href_option @click.option("--path") @multi_repository_option -@click.option("--repository-href", multiple=True) +@click.option("--repository-href", multiple=True) # This should be deprecated @pass_entity_context @pass_pulp_context def update( diff --git a/pulpcore/cli/file/repository.py b/pulpcore/cli/file/repository.py index 7cd122462..5880d42aa 100644 --- a/pulpcore/cli/file/repository.py +++ b/pulpcore/cli/file/repository.py @@ -9,6 +9,7 @@ PluginRequirement, PulpContext, PulpEntityContext, + PulpRemoteContext, PulpRepositoryContext, pass_pulp_context, pass_repository_context, @@ -49,6 +50,10 @@ default_plugin="file", default_type="file", context_table={"file:file": PulpFileRemoteContext}, + href_pattern=PulpRemoteContext.HREF_PATTERN, + help=_( + "Remote used for synching in the form '[[:]:]' or by href." + ), ) diff --git a/pulpcore/cli/python/repository.py b/pulpcore/cli/python/repository.py index 6cb855f13..3c9ab7ed1 100644 --- a/pulpcore/cli/python/repository.py +++ b/pulpcore/cli/python/repository.py @@ -8,6 +8,7 @@ PluginRequirement, PulpContext, PulpEntityContext, + PulpRemoteContext, PulpRepositoryContext, pass_pulp_context, pass_repository_context, @@ -46,6 +47,10 @@ default_plugin="python", default_type="python", context_table={"python:python": PulpPythonRemoteContext}, + href_pattern=PulpRemoteContext.HREF_PATTERN, + help=_( + "Remote used for synching in the form '[[:]:]' or by href." + ), ) diff --git a/pulpcore/cli/rpm/repository.py b/pulpcore/cli/rpm/repository.py index 77398e685..d716279f7 100644 --- a/pulpcore/cli/rpm/repository.py +++ b/pulpcore/cli/rpm/repository.py @@ -8,6 +8,7 @@ PluginRequirement, PulpContext, PulpEntityContext, + PulpRemoteContext, PulpRepositoryContext, pass_pulp_context, pass_repository_context, @@ -42,6 +43,10 @@ default_plugin="rpm", default_type="rpm", context_table={"rpm:rpm": PulpRpmRemoteContext}, + href_pattern=PulpRemoteContext.HREF_PATTERN, + help=_( + "Remote used for synching in the form '[[:]:]' or by href." + ), ) diff --git a/tests/scripts/pulp_file/test_repository.sh b/tests/scripts/pulp_file/test_repository.sh index a4848196d..d0c24691e 100755 --- a/tests/scripts/pulp_file/test_repository.sh +++ b/tests/scripts/pulp_file/test_repository.sh @@ -28,6 +28,9 @@ expect_succ pulp file repository update --name "cli_test_file_repo" --remote "fi expect_succ pulp file repository show --name "cli_test_file_repo" test "$(echo "$OUTPUT" | jq '.description')" = '"Test\nrepository"' test "$(echo "$OUTPUT" | jq -r '.remote')" = "$REMOTE2_HREF" +expect_succ pulp file repository update --name "cli_test_file_repo" --remote "$REMOTE1_HREF" +expect_succ pulp file repository show --name "cli_test_file_repo" +test "$(echo "$OUTPUT" | jq -r '.remote')" = "$REMOTE1_HREF" expect_succ pulp file repository update --name "cli_test_file_repo" --remote "" expect_succ pulp file repository show --name "cli_test_file_repo" test "$(echo "$OUTPUT" | jq -r '.remote')" = "null" @@ -35,7 +38,7 @@ expect_succ pulp file repository list test "$(echo "$OUTPUT" | jq -r '.|length')" != "0" expect_succ pulp file repository task list --repository "cli_test_file_repo" -test "$(echo "$OUTPUT" | jq -r '.|length')" = "5" +test "$(echo "$OUTPUT" | jq -r '.|length')" = "6" if [ "$(pulp debug has-plugin --name "file" --min-version "1.7.0")" = "true" ] then diff --git a/tests/scripts/pulpcore/test_pulpexporter.sh b/tests/scripts/pulpcore/test_pulpexporter.sh index b4f9a9787..ae76234e5 100755 --- a/tests/scripts/pulpcore/test_pulpexporter.sh +++ b/tests/scripts/pulpcore/test_pulpexporter.sh @@ -27,10 +27,11 @@ expect_succ pulp file remote create --name $RMOTE --url "$FILE_REMOTE_URL" expect_succ pulp file repository create --name $REPO1 expect_succ pulp file repository sync --name $REPO1 --remote $RMOTE expect_succ pulp file repository create --name $REPO2 +REPO1_HREF="$(pulp file repository show --name $REPO1 | jq -r '.pulp_href')" # Test create expect_succ pulp exporter pulp create --name $NAME1 --path $PATH1 --repository file:file:$REPO1 --repository file:file:$REPO2 -expect_succ pulp exporter pulp create --name $NAME2 --path $PATH1 --repository file:file:$REPO1 +expect_succ pulp exporter pulp create --name $NAME2 --path $PATH1 --repository "$REPO1_HREF" # Test read expect_succ pulp exporter pulp show --name $NAME1 @@ -52,14 +53,14 @@ expect_succ pulp export pulp run --exporter $NAME1 --full False expect_succ pulp export pulp run --exporter $NAME1 --full True --chunk-size 5KB expect_succ pulp export pulp run --exporter $NAME2 --full False --start-versions file:file:${REPO1} 0 expect_succ pulp export pulp run --exporter $NAME2 --full False --versions file:file:${REPO1} 0 -expect_succ pulp export pulp run --exporter $NAME2 --full False --start-versions file:file:${REPO1} 0 --versions file:file:${REPO1} 1 -expect_succ pulp export pulp run --exporter $NAME2 --full False --start-versions file:file:${REPO1} 0 --versions file:file:${REPO1} 1 --chunk-size 5KB +expect_succ pulp export pulp run --exporter $NAME2 --full False --start-versions file:file:${REPO1} 0 --versions "${REPO1_HREF}" 1 +expect_succ pulp export pulp run --exporter $NAME2 --full False --start-versions "${REPO1_HREF}" 0 --versions file:file:${REPO1} 1 --chunk-size 5KB # Test list exports expect_succ pulp export pulp list --exporter $NAME1 # Test delete export -HREF=$(pulp export pulp list --exporter $NAME1 | jq -r '.[0].pulp_href') +HREF=$(pulp export pulp list --exporter $NAME1 | jq -r '.[0].pulp_href') expect_succ pulp export pulp destroy --href "$HREF" # Test delete exporter