Add Policy DSL Validator, Schema Exporter, and Simulation Smoke tools
- Implemented PolicyDslValidator with command-line options for strict mode and JSON output. - Created PolicySchemaExporter to generate JSON schemas for policy-related models. - Developed PolicySimulationSmoke tool to validate policy simulations against expected outcomes. - Added project files and necessary dependencies for each tool. - Ensured proper error handling and usage instructions across tools.
This commit is contained in:
232
ops/devops/release/test_verify_release.py
Normal file
232
ops/devops/release/test_verify_release.py
Normal file
@@ -0,0 +1,232 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
sys.path.append(str(Path(__file__).resolve().parent))
|
||||
|
||||
from build_release import write_manifest # type: ignore import-not-found
|
||||
from verify_release import VerificationError, compute_sha256, verify_release
|
||||
|
||||
|
||||
class VerifyReleaseTests(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.release_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
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:
|
||||
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", "externalParameters": {}},
|
||||
"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_verify_release_success(self) -> None:
|
||||
self._create_sample_release()
|
||||
# Should not raise
|
||||
verify_release(self.release_dir)
|
||||
|
||||
def test_verify_release_detects_sha_mismatch(self) -> None:
|
||||
self._create_sample_release()
|
||||
tampered = self.release_dir / "artifacts/sboms/sample.cyclonedx.json"
|
||||
tampered.write_text("tampered\n", encoding="utf-8")
|
||||
with self.assertRaises(VerificationError):
|
||||
verify_release(self.release_dir)
|
||||
|
||||
def test_verify_release_detects_missing_debug_file(self) -> None:
|
||||
self._create_sample_release()
|
||||
debug_file = self.release_dir / "debug/.build-id/ab/cdef.debug"
|
||||
debug_file.unlink()
|
||||
with self.assertRaises(VerificationError):
|
||||
verify_release(self.release_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user