Some checks failed
		
		
	
	Docs CI / lint-and-preview (push) Has been cancelled
				
			- 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.
		
			
				
	
	
		
			137 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python3
 | |
| """Package telemetry collector assets for offline/air-gapped installs.
 | |
| 
 | |
| Outputs a tarball containing the collector configuration, Compose overlay,
 | |
| Helm defaults, and operator README. A SHA-256 checksum sidecar is emitted, and
 | |
| optional Cosign signing can be enabled with --sign.
 | |
| """
 | |
| from __future__ import annotations
 | |
| 
 | |
| import argparse
 | |
| import hashlib
 | |
| import os
 | |
| import subprocess
 | |
| import sys
 | |
| import tarfile
 | |
| from pathlib import Path
 | |
| from typing import Iterable
 | |
| 
 | |
| REPO_ROOT = Path(__file__).resolve().parents[3]
 | |
| DEFAULT_OUTPUT = REPO_ROOT / "out" / "telemetry" / "telemetry-offline-bundle.tar.gz"
 | |
| BUNDLE_CONTENTS: tuple[Path, ...] = (
 | |
|     Path("deploy/telemetry/README.md"),
 | |
|     Path("deploy/telemetry/otel-collector-config.yaml"),
 | |
|     Path("deploy/telemetry/storage/README.md"),
 | |
|     Path("deploy/telemetry/storage/prometheus.yaml"),
 | |
|     Path("deploy/telemetry/storage/tempo.yaml"),
 | |
|     Path("deploy/telemetry/storage/loki.yaml"),
 | |
|     Path("deploy/telemetry/storage/tenants/tempo-overrides.yaml"),
 | |
|     Path("deploy/telemetry/storage/tenants/loki-overrides.yaml"),
 | |
|     Path("deploy/helm/stellaops/files/otel-collector-config.yaml"),
 | |
|     Path("deploy/helm/stellaops/values.yaml"),
 | |
|     Path("deploy/helm/stellaops/templates/otel-collector.yaml"),
 | |
|     Path("deploy/compose/docker-compose.telemetry.yaml"),
 | |
|     Path("deploy/compose/docker-compose.telemetry-storage.yaml"),
 | |
|     Path("docs/ops/telemetry-collector.md"),
 | |
|     Path("docs/ops/telemetry-storage.md"),
 | |
| )
 | |
| 
 | |
| 
 | |
| def compute_sha256(path: Path) -> str:
 | |
|     sha = hashlib.sha256()
 | |
|     with path.open("rb") as handle:
 | |
|         for chunk in iter(lambda: handle.read(1024 * 1024), b""):
 | |
|             sha.update(chunk)
 | |
|     return sha.hexdigest()
 | |
| 
 | |
| 
 | |
| def validate_files(paths: Iterable[Path]) -> None:
 | |
|     missing = [str(p) for p in paths if not (REPO_ROOT / p).exists()]
 | |
|     if missing:
 | |
|         raise FileNotFoundError(f"Missing bundle artefacts: {', '.join(missing)}")
 | |
| 
 | |
| 
 | |
| def create_bundle(output_path: Path) -> Path:
 | |
|     output_path.parent.mkdir(parents=True, exist_ok=True)
 | |
|     with tarfile.open(output_path, "w:gz") as tar:
 | |
|         for rel_path in BUNDLE_CONTENTS:
 | |
|             abs_path = REPO_ROOT / rel_path
 | |
|             tar.add(abs_path, arcname=str(rel_path))
 | |
|     return output_path
 | |
| 
 | |
| 
 | |
| def write_checksum(bundle_path: Path) -> Path:
 | |
|     digest = compute_sha256(bundle_path)
 | |
|     sha_path = bundle_path.with_suffix(bundle_path.suffix + ".sha256")
 | |
|     sha_path.write_text(f"{digest}  {bundle_path.name}\n", encoding="utf-8")
 | |
|     return sha_path
 | |
| 
 | |
| 
 | |
| def cosign_sign(bundle_path: Path, key_ref: str | None, identity_token: str | None) -> None:
 | |
|     cmd = ["cosign", "sign-blob", "--yes", str(bundle_path)]
 | |
|     if key_ref:
 | |
|         cmd.extend(["--key", key_ref])
 | |
|     env = os.environ.copy()
 | |
|     if identity_token:
 | |
|         env["COSIGN_IDENTITY_TOKEN"] = identity_token
 | |
|     try:
 | |
|         subprocess.run(cmd, check=True, env=env)
 | |
|     except FileNotFoundError as exc:
 | |
|         raise RuntimeError("cosign not found on PATH; install cosign or omit --sign") from exc
 | |
|     except subprocess.CalledProcessError as exc:
 | |
|         raise RuntimeError(f"cosign sign-blob failed: {exc}") from exc
 | |
| 
 | |
| 
 | |
| def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
 | |
|     parser = argparse.ArgumentParser(description=__doc__)
 | |
|     parser.add_argument(
 | |
|         "--output",
 | |
|         type=Path,
 | |
|         default=DEFAULT_OUTPUT,
 | |
|         help=f"Output bundle path (default: {DEFAULT_OUTPUT})",
 | |
|     )
 | |
|     parser.add_argument(
 | |
|         "--sign",
 | |
|         action="store_true",
 | |
|         help="Sign the bundle using cosign (requires cosign on PATH)",
 | |
|     )
 | |
|     parser.add_argument(
 | |
|         "--cosign-key",
 | |
|         type=str,
 | |
|         default=os.environ.get("COSIGN_KEY_REF"),
 | |
|         help="Cosign key reference (file:..., azurekms://..., etc.)",
 | |
|     )
 | |
|     parser.add_argument(
 | |
|         "--identity-token",
 | |
|         type=str,
 | |
|         default=os.environ.get("COSIGN_IDENTITY_TOKEN"),
 | |
|         help="OIDC identity token for keyless signing",
 | |
|     )
 | |
|     return parser.parse_args(argv)
 | |
| 
 | |
| 
 | |
| def main(argv: list[str] | None = None) -> int:
 | |
|     args = parse_args(argv)
 | |
|     validate_files(BUNDLE_CONTENTS)
 | |
| 
 | |
|     bundle_path = args.output.resolve()
 | |
|     print(f"[*] Creating telemetry bundle at {bundle_path}")
 | |
|     create_bundle(bundle_path)
 | |
|     sha_path = write_checksum(bundle_path)
 | |
|     print(f"[✓] SHA-256 written to {sha_path}")
 | |
| 
 | |
|     if args.sign:
 | |
|         print("[*] Signing bundle with cosign")
 | |
|         cosign_sign(bundle_path, args.cosign_key, args.identity_token)
 | |
|         sig_path = bundle_path.with_suffix(bundle_path.suffix + ".sig")
 | |
|         if sig_path.exists():
 | |
|             print(f"[✓] Cosign signature written to {sig_path}")
 | |
|         else:
 | |
|             print("[!] Cosign completed but signature file not found (ensure cosign version >= 2.2)")
 | |
| 
 | |
|     return 0
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     sys.exit(main())
 |