69 lines
3.8 KiB
Python
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))
|