Converge the S3 platform-service pattern onto ADR-0007, emit the reef-railiance live deployable inventory for the family coverage check, and mark T02 done. Declaration edits land in rapp-openbao and rapp-postgres.
143 lines
4.1 KiB
Python
Executable file
143 lines
4.1 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Capture live reef-railiance deployables for the family-declaration validator.
|
|
|
|
The validator in railiance-master consumes this file via
|
|
`--inventory` and must not grow a cluster dependency of its own.
|
|
Substrate namespaces and Knative revision Deployments are omitted so
|
|
the coverage check sees workload units, not rail machinery.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
from typing import Any
|
|
|
|
DEFAULT_REMOTE = "railiance01"
|
|
DEFAULT_REEF = "reef-railiance"
|
|
SOURCE = "railiance-platform"
|
|
|
|
SUBSTRATE_NAMESPACES = {
|
|
"cert-manager",
|
|
"cert-manager-test",
|
|
"cnpg-system",
|
|
"default",
|
|
"external-secrets",
|
|
"knative-serving",
|
|
"kube-node-lease",
|
|
"kube-public",
|
|
"kube-system",
|
|
"kourier-system",
|
|
"local-path-storage",
|
|
}
|
|
|
|
EPHEMERAL_NAMESPACE_SUFFIXES = ("-drill", "-test")
|
|
KNATIVE_REVISION_DEPLOY = re.compile(r".*-\d{5}-deployment$")
|
|
|
|
RESOURCE_SPECS = (
|
|
("Deployment", "deploy"),
|
|
("StatefulSet", "sts"),
|
|
("Cluster", "cluster.postgresql.cnpg.io"),
|
|
("KnativeService", "service.serving.knative.dev"),
|
|
)
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__.split("\n\n", 1)[0])
|
|
parser.add_argument(
|
|
"--remote",
|
|
default=DEFAULT_REMOTE,
|
|
help=f"SSH host that can reach the reef API (default: {DEFAULT_REMOTE})",
|
|
)
|
|
parser.add_argument(
|
|
"--reef",
|
|
default=DEFAULT_REEF,
|
|
help=f"Reef identifier recorded in the inventory (default: {DEFAULT_REEF})",
|
|
)
|
|
parser.add_argument(
|
|
"-o",
|
|
"--output",
|
|
help="Write JSON to this path instead of stdout",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def remote_get(remote: str, resource: str) -> dict[str, Any]:
|
|
cmd = [
|
|
"ssh",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
remote,
|
|
"kubectl",
|
|
"get",
|
|
resource,
|
|
"-A",
|
|
"-o",
|
|
"json",
|
|
]
|
|
result = subprocess.run(cmd, check=False, capture_output=True, text=True)
|
|
if result.returncode != 0:
|
|
stderr = (result.stderr or "").strip()
|
|
if "the server doesn't have a resource type" in stderr:
|
|
return {"items": []}
|
|
raise RuntimeError(f"{' '.join(cmd)} failed ({result.returncode}): {stderr}")
|
|
return json.loads(result.stdout)
|
|
|
|
|
|
def skip_namespace(name: str) -> bool:
|
|
if name in SUBSTRATE_NAMESPACES:
|
|
return True
|
|
return any(name.endswith(suffix) for suffix in EPHEMERAL_NAMESPACE_SUFFIXES)
|
|
|
|
|
|
def collect(remote: str) -> list[dict[str, str]]:
|
|
seen: set[tuple[str, str, str]] = set()
|
|
deployables: list[dict[str, str]] = []
|
|
for kind, resource in RESOURCE_SPECS:
|
|
payload = remote_get(remote, resource)
|
|
for item in payload.get("items") or []:
|
|
meta = item.get("metadata") or {}
|
|
name = meta.get("name")
|
|
namespace = meta.get("namespace") or ""
|
|
if not isinstance(name, str) or not name:
|
|
continue
|
|
if skip_namespace(namespace):
|
|
continue
|
|
if kind == "Deployment" and KNATIVE_REVISION_DEPLOY.fullmatch(name):
|
|
continue
|
|
key = (name, namespace, kind)
|
|
if key in seen:
|
|
continue
|
|
seen.add(key)
|
|
deployables.append({"name": name, "namespace": namespace, "kind": kind})
|
|
deployables.sort(key=lambda row: (row["namespace"], row["name"], row["kind"]))
|
|
return deployables
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
try:
|
|
deployables = collect(args.remote)
|
|
except (RuntimeError, json.JSONDecodeError) as exc:
|
|
print(f"capture failed: {exc}", file=sys.stderr)
|
|
return 1
|
|
inventory = {
|
|
"source": SOURCE,
|
|
"captured_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
"reef": args.reef,
|
|
"deployables": deployables,
|
|
}
|
|
text = json.dumps(inventory, indent=2) + "\n"
|
|
if args.output:
|
|
with open(args.output, "w", encoding="utf-8") as fh:
|
|
fh.write(text)
|
|
else:
|
|
sys.stdout.write(text)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|