40 lines
2.2 KiB
Python
40 lines
2.2 KiB
Python
|
|
"""Run native delivery/scope/rotation acceptance; no attended admin or secret output."""
|
||
|
|
import copy,json,subprocess,time,uuid
|
||
|
|
from pathlib import Path
|
||
|
|
from keycape_factor_activate import CLUSTER,kubectl
|
||
|
|
ROOT=Path(__file__).resolve().parents[1]
|
||
|
|
def run():
|
||
|
|
if kubectl("get","ns","kube-system","-o","json")["metadata"]["uid"]!=CLUSTER:raise ValueError()
|
||
|
|
cron=kubectl("-n","sso","get","cronjob","keycape-factor-renewer","-o","json")
|
||
|
|
spec=copy.deepcopy(cron["spec"]["jobTemplate"]["spec"])
|
||
|
|
spec.update(activeDeadlineSeconds=300,backoffLimit=0)
|
||
|
|
p=spec["template"]["spec"];p["serviceAccountName"]="keycape-factor-eso"
|
||
|
|
p["containers"][0]["command"]=["python3","-c",(ROOT/"scripts/keycape_factor_delivery_probe.py").read_text()]
|
||
|
|
p["volumes"].append({"name":"factor","secret":{"secretName":"keycape-factor-read","defaultMode":288}})
|
||
|
|
p["containers"][0]["volumeMounts"].append({"name":"factor","mountPath":"/factor","readOnly":True})
|
||
|
|
suffix=uuid.uuid4().hex[:8];name="keycape-factor-proof-"+suffix
|
||
|
|
kubectl("create","-f","-","-o","json",payload={"apiVersion":"batch/v1","kind":"Job","metadata":{"name":name,"namespace":"sso"},"spec":spec})
|
||
|
|
deadline=time.monotonic()+310;rotating=False
|
||
|
|
while time.monotonic()<deadline:
|
||
|
|
r=subprocess.run(["kubectl","-n","sso","logs","job/"+name],capture_output=True,text=True,timeout=15)
|
||
|
|
results=[]
|
||
|
|
if r.returncode==0:
|
||
|
|
for line in r.stdout.splitlines():
|
||
|
|
try:results.append(json.loads(line))
|
||
|
|
except ValueError:pass
|
||
|
|
if results and not rotating:
|
||
|
|
if results[0].get("phase")!="awaiting_rotation":raise ValueError()
|
||
|
|
renewal={"apiVersion":"batch/v1","kind":"Job","metadata":{"name":"keycape-factor-rotate-"+suffix,"namespace":"sso"},"spec":cron["spec"]["jobTemplate"]["spec"]}
|
||
|
|
kubectl("create","-f","-","-o","json",payload=renewal);rotating=True
|
||
|
|
print(json.dumps({"phase":"waiting_for_mounted_rotation","job":name}),flush=True)
|
||
|
|
if results and "phase" not in results[-1]:
|
||
|
|
result={k:v for k,v in results[-1].items() if isinstance(v,bool)}
|
||
|
|
result["job"]=name
|
||
|
|
return result
|
||
|
|
time.sleep(5)
|
||
|
|
raise TimeoutError()
|
||
|
|
if __name__=="__main__":
|
||
|
|
try:result=run()
|
||
|
|
except Exception:result={"success":False,"failure":"native acceptance failed or timed out"}
|
||
|
|
print(json.dumps(result),flush=True);raise SystemExit(0 if result.get("success") else 1)
|