Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/+distribution-version.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added the `--version` option to rpm and file distribution create and update commands to pin a specific repository version for distribution.
1 change: 1 addition & 0 deletions CHANGES/pulp-glue/+distribution-version.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for setting `repository_version` on rpm and file distributions via a `version` parameter.
18 changes: 18 additions & 0 deletions pulp-glue/src/pulp_glue/file/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
PulpRepositoryVersionContext,
api_spec_quirk,
)
from pulp_glue.common.exceptions import PulpException
from pulp_glue.common.i18n import get_translation

translation = get_translation(__package__)
Expand Down Expand Up @@ -87,6 +88,23 @@ def preprocess_entity(self, body: EntityDefinition, partial: bool = False) -> En
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:
self.pulp_ctx.needs_plugin(PluginRequirement("core", specifier=">=3.106.0"))
repository_href = body.pop("repository", None)
if repository_href is None and partial:
repository_href = self.entity.get("repository")
if repository_href is None:
raise PulpException(_("--repository must be provided"))
body["repository_version"] = f"{repository_href}versions/{version}/"
body["repository"] = None
body["publication"] = None
elif "repository" in body and self.pulp_ctx.has_plugin(
PluginRequirement("core", specifier=">=3.106.0")
):
body["repository_version"] = None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that is kind of easy to follow.


return body


Expand Down
18 changes: 18 additions & 0 deletions pulp-glue/src/pulp_glue/rpm/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
PulpViewSetContext,
api_spec_quirk,
)
from pulp_glue.common.exceptions import PulpException
from pulp_glue.common.i18n import get_translation

translation = get_translation(__package__)
Expand Down Expand Up @@ -86,6 +87,23 @@ def preprocess_entity(self, body: EntityDefinition, partial: bool = False) -> En
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:
self.pulp_ctx.needs_plugin(PluginRequirement("core", specifier=">=3.106.0"))
repository_href = body.pop("repository", None)
if repository_href is None and partial:
repository_href = self.entity.get("repository")
if repository_href is None:
raise PulpException(_("--repository must be provided"))
body["repository_version"] = f"{repository_href}versions/{version}/"
body["repository"] = None
body["publication"] = None
elif "repository" in body and self.pulp_ctx.has_plugin(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the intention here that when specifying the repository but not the version, the version should be nullified?
So we need the "repository" in body part of that condition?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's 2 fields in the CLI (repository and version) which controls which of the 3 fields on distribution get set. If both are present then it sets the repository version, otherwise it sets the repository only.

PluginRequirement("core", specifier=">=3.106.0")
):
body["repository_version"] = None

if body.get("generate_repo_config") is False:
self.pulp_ctx.needs_plugin(
PluginRequirement(
Expand Down
111 changes: 111 additions & 0 deletions pulp-glue/tests/test_distribution_preprocess.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import json
import typing as t

import pytest

from pulp_glue.common import context as context_module
from pulp_glue.common.context import PulpContext, PulpDistributionContext
from pulp_glue.common.exceptions import PulpException
from pulp_glue.common.openapi import OpenAPI
from pulp_glue.file.context import PulpFileDistributionContext
from pulp_glue.rpm.context import PulpRpmDistributionContext

pytestmark = pytest.mark.glue

REPO_HREF = "/pulp/api/v3/repositories/rpm/rpm/01234567-0123-0123-0123-0123456789ab/"
REPO_VERSION_HREF = REPO_HREF + "versions/5/"
PUBLICATION_HREF = "/pulp/api/v3/publications/rpm/rpm/01234567-0123-0123-0123-0123456789ab/"


@pytest.fixture
def modern_pulp_ctx(monkeypatch: pytest.MonkeyPatch) -> PulpContext:
spec = json.dumps(
{
"openapi": "3.0.3",
"info": {
"title": "test",
"version": "0.0.0",
"x-pulp-app-versions": {"core": "3.106.0", "rpm": "3.30.0", "file": "3.75.0"},
},
"paths": {},
}
)
monkeypatch.setattr(context_module, "_patch_api_hook", lambda spec: spec)
monkeypatch.setattr(OpenAPI, "load_api", lambda self, refresh_cache: self._parse_api(spec))
monkeypatch.setattr(
OpenAPI,
"_send_request",
lambda *args, **kwargs: pytest.fail("No API calls allowed in unit tests."),
)
settings: dict[str, t.Any] = {"base_url": "nowhere"}
return PulpContext.from_config(settings)


@pytest.mark.parametrize(
"ctx_cls",
[PulpRpmDistributionContext, PulpFileDistributionContext],
ids=["rpm", "file"],
)
class TestDistributionPreprocess:
def test_version_with_repository(
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
) -> None:
ctx = ctx_cls(modern_pulp_ctx)
body = ctx.preprocess_entity({"version": 5, "repository": REPO_HREF}, partial=False)
assert body["repository_version"] == REPO_VERSION_HREF
assert body["repository"] is None
assert body["publication"] is None
assert "version" not in body

def test_repository_without_version(
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
) -> None:
ctx = ctx_cls(modern_pulp_ctx)
body = ctx.preprocess_entity({"repository": REPO_HREF}, partial=False)
assert body["repository"] == REPO_HREF
assert body["repository_version"] is None
assert body["publication"] is None

def test_publication_without_repository(
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
) -> None:
ctx = ctx_cls(modern_pulp_ctx)
body = ctx.preprocess_entity({"publication": PUBLICATION_HREF}, partial=False)
assert body["publication"] == PUBLICATION_HREF
assert body["repository"] is None
assert body["repository_version"] is None

def test_version_without_repository_raises(
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
) -> None:
ctx = ctx_cls(modern_pulp_ctx)
with pytest.raises(PulpException, match="--repository"):
ctx.preprocess_entity({"version": 5}, partial=False)

def test_version_without_repository_partial_raises(
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
) -> None:
ctx = ctx_cls(modern_pulp_ctx)
ctx._entity = {"repository": None, "repository_version": None}
with pytest.raises(PulpException, match="--repository"):
ctx.preprocess_entity({"version": 5}, partial=True)

def test_partial_version_infers_repository(
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
) -> None:
ctx = ctx_cls(modern_pulp_ctx)
ctx._entity = {"repository": REPO_HREF, "repository_version": None}
body = ctx.preprocess_entity({"version": 3}, partial=True)
assert body["repository_version"] == REPO_HREF + "versions/3/"
assert body["repository"] is None
assert body["publication"] is None

def test_unrelated_field_no_nullification(
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
) -> None:
ctx = ctx_cls(modern_pulp_ctx)
body = ctx.preprocess_entity({"base_path": "/new/path"}, partial=True)
assert body.get("base_path") == "/new/path"
assert "repository" not in body
assert "repository_version" not in body
assert "publication" not in body
9 changes: 9 additions & 0 deletions src/pulpcore/cli/file/distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ def distribution() -> None:
),
),
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("core", specifier=">=3.106.0")],
),
content_guard_option,
pulp_labels_option,
pulp_option(
Expand Down
9 changes: 9 additions & 0 deletions src/pulpcore/cli/rpm/distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ def distribution() -> None:
help=_("Option specifying whether ``*.repo`` files will be generated and served."),
),
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("core", specifier=">=3.106.0")],
),
content_guard_option,
pulp_labels_option,
pulp_option(
Expand Down
Loading