#!/usr/bin/python3 """Retain outside history, export updated kernel, boot same applied disk via BIOS.""" import argparse import fcntl import hashlib import io import json from pathlib import Path import subprocess import sys import tarfile import time p=argparse.ArgumentParser(description=__doc__) p.add_argument('--applied',type=Path,required=True) p.add_argument('--output',type=Path,required=True) p.add_argument('--wait-applied',type=int,default=0) a=p.parse_args() deadline=time.monotonic()+a.wait_applied while True: try: result=json.loads((a.applied/'result.json').read_text()) if result['passed']:break if result['results'][-1]['name']=='activation': raise RuntimeError('actual activation fixture failed; retain its evidence') except (FileNotFoundError,json.JSONDecodeError): pass if time.monotonic()>=deadline:raise TimeoutError('successful actual activation required') time.sleep(2) applied=json.loads((a.applied/'activation/result.json').read_text()) effects=json.loads((a.applied/'activation/os-ota-stage/bootstrap-result.json').read_text()) assert effects['passed'] and effects['accepted_native_artifact'] disk=Path(applied['disk']) a.output.mkdir(mode=0o755,exist_ok=False) source=Path(__file__).resolve().parents[2] with open('/tmp/opencode/release-heavy.lock','a') as lock: fcntl.flock(lock,fcntl.LOCK_EX) with (a.output/'external-ledger-export.log').open('x') as log: subprocess.run(['/usr/sbin/debugfs','-R',f'dump /var/lib/archipelago-d09/ledger.json {a.output}/external-ledger.json',str(disk)],stdout=log,stderr=subprocess.STDOUT,check=True) with (a.output/'external-binding-export.log').open('x') as log: subprocess.run(['/usr/sbin/debugfs','-R',f'dump /etc/archipelago-d09/binding.json {a.output}/external-binding.json',str(disk)],stdout=log,stderr=subprocess.STDOUT,check=True) ledger=(a.output/'external-ledger.json').read_bytes() binding=(a.output/'external-binding.json').read_bytes() raw=json.dumps(dict(ledger_sha256=hashlib.sha256(ledger).hexdigest(), binding_sha256=hashlib.sha256(binding).hexdigest(),effects=effects),sort_keys=True,separators=(',',':')).encode() (a.output/'external-history.json').write_bytes(raw) with tarfile.open(a.output/'observer-input.tar','x') as archive: entry=tarfile.TarInfo('bootstrap-external.json');entry.uid=entry.gid=0;entry.mode=0o600;entry.size=len(raw) archive.addfile(entry,io.BytesIO(raw)) guest=(source/'scripts/tests/os_updater_postboot_guest.py').read_text() # Prefix after the docstring is unnecessary: this generated script has the # hash appointment before imports; the original source is retained alongside. (a.output/'postboot-guest.py').write_text('EXPECTED_EXTERNAL_SHA256='+repr(hashlib.sha256(raw).hexdigest())+'\n'+guest) release=effects['os_status']['events'][-1]['details']['boot']['release'] harness=source/'scripts/tests/os_ota_installed_vm.py' results=[] for name,args in ( ('export',['export','--disk',str(disk),'--kernel-release',release]), ('boot',['boot','--disk',str(disk),'--kernel',str(a.output/'export'/('vmlinuz-'+release)), '--initrd',str(a.output/'export'/('initrd.img-'+release)), '--guest',str(a.output/'postboot-guest.py'),'--payload',str(a.output/'observer-input.tar'),'--offline','--firmware'])): argv=[sys.executable,'-B',str(harness),'--output',str(a.output/name),*args] with (a.output/(name+'.log')).open('x') as log: result=subprocess.run(argv,stdout=log,stderr=subprocess.STDOUT) results.append(dict(name=name,exit=result.returncode,argv=argv)) (a.output/'result.json').write_text(json.dumps(dict(passed=False,results=results),indent=2)) if result.returncode:raise SystemExit(result.returncode) (a.output/'result.json').write_text(json.dumps(dict(passed=True,results=results),indent=2))