Files
archy/scripts/tests/qualify_os_updater_boot.py

69 lines
3.8 KiB
Python

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