91 lines
4.3 KiB
Python
91 lines
4.3 KiB
Python
#!/usr/bin/python3
|
|
"""Second stage after restart of the SAME disposable rootless PID1 container.
|
|
|
|
This is container/PID1 restart qualification, not a physical-node reboot or
|
|
native artifact/terminal acceptance. Prior authority evidence stays intact.
|
|
"""
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import secrets
|
|
import socket
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
assert os.geteuid() == 0 and socket.gethostname() == 'd09-qualification'
|
|
assert Path('/run/.containerenv').exists() and os.environ.get('D09_DISPOSABLE_SYSTEMD') == '1'
|
|
PACKAGE = Path('/opt/d09-fixture')
|
|
sys.path.insert(0, str(PACKAGE / 'scripts'))
|
|
import d09_store as store
|
|
import d09_wire as wire
|
|
spec = importlib.util.spec_from_file_location('provision', PACKAGE / 'scripts/provision-d09-lifecycle.py')
|
|
provision = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(provision)
|
|
owner = provision.owner
|
|
|
|
|
|
def run(*args):
|
|
return subprocess.run(args, check=True, capture_output=True, text=True, timeout=45)
|
|
|
|
|
|
def wait(predicate):
|
|
for _ in range(450):
|
|
try:
|
|
value = predicate()
|
|
if value:
|
|
return value
|
|
except (OSError, ValueError, subprocess.SubprocessError):
|
|
pass
|
|
time.sleep(.1)
|
|
raise AssertionError('reboot fixture timed out')
|
|
|
|
|
|
before = store.read(owner.ROOT / 'ledger.json')
|
|
value = json.loads(before)
|
|
binding = value['active_binding']
|
|
assert value['receipt']['phase'] == 'COMPLETE' and value['receipt']['config_action'] == 'RESTORE'
|
|
assert not (owner.RUN / 'bootstrap.json').exists()
|
|
# /run is genuinely fresh. Restore only the inert requester's disposable IPC
|
|
# directory; no authority, journal or permit is copied into the new boot.
|
|
import pwd
|
|
account = pwd.getpwnam('archipelago')
|
|
control = Path('/run/d09-fixture-client')
|
|
control.mkdir(mode=0o700, exist_ok=True)
|
|
os.chown(control, account.pw_uid, account.pw_gid)
|
|
run('systemctl', 'start', 'archipelago-d09-lifecycle.service')
|
|
wait(lambda: json.loads(run('/usr/libexec/archipelago-d09ctl', 'inspect').stdout)['state'] == 'UNKNOWN')
|
|
assert store.read(owner.ROOT / 'ledger.json') == before
|
|
probe = owner.Owner.__new__(owner.Owner)
|
|
disposition = wait(lambda: probe.drained_disposition(value))
|
|
index = {k: binding[k] for k in ('target_binding_digest', 'composition_digest', 'transaction_id')}
|
|
fields = dict(index, predecessor_epoch_id=value['epoch'], head_sequence=value['sequence'],
|
|
preserved_head_digest=wire.digest(wire.canonical(value['receipt'])),
|
|
preserved_journal_digest=wire.digest(wire.canonical({'receipts': value['journal'], 'intents': value['intents']})),
|
|
preserved_consumed_history_digest=wire.digest(wire.canonical(owner.consumption_history(value))),
|
|
preserved_target_index_digest=wire.digest(wire.canonical(index)), disposition_digest=disposition,
|
|
new_epoch_id=secrets.token_hex(16), new_grant_digest=secrets.token_hex(32),
|
|
execution_grant_digest=secrets.token_hex(32), allowed_action='REACTIVATE_RETAINED')
|
|
directory = PACKAGE / 'external-history'
|
|
history_path = directory / 'pid1-restart-history.json'
|
|
record_path = directory / 'pid1-restart-record.json'
|
|
store.atomic(history_path, wire.canonical(dict(fields, sequence=1)))
|
|
record = dict(fields, schema_version=1, external_history_sequence=1,
|
|
external_history_digest=wire.digest(store.read(history_path)), observed_boot_id=owner.boot_id())
|
|
store.atomic(record_path, wire.canonical(record))
|
|
provision.readmit(record_path, history_path, activation=True)
|
|
def awaiting():
|
|
latest = json.loads(store.read(owner.ROOT / 'ledger.json'))
|
|
return latest if latest['receipt']['phase'] == 'AWAITING_OBSERVATION' else None
|
|
latest = wait(awaiting)
|
|
assert latest['journal'][:len(value['journal'])] == value['journal']
|
|
assert latest['receipt']['observed_invocation_id'] != value['receipt']['observed_invocation_id']
|
|
assert latest['receipt']['namespace_observation_digest'] is None
|
|
for name, destination in owner.DESTINATIONS.items():
|
|
assert owner.object_digest(destination) == binding['objects'][name]['restore']
|
|
before_retry = store.read(owner.ROOT / 'ledger.json')
|
|
provision.readmit(record_path, history_path, activation=True)
|
|
assert store.read(owner.ROOT / 'ledger.json') == before_retry
|
|
print('PASS fresh container PID1/run state: UNKNOWN before external authority; exact retained B activated once; journal preserved; fresh observation still required', flush=True)
|