the-custodian/docs/changes/CUST-WP-0080/restart_forge_custody.py

59 lines
3.1 KiB
Python

#!/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()