#!/usr/bin/env python3 """Prepare a fixed email-connect sender candidate using reads and server dry-runs. No create/apply/patch is executed without --dry-run=server. This does not admit OpenBao access, install a resource, scan a mailbox or send an email. """ import argparse import hashlib import json import os import shlex import subprocess from pathlib import Path parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--output", type=Path, required=True) args = parser.parse_args() if not __debug__: raise SystemExit("Assertions must be enabled") if args.output.exists(): raise SystemExit("Choose a new candidate output; existing evidence is retained") code = r""" import subprocess, json, hashlib from datetime import datetime, timezone K = ["k3s", "kubectl"] def get(kind, name): return json.loads( subprocess.check_output( K + ["get", kind, name, "-n", "email-connect", "-o", "json"], timeout=20 ) ) d = get("deployment", "email-connect") cm = get("configmap", "email-connect-config") assert d["metadata"]["uid"] == "2e98e52a-a2d1-4c52-9ac8-6efbc0a18e72" assert ( d["status"]["observedGeneration"] == d["metadata"]["generation"] and d["status"]["readyReplicas"] == 1 ) c = d["spec"]["template"]["spec"]["containers"][0] assert c["name"] == "email-connect" and len(d["spec"]["template"]["spec"]["containers"]) == 1 assert ( c["image"] == "forgejo.coulomb.social/coulomb/email-connect@sha256:" "5d79fd1fec15d3a279383820f170d263de3a55f5308df01a0a34732ebaea6d3d" ) assert c["envFrom"] == [ {"configMapRef": {"name": "email-connect-config"}}, {"secretRef": {"name": "email-connect-runtime", "optional": False}}, ] assert cm["data"]["EMAIL_CONNECT_SENDER"] == "forgejo@coulomb.social" assert all( e["name"] not in {"EMAIL_CONNECT_SMTP_USERNAME", "EMAIL_CONNECT_SMTP_PASSWORD", "EMAIL_CONNECT_SENDER"} for e in c["env"] ) assert c["env"] == [ {"name": "EMAIL_CONNECT_CALLERS_FILE", "value": "/etc/email-connect-callers/callers.json"} ], "unexpected environment; refuse to copy unknown values" role = "email-connect-platform-smtp-eso" store = "openbao-email-connect-platform-smtp" secret = "email-connect-platform-smtp" def obj(api, kind, name, **extra): return { "apiVersion": api, "kind": kind, "metadata": {"name": name, "namespace": "email-connect"}, **extra, } resources = [ obj("v1", "ServiceAccount", role, automountServiceAccountToken=False), obj( "external-secrets.io/v1", "SecretStore", store, spec={ "provider": { "vault": { "server": "http://openbao.openbao.svc:8200", "path": "platform", "version": "v2", "auth": { "kubernetes": { "mountPath": "kubernetes", "role": role, "serviceAccountRef": {"name": role, "audiences": ["openbao"]}, } }, } } }, ), obj( "external-secrets.io/v1", "ExternalSecret", secret, spec={ "refreshInterval": "1h", "secretStoreRef": {"kind": "SecretStore", "name": store}, "target": { "name": secret, "creationPolicy": "Owner", "deletionPolicy": "Retain", "template": {"type": "Opaque"}, }, "data": [ { "secretKey": "password", "remoteRef": { "key": "workloads/railiance-telemetry/smtp", "property": "SMTP_PASSWORD", }, } ], }, ), ] for x in resources: out = subprocess.check_output( K + [ "get", x["kind"], x["metadata"]["name"], "-n", "email-connect", "--ignore-not-found", "-o", "name", ], timeout=20, ) assert not out.strip(), "candidate resource already exists" base = "/spec/template/spec/containers/0" dpatch = [ {"op": "test", "path": "/metadata/uid", "value": d["metadata"]["uid"]}, {"op": "test", "path": "/metadata/resourceVersion", "value": d["metadata"]["resourceVersion"]}, {"op": "test", "path": base + "/image", "value": c["image"]}, {"op": "test", "path": base + "/envFrom", "value": c["envFrom"]}, {"op": "test", "path": base + "/env", "value": c["env"]}, { "op": "replace", "path": base + "/envFrom", "value": [{"configMapRef": {"name": "email-connect-config"}}], }, { "op": "replace", "path": base + "/env", "value": c["env"] + [ {"name": "EMAIL_CONNECT_SMTP_USERNAME", "value": "platform@coulomb.social"}, { "name": "EMAIL_CONNECT_SMTP_PASSWORD", "valueFrom": { "secretKeyRef": {"name": secret, "key": "password", "optional": False} }, }, ], }, ] cmpatch = [ {"op": "test", "path": "/metadata/uid", "value": cm["metadata"]["uid"]}, {"op": "test", "path": "/data/EMAIL_CONNECT_SENDER", "value": "forgejo@coulomb.social"}, {"op": "replace", "path": "/data/EMAIL_CONNECT_SENDER", "value": "platform@coulomb.social"}, ] def patchdry(kind, name, patch): return json.loads( subprocess.check_output( K + [ "patch", kind, name, "-n", "email-connect", "--type=json", "--dry-run=server", "-p", json.dumps(patch), "-o", "json", ], timeout=30, ) ) checks = [] for x in resources: p = subprocess.run( K + ["create", "--dry-run=server", "-f", "-", "-o", "json"], input=json.dumps(x).encode(), capture_output=True, timeout=30, ) if p.returncode: raise RuntimeError("resource dry-run failed: " + x["kind"]) y = json.loads(p.stdout) checks.append({"kind": y["kind"], "name": y["metadata"]["name"], "accepted": True}) da = patchdry("deployment", "email-connect", dpatch) ca = patchdry("configmap", "email-connect-config", cmpatch) expected = json.loads(json.dumps(d["spec"])) expected["template"]["spec"]["containers"][0]["envFrom"] = dpatch[-2]["value"] expected["template"]["spec"]["containers"][0]["env"] = dpatch[-1]["value"] assert da["spec"] == expected, "unexpected deployment spec change" expectedcm = dict(cm["data"], EMAIL_CONNECT_SENDER="platform@coulomb.social") assert ca["data"] == expectedcm checks.extend( [ { "kind": "Deployment", "name": "email-connect", "accepted": True, "only_envFrom_and_env_changed": True, }, { "kind": "ConfigMap", "name": "email-connect-config", "accepted": True, "only_sender_changed": True, }, ] ) volumes = d["spec"]["template"]["spec"]["volumes"] policy_name = next(v["configMap"]["name"] for v in volumes if v["name"] == "caller-policy") pol = get("configmap", policy_name)["data"]["callers.json"] parsed = json.loads(pol) publiccallers = [ {k: x.get(k) for k in ("caller_id", "operations", "templates")} for x in parsed["callers"] ] print( json.dumps( { "schema": "coulomb.platform-mail-sender-candidate.v1", "observed_at": datetime.now(timezone.utc).isoformat(), "deployment_uid": d["metadata"]["uid"], "deployment_generation": d["metadata"]["generation"], "image": c["image"], "new_resources": resources, "deployment_json_patch": dpatch, "configmap_json_patch": cmpatch, "native_server_dry_run": checks, "live_caller_policy": { "name": policy_name, "sha256": hashlib.sha256(pol.encode()).hexdigest(), "callers": publiccallers, }, "platform_mailbox_admission_executed": False, "resources_installed": False, "mail_sent": False, "secrets_read": False, "execution_ready": False, } ) ) """ r = subprocess.run( [ "ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=10", "railiance01", "sudo -n python3 -c " + shlex.quote(code), ], capture_output=True, text=True, timeout=180, ) if r.returncode: raise SystemExit("read/dry-run preparation failed: " + r.stderr[-1500:]) j = json.loads(r.stdout) j["preparer_sha256"] = hashlib.sha256(Path(__file__).read_bytes()).hexdigest() fd = os.open(args.output, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) with os.fdopen(fd, "w") as target: json.dump(j, target, indent=2) target.write("\n") target.flush() os.fsync(target.fileno()) print( json.dumps( { k: j[k] for k in ( "observed_at", "deployment_generation", "native_server_dry_run", "live_caller_policy", "execution_ready", ) }, indent=2, ) )