Some checks failed
Use-case regression gate / usecase-regressions (push) Has been cancelled
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
123 lines
5.7 KiB
Python
123 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Read-only native observation and private capsule preparation, after storage release.
|
|
|
|
Run on the content node. No backup, scale, pod creation, schema/date update,
|
|
credential delivery or timer activation is performed. Server dry-run is used.
|
|
"""
|
|
|
|
import argparse
|
|
import base64
|
|
import json
|
|
import os
|
|
import runpy
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
from source_date_supervisor import (
|
|
CLAIM,
|
|
NS,
|
|
Capsule,
|
|
Node,
|
|
Refused,
|
|
Supervisor,
|
|
canonical,
|
|
sha,
|
|
sync_directory,
|
|
)
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def observe(plan, output, *, image, cluster_uid, deployment_uid, pvc_uid, node=None):
|
|
if os.geteuid() != 0:
|
|
raise Refused("native_node_root_read_context_required")
|
|
output = output.absolute()
|
|
if (output.exists() or any(p.is_symlink() for p in [output, *output.parents])
|
|
or output.resolve().is_relative_to(ROOT)
|
|
or not output.resolve().is_relative_to(Path("/tmp"))):
|
|
raise Refused("fresh_private_tmp_output_outside_checkout_required")
|
|
node = node or Node()
|
|
dep = node.doc("get", "deployment", NS, "-n", NS)
|
|
spec = dep["spec"]
|
|
if (dep["metadata"]["uid"] != deployment_uid or spec["replicas"] != 1
|
|
or len(spec["template"]["spec"]["containers"]) != 1
|
|
or spec["template"]["spec"]["containers"][0]["image"] != image):
|
|
raise Refused("storage_release_or_target_prerequisite_missing")
|
|
if node.doc("get", "namespace", "kube-system")["metadata"]["uid"] != cluster_uid:
|
|
raise Refused("cluster_changed")
|
|
pvc = node.doc("get", "pvc", CLAIM, "-n", NS)
|
|
if pvc["metadata"]["uid"] != pvc_uid or pvc["status"]["phase"] != "Bound":
|
|
raise Refused("pvc_changed")
|
|
pods = [p for p in node.doc("get", "pods", "-n", NS)["items"]
|
|
if p.get("status", {}).get("phase") not in {"Succeeded", "Failed"}
|
|
and any(v.get("persistentVolumeClaim", {}).get("claimName") == CLAIM
|
|
for v in p["spec"].get("volumes", []))]
|
|
if len(pods) != 1:
|
|
raise Refused("single_application_pod_required")
|
|
host = node.doc("get", "node", pods[0]["spec"]["nodeName"])
|
|
node.verify_local_node(host)
|
|
pins = node.installation_pins()
|
|
history = json.loads(node.command(["helm", "history", NS, "-n", NS, "-o", "json"]))
|
|
if not history or history[-1]["status"] != "deployed":
|
|
raise Refused("stable_helm_release_required")
|
|
target = {"cluster_uid": cluster_uid, "deployment_uid": deployment_uid, "pvc_uid": pvc_uid,
|
|
"node_name": host["metadata"]["name"], "node_uid": host["metadata"]["uid"],
|
|
"image": image, "deployment_spec_sha256": sha(canonical(spec)),
|
|
"helm_revision": history[-1]["revision"],
|
|
"helm_manifest_sha256": sha(node.command(["helm", "get", "manifest", NS, "-n", NS])),
|
|
**pins}
|
|
modules = {name: (ROOT / "coulomb_social/apps/spaces" / name).read_bytes() for name in
|
|
("date_backfill.py", "date_backfill_journal.py", "date_backfill_operation.py")}
|
|
payload = {"mode": "probe", "database_name": "coulomb_social_db",
|
|
"modules": {n: base64.b64encode(v).decode() for n, v in modules.items()},
|
|
"module_hashes": {n: sha(v) for n, v in modules.items()}}
|
|
baseline = json.loads(node.command(
|
|
["kubectl", "exec", "-i", "-n", NS, pods[0]["metadata"]["name"], "--", "env",
|
|
"PYTHONDONTWRITEBYTECODE=1", "python", "-c",
|
|
(ROOT / "scripts/source_date_wire.py").read_text()], canonical(payload)))["fingerprint"]
|
|
prepare = runpy.run_path(str(ROOT / "scripts/prepare-source-date-capsule.py"))["prepare"]
|
|
with tempfile.TemporaryDirectory(prefix="csoc-native-date-observation-") as temporary:
|
|
work = Path(temporary)
|
|
(work / "baseline.json").write_bytes(canonical(baseline))
|
|
(work / "target.json").write_bytes(canonical(target))
|
|
prepared = prepare(plan, work / "baseline.json", work / "target.json", work / "capsule")
|
|
capsule = Capsule(work / "capsule", prepared["proposal_digest"])
|
|
check = Supervisor(capsule, node)
|
|
_, proof = check.preflight()
|
|
output.mkdir(mode=0o700)
|
|
# Publish exactly the already checked capsule, retaining its attempt id.
|
|
for name in [*capsule.content, "manifest.json"]:
|
|
data = (work / "capsule" / name).read_bytes()
|
|
fd = os.open(output / name, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
|
with os.fdopen(fd, "wb") as stream:
|
|
stream.write(data)
|
|
stream.flush()
|
|
os.fsync(stream.fileno())
|
|
sync_directory(output)
|
|
sync_directory(output.parent)
|
|
return {**prepared, "status": "native_read_only_capsule_prepared_not_admitted",
|
|
"production_access": True,
|
|
"baseline_rechecked": proof["fingerprint"] == baseline,
|
|
"helper_server_dry_run_sha256": check.preview_helper_spec,
|
|
"capture_performed": False, "live_changes": False}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--plan", type=Path, required=True)
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
parser.add_argument("--image", required=True)
|
|
parser.add_argument("--cluster-uid", required=True)
|
|
parser.add_argument("--deployment-uid", required=True)
|
|
parser.add_argument("--pvc-uid", required=True)
|
|
args = parser.parse_args()
|
|
print(json.dumps(observe(args.plan, args.output, image=args.image, cluster_uid=args.cluster_uid,
|
|
deployment_uid=args.deployment_uid, pvc_uid=args.pvc_uid)))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except Exception as exc:
|
|
print(json.dumps({"status": "observation_refused", "failure_type": type(exc).__name__}))
|
|
raise SystemExit(1)
|