Files
git.stella-ops.org/ops/offline-kit/test_build_offline_kit.py
StellaOps Bot d63af51f84
Some checks failed
api-governance / spectral-lint (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
oas-ci / oas-validate (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
up
2025-11-26 20:23:28 +02:00

335 lines
15 KiB
Python

from __future__ import annotations
import json
import tarfile
import tempfile
import unittest
import argparse
import sys
from collections import OrderedDict
from pathlib import Path
current_dir = Path(__file__).resolve().parent
sys.path.append(str(current_dir))
sys.path.append(str(current_dir.parent / "devops" / "release"))
from build_release import write_manifest # type: ignore import-not-found
from build_offline_kit import build_offline_kit, compute_sha256 # type: ignore import-not-found
class OfflineKitBuilderTests(unittest.TestCase):
def setUp(self) -> None:
self._temp = tempfile.TemporaryDirectory()
self.base_path = Path(self._temp.name)
self.out_dir = self.base_path / "out"
self.release_dir = self.out_dir / "release"
self.staging_dir = self.base_path / "staging"
self.output_dir = self.base_path / "dist"
self._create_sample_release()
def tearDown(self) -> None:
self._temp.cleanup()
def _relative_to_out(self, path: Path) -> str:
return path.relative_to(self.out_dir).as_posix()
def _write_json(self, path: Path, payload: dict[str, object]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8") as handle:
json.dump(payload, handle, indent=2)
handle.write("\n")
def _create_sample_release(self) -> None:
self.release_dir.mkdir(parents=True, exist_ok=True)
cli_archive = self.release_dir / "cli" / "stellaops-cli-linux-x64.tar.gz"
cli_archive.parent.mkdir(parents=True, exist_ok=True)
cli_archive.write_bytes(b"cli-bytes")
compute_sha256(cli_archive)
container_bundle = self.release_dir / "containers" / "stellaops-containers.tar.gz"
container_bundle.parent.mkdir(parents=True, exist_ok=True)
container_bundle.write_bytes(b"container-bundle")
compute_sha256(container_bundle)
orchestrator_service = self.release_dir / "orchestrator" / "service" / "orchestrator-service.tar.gz"
orchestrator_service.parent.mkdir(parents=True, exist_ok=True)
orchestrator_service.write_bytes(b"orch-service")
compute_sha256(orchestrator_service)
orchestrator_dash = self.release_dir / "orchestrator" / "dashboards" / "dash.json"
orchestrator_dash.parent.mkdir(parents=True, exist_ok=True)
orchestrator_dash.write_text("{}\n", encoding="utf-8")
export_bundle = self.release_dir / "export-center" / "export-offline-bundle.tar.gz"
export_bundle.parent.mkdir(parents=True, exist_ok=True)
export_bundle.write_bytes(b"export")
compute_sha256(export_bundle)
notifier_pack = self.release_dir / "notifier" / "notifier-offline-pack.tar.gz"
notifier_pack.parent.mkdir(parents=True, exist_ok=True)
notifier_pack.write_bytes(b"notifier")
compute_sha256(notifier_pack)
secrets_bundle = self.release_dir / "surface-secrets" / "secrets-bundle.tar.gz"
secrets_bundle.parent.mkdir(parents=True, exist_ok=True)
secrets_bundle.write_bytes(b"secrets")
compute_sha256(secrets_bundle)
sbom_path = self.release_dir / "artifacts/sboms/sample.cyclonedx.json"
sbom_path.parent.mkdir(parents=True, exist_ok=True)
sbom_path.write_text('{"bomFormat":"CycloneDX","specVersion":"1.5"}\n', encoding="utf-8")
sbom_sha = compute_sha256(sbom_path)
provenance_path = self.release_dir / "artifacts/provenance/sample.provenance.json"
self._write_json(
provenance_path,
{
"buildDefinition": {"buildType": "https://example/build"},
"runDetails": {"builder": {"id": "https://example/ci"}},
},
)
provenance_sha = compute_sha256(provenance_path)
signature_path = self.release_dir / "artifacts/signatures/sample.signature"
signature_path.parent.mkdir(parents=True, exist_ok=True)
signature_path.write_text("signature-data\n", encoding="utf-8")
signature_sha = compute_sha256(signature_path)
metadata_path = self.release_dir / "artifacts/metadata/sample.metadata.json"
self._write_json(metadata_path, {"digest": "sha256:1234"})
metadata_sha = compute_sha256(metadata_path)
chart_path = self.release_dir / "helm/stellaops-1.0.0.tgz"
chart_path.parent.mkdir(parents=True, exist_ok=True)
chart_path.write_bytes(b"helm-chart-data")
chart_sha = compute_sha256(chart_path)
compose_path = self.release_dir.parent / "deploy/compose/docker-compose.dev.yaml"
compose_path.parent.mkdir(parents=True, exist_ok=True)
compose_path.write_text("services: {}\n", encoding="utf-8")
compose_sha = compute_sha256(compose_path)
debug_file = self.release_dir / "debug/.build-id/ab/cdef.debug"
debug_file.parent.mkdir(parents=True, exist_ok=True)
debug_file.write_bytes(b"\x7fELFDEBUGDATA")
debug_sha = compute_sha256(debug_file)
debug_manifest_path = self.release_dir / "debug/debug-manifest.json"
debug_manifest = OrderedDict(
(
("generatedAt", "2025-10-26T00:00:00Z"),
("version", "1.0.0"),
("channel", "edge"),
(
"artifacts",
[
OrderedDict(
(
("buildId", "abcdef1234"),
("platform", "linux/amd64"),
("debugPath", "debug/.build-id/ab/cdef.debug"),
("sha256", debug_sha),
("size", debug_file.stat().st_size),
("components", ["sample"]),
("images", ["registry.example/sample@sha256:feedface"]),
("sources", ["app/sample.dll"]),
)
)
],
),
)
)
self._write_json(debug_manifest_path, debug_manifest)
debug_manifest_sha = compute_sha256(debug_manifest_path)
(debug_manifest_path.with_suffix(debug_manifest_path.suffix + ".sha256")).write_text(
f"{debug_manifest_sha} {debug_manifest_path.name}\n",
encoding="utf-8",
)
manifest = OrderedDict(
(
(
"release",
OrderedDict(
(
("version", "1.0.0"),
("channel", "edge"),
("date", "2025-10-26T00:00:00Z"),
("calendar", "2025.10"),
)
),
),
(
"components",
[
OrderedDict(
(
("name", "sample"),
("image", "registry.example/sample@sha256:feedface"),
("tags", ["registry.example/sample:1.0.0"]),
(
"sbom",
OrderedDict(
(
("path", self._relative_to_out(sbom_path)),
("sha256", sbom_sha),
)
),
),
(
"provenance",
OrderedDict(
(
("path", self._relative_to_out(provenance_path)),
("sha256", provenance_sha),
)
),
),
(
"signature",
OrderedDict(
(
("path", self._relative_to_out(signature_path)),
("sha256", signature_sha),
("ref", "sigstore://example"),
("tlogUploaded", True),
)
),
),
(
"metadata",
OrderedDict(
(
("path", self._relative_to_out(metadata_path)),
("sha256", metadata_sha),
)
),
),
)
)
],
),
(
"charts",
[
OrderedDict(
(
("name", "stellaops"),
("version", "1.0.0"),
("path", self._relative_to_out(chart_path)),
("sha256", chart_sha),
)
)
],
),
(
"compose",
[
OrderedDict(
(
("name", "docker-compose.dev.yaml"),
("path", compose_path.relative_to(self.out_dir).as_posix()),
("sha256", compose_sha),
)
)
],
),
(
"debugStore",
OrderedDict(
(
("manifest", "debug/debug-manifest.json"),
("sha256", debug_manifest_sha),
("entries", 1),
("platforms", ["linux/amd64"]),
("directory", "debug/.build-id"),
)
),
),
)
)
write_manifest(manifest, self.release_dir)
def test_build_offline_kit(self) -> None:
args = argparse.Namespace(
version="2025.10.0",
channel="edge",
bundle_id="bundle-001",
release_dir=self.release_dir,
staging_dir=self.staging_dir,
output_dir=self.output_dir,
cosign_key=None,
cosign_password=None,
cosign_identity_token=None,
no_transparency=False,
skip_smoke=True,
)
result = build_offline_kit(args)
bundle_path = Path(result["bundlePath"])
self.assertTrue(bundle_path.exists())
offline_manifest = self.output_dir.parent / "staging" / "manifest" / "offline-manifest.json"
self.assertTrue(offline_manifest.exists())
bootstrap_notify = self.staging_dir / "bootstrap" / "notify"
self.assertTrue((bootstrap_notify / "notify.yaml").exists())
self.assertTrue((bootstrap_notify / "notify-web.secret.example").exists())
taskrunner_bootstrap = self.staging_dir / "bootstrap" / "task-runner"
self.assertTrue((taskrunner_bootstrap / "task-runner.yaml.sample").exists())
docs_taskpacks = self.staging_dir / "docs" / "task-packs"
self.assertTrue(docs_taskpacks.exists())
self.assertTrue((self.staging_dir / "docs" / "mirror-bundles.md").exists())
containers_dir = self.staging_dir / "containers"
self.assertTrue((containers_dir / "stellaops-containers.tar.gz").exists())
orchestrator_dir = self.staging_dir / "orchestrator"
self.assertTrue((orchestrator_dir / "service" / "orchestrator-service.tar.gz").exists())
self.assertTrue((orchestrator_dir / "dashboards" / "dash.json").exists())
export_dir = self.staging_dir / "export-center"
self.assertTrue((export_dir / "export-offline-bundle.tar.gz").exists())
notifier_dir = self.staging_dir / "notifier"
self.assertTrue((notifier_dir / "notifier-offline-pack.tar.gz").exists())
secrets_dir = self.staging_dir / "surface-secrets"
self.assertTrue((secrets_dir / "secrets-bundle.tar.gz").exists())
with offline_manifest.open("r", encoding="utf-8") as handle:
manifest_data = json.load(handle)
artifacts = manifest_data["artifacts"]
self.assertTrue(any(item["name"].startswith("sboms/") for item in artifacts))
self.assertTrue(any(item["name"].startswith("cli/") for item in artifacts))
metadata_path = Path(result["metadataPath"])
data = json.loads(metadata_path.read_text(encoding="utf-8"))
self.assertTrue(data["bundleSha256"].startswith("sha256:"))
self.assertTrue(data["manifestSha256"].startswith("sha256:"))
counts = data["counts"]
self.assertGreaterEqual(counts["cli"], 1)
self.assertGreaterEqual(counts["containers"], 1)
self.assertGreaterEqual(counts["orchestrator"], 2)
self.assertGreaterEqual(counts["exportCenter"], 1)
self.assertGreaterEqual(counts["notifier"], 1)
self.assertGreaterEqual(counts["surfaceSecrets"], 1)
with tarfile.open(bundle_path, "r:gz") as tar:
members = tar.getnames()
self.assertIn("manifest/release.yaml", members)
self.assertTrue(any(name.startswith("sboms/sample-") for name in members))
self.assertIn("bootstrap/notify/notify.yaml", members)
self.assertIn("bootstrap/notify/notify-web.secret.example", members)
self.assertIn("containers/stellaops-containers.tar.gz", members)
self.assertIn("orchestrator/service/orchestrator-service.tar.gz", members)
self.assertIn("export-center/export-offline-bundle.tar.gz", members)
self.assertIn("notifier/notifier-offline-pack.tar.gz", members)
self.assertIn("surface-secrets/secrets-bundle.tar.gz", members)
if __name__ == "__main__":
unittest.main()