Files
archy/scripts/tests/qualify_os_ota_crypto.py

88 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""Fresh installed Debian -> genuine fixture crypto -> bootstrap/D09 -> BIOS.
One durable CPU2/8G/no-swap job; each heavyweight child takes release-heavy.lock.
The minimal native artifact's test anchor is not production/candidate trust.
"""
import argparse
import fcntl
import importlib.util
import json
from pathlib import Path
import subprocess
import sys
import tarfile
p=argparse.ArgumentParser(description=__doc__)
p.add_argument('--output',type=Path,required=True)
p.add_argument('--rootfs',type=Path,required=True)
p.add_argument('--receipt',type=Path,required=True)
p.add_argument('--crypto-fixture',type=Path,required=True)
p.add_argument('--production-control',type=Path,required=True)
p.add_argument('--baseline-only',action='store_true')
p.add_argument('--baseline-disk',type=Path)
p.add_argument('--baseline-boot',type=Path)
a=p.parse_args()
a.output.mkdir(mode=0o755,exist_ok=False)
source=Path(__file__).resolve().parents[2]
live_source=source
with open('/tmp/opencode/release-heavy.lock','a') as freeze_lock:
fcntl.flock(freeze_lock,fcntl.LOCK_EX)
spec=importlib.util.spec_from_file_location('vm_freeze',source/'scripts/tests/os_ota_installed_vm.py')
vm=importlib.util.module_from_spec(spec);spec.loader.exec_module(vm)
manifest=vm.source_archive(source,a.output/'frozen-source.tar')
source=a.output/'frozen-source'
source.mkdir(mode=0o755)
with tarfile.open(a.output/'frozen-source.tar') as archive:
archive.extractall(source,filter='data')
assert all(vm.sha(source/name)==expected for name,expected in manifest.items())
(a.output/'frozen-source-sha256.json').write_text(json.dumps(manifest,indent=2)+'\n')
harness=source/'scripts/tests/os_ota_installed_vm.py'
results=[]
def step(name,*args):
command=[sys.executable,'-B',str(harness),'--output',str(a.output/name),*map(str,args)]
with (a.output/(name+'.driver.log')).open('x') as log:
r=subprocess.run(command,stdout=log,stderr=subprocess.STDOUT)
results.append(dict(name=name,exit=r.returncode,command=command))
(a.output/'progress.json').write_text(json.dumps(results,indent=2)+'\n')
r.check_returncode()
try:
if a.baseline_disk:
assert a.baseline_boot
step('disk','clone','--disk',a.baseline_disk)
baseline=a.baseline_boot
else:
step('disk','prepare','--rootfs',a.rootfs,'--receipt',a.receipt,'--kernel-release','6.12.73+deb13-amd64')
step('baseline','boot','--disk',a.output/'disk/installed.raw',
'--kernel',a.output/'disk/vmlinuz-6.12.73+deb13-amd64',
'--initrd',a.output/'disk/initrd.img-6.12.73+deb13-amd64',
'--source-root',source,'--guest',source/'tests/regression/os-ota-crypto-baseline.py')
step('baseline-boot','export','--disk',a.output/'disk/installed.raw','--kernel-release','6.12.107+deb13-amd64')
baseline=a.output/'baseline-boot'
if not a.baseline_only:
step('activation','boot','--disk',a.output/'disk/installed.raw',
'--kernel',baseline/'vmlinuz-6.12.107+deb13-amd64',
'--initrd',baseline/'initrd.img-6.12.107+deb13-amd64',
'--source-root',source,'--crypto-fixture',a.crypto_fixture,'--native-artifact',a.production_control,
'--guest',source/'tests/regression/os-ota-crypto-bootstrap.py')
crypto=json.loads((a.output/'activation/os-ota-stage/crypto-result.json').read_text())
release=crypto['target_kernel']
step('updated-boot','export','--disk',a.output/'disk/installed.raw','--kernel-release',release)
# Retain the complete preboot filesystem for independent observer retries.
# Failed observer/readmission disks are never reset or reused.
step('postboot-disk','clone','--disk',a.output/'disk/installed.raw')
step('postboot','boot','--disk',a.output/'postboot-disk/installed.raw',
'--kernel',a.output/'updated-boot'/('vmlinuz-'+release),
'--initrd',a.output/'updated-boot'/('initrd.img-'+release),'--offline','--firmware',
'--preboot-evidence',a.output/'activation/os-ota-stage/same-boot-complete-ledger.json',
'--guest',source/'tests/regression/os-ota-installed-owner-postboot.py')
finally:
source_changes=[name for name,expected in manifest.items() if vm.sha(source/name)!=expected]
live_changes=[name for name,expected in manifest.items() if not (live_source/name).exists() or vm.sha(live_source/name)!=expected]
(a.output/'source-after.json').write_text(json.dumps(dict(frozen_changes=source_changes,live_changes=live_changes),indent=2)+'\n')
passed=bool(results) and all(r['exit']==0 for r in results) and not source_changes
(a.output/'result.json').write_text(json.dumps(dict(passed=passed,
full_flow_under_isolated_test_anchor=passed and not a.baseline_only,
frozen_source_unchanged=not source_changes,source_manifest_sha256=vm.sha(a.output/'frozen-source-sha256.json'),
baseline_only=a.baseline_only,production_approved=False,candidate_qualified=False,results=results),indent=2)+'\n')