Files
ModelForge-Public/backend/tests/test_rc_candidate_freeze.py
T

217 lines
7.9 KiB
Python

"""Unit contracts for the transportable, exact RC candidate image set."""
from __future__ import annotations
import hashlib
import importlib.util
import io
import json
import subprocess
import sys
import tarfile
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
RELEASE_BUILD = ROOT / "scripts" / "release_build.py"
def _release_build_module():
spec = importlib.util.spec_from_file_location("rc_candidate_release_build", RELEASE_BUILD)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def _write_public_source_manifest(root: Path, module, source_revision: str) -> dict[str, object]:
files = []
for relative_path in module.SOURCE_DOCKERFILES:
path = root / relative_path
path.parent.mkdir(parents=True, exist_ok=True)
payload = (
f'LABEL {module.OCI_SOURCE_LABEL}="{module.PUBLIC_SOURCE_REPOSITORY}"\n'
).encode()
path.write_bytes(payload)
files.append(
{
"path": relative_path,
"bytes": len(payload),
"sha256": hashlib.sha256(payload).hexdigest(),
}
)
manifest: dict[str, object] = {
"schemaVersion": 1,
"sourceRevision": source_revision,
"sourceRepository": module.PUBLIC_SOURCE_REPOSITORY,
"files": files,
}
(root / "PUBLIC_SOURCE_MANIFEST.json").write_text(json.dumps(manifest), encoding="utf-8")
return manifest
def test_release_command_failure_keeps_the_docker_error_tail(
monkeypatch: pytest.MonkeyPatch,
) -> None:
module = _release_build_module()
failure = subprocess.CompletedProcess(
["docker", "build"], 73, stdout="", stderr="progress\n" * 300 + "final Docker error"
)
monkeypatch.setattr(module.subprocess, "run", lambda *_args, **_kwargs: failure)
with pytest.raises(RuntimeError) as error:
module.run("docker", "build")
assert "final Docker error" in str(error.value)
assert "failed (73)" in str(error.value)
assert "progress\n" * 300 not in str(error.value)
def _add_file(archive: tarfile.TarFile, name: str, payload: bytes) -> None:
info = tarfile.TarInfo(name)
info.size = len(payload)
archive.addfile(info, io.BytesIO(payload))
def _image_records(module) -> dict[str, dict[str, object]]:
records: dict[str, dict[str, object]] = {}
for index, name in enumerate(module.IMAGES):
records[name] = {
"tag": f"{name}:1.3.0-rc.1",
"architecture": "amd64",
"labels": {
"org.opencontainers.image.version": "1.3.0-rc.1",
"org.opencontainers.image.revision": "a" * 40,
},
"rootfs_layers": [f"sha256:{index + 1:064x}"],
}
return records
def _write_bundle(path: Path, records: dict[str, dict[str, object]]) -> None:
manifest: list[dict[str, object]] = []
with tarfile.open(path, "w") as archive:
for index, (_name, record) in enumerate(records.items()):
config = f"{index:064x}.json"
layer = f"{index:064x}/layer.tar"
payload = {
"architecture": record["architecture"],
"config": {"Labels": record["labels"]},
"rootfs": {"diff_ids": record["rootfs_layers"]},
}
_add_file(archive, config, json.dumps(payload).encode())
_add_file(archive, layer, b"layer")
manifest.append({"Config": config, "RepoTags": [record["tag"]], "Layers": [layer]})
_add_file(archive, "manifest.json", json.dumps(manifest).encode())
def test_image_bundle_binds_exact_tags_labels_and_layer_sets(tmp_path: Path) -> None:
module = _release_build_module()
records = _image_records(module)
bundle = tmp_path / "candidate-images.tar"
_write_bundle(bundle, records)
verified = module.verify_image_bundle(bundle, records)
assert set(verified) == set(module.IMAGES)
assert verified["modelforge-api"]["tag"] == "modelforge-api:1.3.0-rc.1"
def test_image_bundle_rejects_a_changed_filesystem_layer_set(tmp_path: Path) -> None:
module = _release_build_module()
records = _image_records(module)
bundle = tmp_path / "candidate-images.tar"
_write_bundle(bundle, records)
records["modelforge-api"]["rootfs_layers"] = ["sha256:" + "f" * 64]
with pytest.raises(ValueError, match="layer set"):
module.verify_image_bundle(bundle, records)
def test_parentless_export_requires_an_exact_canonical_source_manifest(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
module = _release_build_module()
build_commit = "b" * 40
canonical_commit = "a" * 40
monkeypatch.setattr(module, "ROOT", tmp_path)
monkeypatch.setattr(
module,
"run",
lambda *args, **kwargs: {
("git", "rev-parse", "HEAD"): build_commit,
("git", "status", "--porcelain"): "",
("git", "describe", "--tags", "--always"): "candidate",
("git", "remote", "get-url", "origin"): "",
("git", "rev-parse", "--abbrev-ref", "HEAD"): "main",
}.get(args, ""),
)
with pytest.raises(SystemExit, match="unbound source-label override"):
module.source_identity(False, canonical_commit)
manifest = _write_public_source_manifest(tmp_path, module, canonical_commit)
with pytest.raises(SystemExit, match="requires --canonical-source-commit"):
module.source_identity(False)
identity = module.source_identity(False, canonical_commit)
assert identity["commit"] == canonical_commit
assert identity["build_commit"] == build_commit
assert identity["public_source_manifest_sha256"]
assert identity["source_repository"] == module.PUBLIC_SOURCE_REPOSITORY
manifest_path = tmp_path / "PUBLIC_SOURCE_MANIFEST.json"
manifest["sourceRepository"] = "https://attacker.invalid/forged"
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
with pytest.raises(SystemExit, match="approved sanitized identity"):
module.source_identity(False, canonical_commit)
manifest["sourceRepository"] = module.PUBLIC_SOURCE_REPOSITORY
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
dockerfile = tmp_path / module.SOURCE_DOCKERFILES[0]
dockerfile.write_text("LABEL org.opencontainers.image.source=\"https://attacker.invalid\"\n")
with pytest.raises(SystemExit, match="does not bind the current"):
module.source_identity(False, canonical_commit)
def test_private_release_identity_uses_the_canonical_https_source(
monkeypatch: pytest.MonkeyPatch,
) -> None:
module = _release_build_module()
commit = "b" * 40
monkeypatch.setattr(module, "ROOT", ROOT)
monkeypatch.setattr(
module,
"run",
lambda *args, **kwargs: {
("git", "rev-parse", "HEAD"): commit,
("git", "status", "--porcelain"): "",
("git", "describe", "--tags", "--always"): "candidate",
("git", "remote", "get-url", "origin"): "ssh://private.example/repo.git",
("git", "rev-parse", "--abbrev-ref", "HEAD"): "main",
}.get(args, ""),
)
identity = module.source_identity(False)
assert identity["source_repository"] == module.CANONICAL_SOURCE_REPOSITORY
def test_release_builder_checks_built_image_source_labels() -> None:
module = _release_build_module()
source = module.PUBLIC_SOURCE_REPOSITORY
records = {
"modelforge-api": {"labels": {module.OCI_SOURCE_LABEL: source}},
"modelforge-web": {"labels": {module.OCI_SOURCE_LABEL: source}},
}
module._assert_image_source_identity(records, source)
records["modelforge-web"]["labels"][module.OCI_SOURCE_LABEL] = "https://attacker.invalid"
with pytest.raises(SystemExit, match="source label does not match source identity"):
module._assert_image_source_identity(records, source)