"""Render the MAS facility manifest for a power state. This file does not talk to the cluster. power.sh and apply.sh do. """ from __future__ import annotations import sys SWITCHABLE = ("mas-mx", "mas-statute") def render(manifest: str, image: str, power: str) -> str: """Return the manifest with IMAGE filled in and replicas set from power. `on` keeps one replica of each switchable Deployment. `off` sets both to zero. Services, the evidence volume, the Ingress, and the ExternalSecret are copied through unchanged. """ if power not in ("on", "off"): raise ValueError(f"power must be on or off, got {power!r}") if not image or any(ch.isspace() for ch in image): raise ValueError("image must be a single token") if "image: IMAGE" not in manifest: raise ValueError("manifest has no image: IMAGE placeholder") rendered = manifest.replace("image: IMAGE", f"image: {image}") docs = rendered.split("\n---\n") seen: set[str] = set() out: list[str] = [] for doc in docs: name = _deployment_name(doc) if name in SWITCHABLE: seen.add(name) doc = _set_replicas(doc, 0 if power == "off" else 1) out.append(doc) missing = [name for name in SWITCHABLE if name not in seen] if missing: raise ValueError("manifest is missing Deployments: " + ", ".join(missing)) return "\n---\n".join(out) def _deployment_name(doc: str) -> str | None: kind = None name = None for line in doc.splitlines(): if line.startswith("kind:"): kind = line.split(":", 1)[1].strip() elif line.startswith(" name:") and name is None: name = line.split(":", 1)[1].strip() if kind == "Deployment": return name return None def _set_replicas(doc: str, count: int) -> str: lines = doc.splitlines(keepends=True) found = 0 out = [] for line in lines: if line.startswith(" replicas:"): found += 1 newline = "\n" if line.endswith("\n") else "" out.append(f" replicas: {count}{newline}") else: out.append(line) if found != 1: raise ValueError(f"expected one replicas field, found {found}") return "".join(out) def main(argv: list[str]) -> int: if len(argv) != 4 or argv[1] != "render": print("usage: facility.py render IMAGE on|off", file=sys.stderr) return 2 sys.stdout.write(render(sys.stdin.read(), argv[2], argv[3])) return 0 if __name__ == "__main__": sys.exit(main(sys.argv))