diff --git a/CHANGES/1324.feature b/CHANGES/1324.feature new file mode 100644 index 000000000..dd588217f --- /dev/null +++ b/CHANGES/1324.feature @@ -0,0 +1 @@ +Added `--version` option to `pulp python distribution` for serving content from a specific repository version. diff --git a/CHANGES/pulp-glue/+entityctx_update.bugfix b/CHANGES/pulp-glue/+entityctx_update.bugfix new file mode 100644 index 000000000..63e685dcf --- /dev/null +++ b/CHANGES/pulp-glue/+entityctx_update.bugfix @@ -0,0 +1 @@ +Fixed `PulpEntityContext.update` method to correctly pass `partial=True` to `preprocess_entity`. diff --git a/CHANGES/pulp-glue/1324.feature b/CHANGES/pulp-glue/1324.feature new file mode 100644 index 000000000..5a1f7c696 --- /dev/null +++ b/CHANGES/pulp-glue/1324.feature @@ -0,0 +1 @@ +Added repository version logic to `PulpPythonDistributionContext` for `pulp_python>=3.21.0`. diff --git a/pulp-glue/docs/dev/learn/architecture.md b/pulp-glue/docs/dev/learn/architecture.md index afe5aadc9..57bac79ea 100644 --- a/pulp-glue/docs/dev/learn/architecture.md +++ b/pulp-glue/docs/dev/learn/architecture.md @@ -24,7 +24,7 @@ Those include: - `PulpContext.api`: When accessed, the `api.json` file for the addressed server will be read or downloaded and processed. Scheduled version checks will be evaluated at that point. - - `PulpContext.needs_version`: This function can be used at any time to declare that an operation needs a plugin in a version range. + - `PulpContext.needs_plugin`: This function can be used at any time to declare that an operation needs a plugin in a version range. The actual check will be performed immediately when `api` already was accessed, or scheduled for later. - `PulpEntityContext.entity`: This property can be used to collect lookup attributes for entities by assigning dicts to it. On read access, the entity lookup will be performed through the `api` property. @@ -50,7 +50,7 @@ Typically, plugin requirements are checked by passing into `PulpContext.has_plug Some Entities may provide support for different Pulp concepts based on their plugins version. e.g. `Pulp Import Export` for a specific repository type may be added in a certain Pulp Plugin version. You can add a `capability` to the `PulpEntityContext` subclass with an attached `PluginRequirement`. -Whenever glue attempts to perform the corresonding action, the capabilities are first checked against the server's versions. +Whenever glue attempts to perform the corresponding action, the capabilities are first checked against the server's versions. ### API quirks @@ -60,5 +60,5 @@ See the `pulp_glue.common.api_quirk` decorator. ## Plugin System -Pulp Glue comes with a plugin interface to be easily extendible. +Pulp Glue comes with a plugin interface to be easily extendable. Multiple plugins can be provided by the same Python package and some plugins are shipped with the pulp-glue core package. diff --git a/pulp-glue/src/pulp_glue/common/context.py b/pulp-glue/src/pulp_glue/common/context.py index 6ae605a22..f5257d239 100644 --- a/pulp-glue/src/pulp_glue/common/context.py +++ b/pulp-glue/src/pulp_glue/common/context.py @@ -1184,7 +1184,7 @@ def update( if parameters: _parameters.update(parameters) if body is not None: - body = self.preprocess_entity(body, partial=False) + body = self.preprocess_entity(body, partial=True) if self.pulp_ctx.fake_mode: assert self._entity is not None if body is not None: diff --git a/pulp-glue/src/pulp_glue/python/context.py b/pulp-glue/src/pulp_glue/python/context.py index e05839e12..5614136b8 100644 --- a/pulp-glue/src/pulp_glue/python/context.py +++ b/pulp-glue/src/pulp_glue/python/context.py @@ -11,6 +11,7 @@ PulpRepositoryVersionContext, api_spec_quirk, ) +from pulp_glue.common.exceptions import PulpException from pulp_glue.common.i18n import get_translation translation = get_translation(__package__) @@ -67,11 +68,33 @@ def preprocess_entity(self, body: EntityDefinition, partial: bool = False) -> En self.pulp_ctx.needs_plugin(PluginRequirement("python", specifier=">=3.4.0")) if "remote" in body: self.pulp_ctx.needs_plugin(PluginRequirement("python", specifier=">=3.6.0")) + if "version" in body: + self.pulp_ctx.needs_plugin(PluginRequirement("python", specifier=">=3.21.0")) if self.pulp_ctx.has_plugin(PluginRequirement("core", specifier=">=3.16.0")): if "repository" in body and "publication" not in body: body["publication"] = None if "repository" not in body and "publication" in body: body["repository"] = None + + version = body.pop("version", None) + if version is not None: + repository_href = body.pop("repository", None) + if not repository_href and partial: + current_entity = self.entity + repository_href = current_entity.get("repository") + if not repository_href and ( + repository_version_href := current_entity.get("repository_version") + ): + repository_href = f"{repository_version_href.rsplit('/', 3)[0]}/" + if not repository_href: + raise PulpException(_("--repository must be provided")) + body["repository_version"] = f"{repository_href}versions/{version}/" + body["repository"] = None + elif "repository" in body and self.pulp_ctx.has_plugin( + PluginRequirement("python", specifier=">=3.21.0") + ): + body["repository_version"] = None + return body diff --git a/src/pulpcore/cli/python/distribution.py b/src/pulpcore/cli/python/distribution.py index 39f67fb31..152b4aa8c 100644 --- a/src/pulpcore/cli/python/distribution.py +++ b/src/pulpcore/cli/python/distribution.py @@ -39,8 +39,8 @@ default_type="python", context_table={"python:python": PulpPythonRepositoryContext}, help=_( - "Repository to be used for auto-distributing." - " When set, this will unset the 'publication'." + "Repository to be distributed." + " When used, this will replace any attached 'publication'." " Specified as '[[:]:]' or as href." ), href_pattern=PulpPythonRepositoryContext.HREF_PATTERN, @@ -75,6 +75,15 @@ def distribution(ctx: click.Context, pulp_ctx: PulpCLIContext, /, distribution_t ), ), repository_option, + pulp_option( + "--version", + type=int, + help=_( + "The repository version number to distribute." + " When unset, the latest version of the repository will be auto-distributed." + ), + needs_plugins=[PluginRequirement("python", specifier=">=3.21.0")], + ), content_guard_option, pulp_option( "--allow-uploads/--block-uploads", diff --git a/tests/scripts/pulp_python/test_distribution.sh b/tests/scripts/pulp_python/test_distribution.sh index 69695d080..a0a817c49 100755 --- a/tests/scripts/pulp_python/test_distribution.sh +++ b/tests/scripts/pulp_python/test_distribution.sh @@ -10,6 +10,8 @@ cleanup() { pulp python repository destroy --name "cli_test_python_distribution_repository" || true pulp python remote destroy --name "cli_test_python_distribution_remote" || true pulp python distribution destroy --name "cli_test_python_distro" || true + pulp python distribution destroy --name "cli_test_python_distro_repo" || true + pulp python distribution destroy --name "cli_test_python_distro_repo_version" || true } trap cleanup EXIT @@ -42,3 +44,48 @@ expect_succ pulp python distribution update \ --remote "cli_test_python_distribution_remote" expect_succ pulp python distribution destroy --distribution "cli_test_python_distro" + +# Test repository_version functionality +if pulp debug has-plugin --name "python" --specifier ">=3.21.0"; then + expect_succ pulp python distribution create \ + --name "cli_test_python_distro_repo" \ + --base-path "cli_test_python_distro_repo" \ + --repository "cli_test_python_distribution_repository" + expect_succ pulp python distribution show --distribution "cli_test_python_distro_repo" + echo "$OUTPUT" | jq -e '.repository_version == null' + echo "$OUTPUT" | jq -e '.repository != null' + + expect_succ pulp python distribution create \ + --name "cli_test_python_distro_repo_version" \ + --base-path "cli_test_python_distro_repo_version" \ + --repository "cli_test_python_distribution_repository" \ + --version 0 + expect_succ pulp python distribution show --distribution "cli_test_python_distro_repo_version" + echo "$OUTPUT" | jq -e '.repository_version | contains("/versions/0/")' + echo "$OUTPUT" | jq -e '.repository == null' + + expect_succ pulp python distribution update \ + --distribution "cli_test_python_distro_repo_version" \ + --repository "cli_test_python_distribution_repository" \ + --version 1 + expect_succ pulp python distribution show --distribution "cli_test_python_distro_repo_version" + echo "$OUTPUT" | jq -e '.repository_version | contains("/versions/1/")' + echo "$OUTPUT" | jq -e '.repository == null' + + expect_succ pulp python distribution update \ + --distribution "cli_test_python_distro_repo_version" \ + --version 0 + expect_succ pulp python distribution show --distribution "cli_test_python_distro_repo_version" + echo "$OUTPUT" | jq -e '.repository_version | contains("/versions/0/")' + echo "$OUTPUT" | jq -e '.repository == null' + + expect_succ pulp python distribution update \ + --distribution "cli_test_python_distro_repo_version" \ + --repository "cli_test_python_distribution_repository" + expect_succ pulp python distribution show --distribution "cli_test_python_distro_repo_version" + echo "$OUTPUT" | jq -e '.repository_version == null' + echo "$OUTPUT" | jq -e '.repository != null' + + expect_succ pulp python distribution destroy --distribution "cli_test_python_distro_repo" + expect_succ pulp python distribution destroy --distribution "cli_test_python_distro_repo_version" +fi