fix: verify WebUI OCI source revision
This commit is contained in:
parent
735a2b240d
commit
99a9de936b
@ -17,6 +17,7 @@ from hermes_webui_flux_release import (
|
|||||||
DEFAULT_IMAGE as DEFAULT_IMAGE,
|
DEFAULT_IMAGE as DEFAULT_IMAGE,
|
||||||
DESTINATION_PATTERN,
|
DESTINATION_PATTERN,
|
||||||
DIGEST_PATTERN,
|
DIGEST_PATTERN,
|
||||||
|
REVISION_PATTERN,
|
||||||
render_workload as render_workload,
|
render_workload as render_workload,
|
||||||
validate_destination,
|
validate_destination,
|
||||||
validate_release_artifacts as validate_flux_release_artifacts,
|
validate_release_artifacts as validate_flux_release_artifacts,
|
||||||
@ -232,13 +233,17 @@ def assert_tag_absent(
|
|||||||
def verify_registry_digest(
|
def verify_registry_digest(
|
||||||
destination: str,
|
destination: str,
|
||||||
digest: str,
|
digest: str,
|
||||||
|
source_revision: str,
|
||||||
*,
|
*,
|
||||||
username: str,
|
username: str,
|
||||||
password: str,
|
password: str,
|
||||||
opener: Callable[[urllib.request.Request, int], Any] = _registry_request,
|
opener: Callable[[urllib.request.Request, int], Any] = _registry_request,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Verify Harbor independently resolves the pushed tag to Kaniko's digest."""
|
"""Verify Harbor resolves the tag, digest, and persisted source revision."""
|
||||||
digest = _validated(digest, DIGEST_PATTERN, "image digest")
|
digest = _validated(digest, DIGEST_PATTERN, "image digest")
|
||||||
|
source_revision = _validated(
|
||||||
|
source_revision, REVISION_PATTERN, "source revision"
|
||||||
|
)
|
||||||
status, body = _artifact_response(
|
status, body = _artifact_response(
|
||||||
destination, username=username, password=password, opener=opener
|
destination, username=username, password=password, opener=opener
|
||||||
)
|
)
|
||||||
@ -263,6 +268,11 @@ def verify_registry_digest(
|
|||||||
raise RuntimeError("Harbor artifact does not contain the expected tag")
|
raise RuntimeError("Harbor artifact does not contain the expected tag")
|
||||||
if matching_tags[0].get("immutable") is not True:
|
if matching_tags[0].get("immutable") is not True:
|
||||||
raise RuntimeError("Harbor did not enforce the expected tag as immutable")
|
raise RuntimeError("Harbor did not enforce the expected tag as immutable")
|
||||||
|
labels = ((artifact.get("extra_attrs") or {}).get("config") or {}).get("Labels")
|
||||||
|
if not isinstance(labels, dict):
|
||||||
|
raise RuntimeError("Harbor artifact omitted OCI image labels")
|
||||||
|
if labels.get("org.opencontainers.image.revision") != source_revision:
|
||||||
|
raise RuntimeError("Harbor OCI source-revision label does not match")
|
||||||
|
|
||||||
|
|
||||||
def validate_release_artifacts(
|
def validate_release_artifacts(
|
||||||
@ -356,7 +366,11 @@ def main() -> int:
|
|||||||
destination=args.destination,
|
destination=args.destination,
|
||||||
)
|
)
|
||||||
verify_registry_digest(
|
verify_registry_digest(
|
||||||
args.destination, digest, username=username, password=password
|
args.destination,
|
||||||
|
digest,
|
||||||
|
args.source_revision,
|
||||||
|
username=username,
|
||||||
|
password=password,
|
||||||
)
|
)
|
||||||
write_release_artifacts(
|
write_release_artifacts(
|
||||||
digest=digest,
|
digest=digest,
|
||||||
|
|||||||
@ -232,12 +232,24 @@ def test_release_verifies_exact_webui_harbor_artifact_and_policy() -> None:
|
|||||||
{
|
{
|
||||||
"digest": digest,
|
"digest": digest,
|
||||||
"tags": [{"name": f"git-{revision}-build-9", "immutable": True}],
|
"tags": [{"name": f"git-{revision}-build-9", "immutable": True}],
|
||||||
|
"extra_attrs": {
|
||||||
|
"config": {
|
||||||
|
"Labels": {
|
||||||
|
"org.opencontainers.image.revision": revision,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
).encode()
|
).encode()
|
||||||
)
|
)
|
||||||
|
|
||||||
module.verify_registry_digest(
|
module.verify_registry_digest(
|
||||||
destination, digest, username="robot", password="private", opener=artifact_open
|
destination,
|
||||||
|
digest,
|
||||||
|
revision,
|
||||||
|
username="robot",
|
||||||
|
password="private",
|
||||||
|
opener=artifact_open,
|
||||||
)
|
)
|
||||||
request = seen[0][0]
|
request = seen[0][0]
|
||||||
assert "/repositories/hermes-webui/artifacts/" in request.full_url
|
assert "/repositories/hermes-webui/artifacts/" in request.full_url
|
||||||
@ -283,6 +295,37 @@ def test_release_verifies_exact_webui_harbor_artifact_and_policy() -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("labels", [None, {}, {"org.opencontainers.image.revision": "c" * 40}])
|
||||||
|
def test_release_rejects_missing_or_wrong_harbor_source_revision(labels) -> None:
|
||||||
|
"""A tag derived from Git cannot substitute for the persisted OCI label."""
|
||||||
|
module = _load(RELEASE, f"webui_registry_revision_{labels!r}")
|
||||||
|
revision = "a" * 40
|
||||||
|
destination = f"{module.DEFAULT_IMAGE}:git-{revision}-build-9"
|
||||||
|
digest = "sha256:" + "b" * 64
|
||||||
|
|
||||||
|
def artifact_open(_request, _timeout):
|
||||||
|
config = {} if labels is None else {"Labels": labels}
|
||||||
|
return _Response(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"digest": digest,
|
||||||
|
"tags": [{"name": f"git-{revision}-build-9", "immutable": True}],
|
||||||
|
"extra_attrs": {"config": config},
|
||||||
|
}
|
||||||
|
).encode()
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError, match="OCI (image labels|source-revision label)"):
|
||||||
|
module.verify_registry_digest(
|
||||||
|
destination,
|
||||||
|
digest,
|
||||||
|
revision,
|
||||||
|
username="robot",
|
||||||
|
password="private",
|
||||||
|
opener=artifact_open,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_flux_tracks_webui_policy_before_jenkins() -> None:
|
def test_flux_tracks_webui_policy_before_jenkins() -> None:
|
||||||
"""The immutable Harbor rule is reviewed desired state, not a pipeline wish."""
|
"""The immutable Harbor rule is reviewed desired state, not a pipeline wish."""
|
||||||
harbor = yaml.safe_load(
|
harbor = yaml.safe_load(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user