#!/usr/bin/env python3 """Repeat custody acceptance on a new pod, preserving all existing annotations.""" import argparse import datetime import hashlib import importlib.util import json from pathlib import Path HERE = Path(__file__).parent s=importlib.util.spec_from_file_location('custody',HERE/'deploy_forge_custody.py') m=importlib.util.module_from_spec(s) s.loader.exec_module(m) t=m.t def main(): parser=argparse.ArgumentParser(description=__doc__) parser.add_argument('--proposal',type=Path,required=True) parser.add_argument('--receipt',type=Path,required=True) parser.add_argument('--apply',action='store_true') args=parser.parse_args() p=json.loads(args.proposal.read_text()) for file,expected in p['executor_hashes'].items(): assert hashlib.sha256((HERE/file).read_bytes()).hexdigest()==expected,'executor-changed' d=t.deployment() assert t.digest(d['spec']['template'])==p['original_template_digest'],'deployment-changed' original=json.loads((HERE/'receive-hook-proposal.json').read_text()) pod=t.target_pod(d,original) annotations=dict(d['spec']['template']['metadata'].get('annotations',{})) assert p['annotation_key'] not in annotations,'marker-preexists' annotations[p['annotation_key']]=p['restart_marker'] patch=[{'op':'test','path':'/metadata/uid','value':p['deployment_uid']}, {'op':'test','path':'/metadata/resourceVersion','value':d['metadata']['resourceVersion']}, {'op':'add','path':'/spec/template/metadata/annotations','value':annotations}] local=t.apply_locally(d,patch) dry=t.patch_remote(patch,dry_run=True) assert t.digest(local['spec']['template'])==t.digest(dry['spec']['template']),'dry-run-differs' receipt={'proposal_id':p['proposal_id'],'revision':p['revision'], 'proposal_sha256':hashlib.sha256(args.proposal.read_bytes()).hexdigest(), 'at':datetime.datetime.now(datetime.timezone.utc).isoformat(), 'before_pod_uid':pod['pod_uid'],'dry_run_verified':True,'result':'dry-run-verified'} args.receipt.write_text(json.dumps(receipt,indent=2)+'\n') if args.apply: applied=t.patch_remote(patch) receipt.update(generation=applied['metadata']['generation'],result='rollout-started') args.receipt.write_text(json.dumps(receipt,indent=2)+'\n') after,new=t.wait_ready(original,t.TEMPLATE) assert new['pod_uid']!=pod['pod_uid'],'pod-not-replaced' assert t.digest(after['spec']['template'])==t.digest(local['spec']['template']),'deployment-changed' raw=json.loads(t.native.command(['-n','forgejo','get','pod',new['pod'],'-o','json'])) status=next(x for x in raw['status']['initContainerStatuses'] if x['name']=='verify-secret-scanner') assert status['state']['terminated']['exitCode']==0,'guard-not-completed' receipt.update(after_pod_uid=new['pod_uid'],guard_completed=True, after_probe=m.verify(new,'restart-'+str(after['metadata']['generation'])),result='verified') args.receipt.write_text(json.dumps(receipt,indent=2)+'\n') print(json.dumps(receipt)) if __name__=='__main__': main()