coulomb-social/tests/test_backup_install_runtime.py
tegwick 7077ca9db0
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 2m27s
Prepare supervised inactive backup installation and record custody blockers
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 12:16:03 +01:00

285 lines
11 KiB
Python

"""Attended controller transactions against disposable files and fake host boundaries."""
import base64
import fcntl
import importlib.util
import json
import os
from contextlib import contextmanager
from pathlib import Path
from types import SimpleNamespace
import pytest
from tests import test_backup_install as fixtures
bundle = fixtures.bundle
install = fixtures.install
prepared = fixtures.prepared
ROOT = Path(__file__).parents[1]
spec = importlib.util.spec_from_file_location(
"backup_install_runtime", ROOT / "scripts/backup_install_runtime.py"
)
runtime = importlib.util.module_from_spec(spec)
spec.loader.exec_module(runtime)
@pytest.fixture
def controller(prepared, monkeypatch):
root, manifest, contents, before, _ = prepared
archive, manifest_hash = bundle()
source = (ROOT / "scripts/backup_install.py").read_bytes()
route = b"# synthetic verifier\n"
plan = dict(
attempt_id="20261031bacc0001",
cluster_uid=runtime.CLUSTER_UID,
archive_sha256=runtime.sha(archive),
manifest_sha256=manifest_hash,
capacity_profile=runtime.PROFILE,
expected_before=before,
installer_sha256=runtime.sha(source),
route_verifier_sha256=runtime.sha(route),
route_execution_digest="sha256:" + "a" * 64,
route_packet={"execution_digest": "sha256:" + "a" * 64},
)
packet = dict(
plan=plan,
proposal_digest="sha256:" + runtime.sha(runtime.canonical(plan)),
installer=base64.b64encode(source).decode(),
archive=base64.b64encode(archive).decode(),
route_verifier=base64.b64encode(route).decode(),
)
monkeypatch.setattr(runtime, "ROOT", root)
monkeypatch.setattr(runtime, "ATTEMPTS", root / "var/lib/install-attempts")
# Controller owner guard is synthetic; filesystem library still sees real ownership.
fake_os = SimpleNamespace(**vars(os))
fake_os.geteuid = lambda: 0
monkeypatch.setattr(runtime, "os", fake_os)
calls = []
@contextmanager
def locked():
calls.append("lock")
try:
yield
finally:
calls.append("unlock")
monkeypatch.setattr(runtime, "locks", locked)
monkeypatch.setattr(runtime, "cluster_identity", lambda: calls.append("cluster"))
monkeypatch.setattr(runtime, "inactive_units", lambda: calls.append("inactive") or {})
monkeypatch.setattr(runtime, "capacity", lambda _: {"fixture": True})
monkeypatch.setattr(runtime, "runtime_dependencies", lambda: {"fixture": True})
monkeypatch.setattr(
runtime, "route_verified", lambda *_: calls.append("route") or {"helm_revision": 6}
)
def command(args, **kwargs):
assert args == ["systemctl", "daemon-reload"]
calls.append("reload")
return b""
monkeypatch.setattr(runtime, "command", command)
return packet, root, calls
def test_preflight_holds_both_locks_without_writing(controller):
packet, root, calls = controller
result = runtime.run(packet, "preflight")
assert result["status"] == "preflight_verified"
assert install.observe(root) == packet["plan"]["expected_before"]
assert not runtime.ATTEMPTS.exists()
assert calls[0] == "lock" and calls[-1] == "unlock"
assert "reload" not in calls
def test_install_and_explicit_rollback_use_durable_receipts(controller):
packet, root, calls = controller
result = runtime.run(packet, "install")
assert result["status"] == "sources_installed_units_inactive_verified"
assert result["verified_targets"] == 19
assert result["timers_activated"] is False and result["capture_executed"] is False
directory = runtime.ATTEMPTS / packet["plan"]["attempt_id"]
assert json.loads((directory / "receipt.json").read_text()) == result
assert (directory / "files/receipt.json").is_file()
assert calls.count("reload") == 1
with pytest.raises(runtime.Refused):
runtime.run(packet, "install")
restored = runtime.run(packet, "rollback")
assert restored["status"] == "prior_files_restored_units_inactive_verified"
now = install.observe(root)
for target, before in packet["plan"]["expected_before"].items():
assert now[target]["exists"] == before["exists"]
if before["exists"]:
assert all(now[target][key] == before[key] for key in ("sha256", "mode", "uid", "gid"))
assert calls.count("reload") == 2
@pytest.mark.parametrize("boundary", ["cluster", "route", "capacity", "units", "sdk"])
def test_missing_prerequisite_refuses_before_attempt_and_replacement(
controller, monkeypatch, boundary
):
packet, root, _ = controller
names = dict(
cluster="cluster_identity",
route="route_verified",
capacity="capacity",
units="inactive_units",
sdk="runtime_dependencies",
)
def refuse(*_):
raise runtime.Refused("synthetic_refusal")
monkeypatch.setattr(runtime, names[boundary], refuse)
with pytest.raises(runtime.Refused):
runtime.run(packet, "install")
assert not runtime.ATTEMPTS.exists()
assert install.observe(root) == packet["plan"]["expected_before"]
def test_changed_bytes_or_digest_refuse_before_locks(controller):
packet, _, calls = controller
packet["installer"] = base64.b64encode(b"raise RuntimeError('must not execute')").decode()
with pytest.raises(runtime.Refused, match="source_digest_differs"):
runtime.run(packet, "install")
assert calls == []
def test_failed_reload_keeps_preimages_and_requires_explicit_rollback(controller, monkeypatch):
packet, _, _ = controller
def fail(*_, **__):
raise RuntimeError("synthetic provider text")
monkeypatch.setattr(runtime, "command", fail)
with pytest.raises(RuntimeError):
runtime.run(packet, "install")
directory = runtime.ATTEMPTS / packet["plan"]["attempt_id"]
receipt = json.loads((directory / "receipt.json").read_text())
assert receipt["status"] == "installation_requires_inspection"
assert "provider text" not in (directory / "receipt.json").read_text()
files = json.loads((directory / "files/receipt.json").read_text())
assert files["status"] == "installation_requires_review"
assert files["changes"]
def test_postflight_route_failure_is_not_installation_success(controller, monkeypatch):
packet, _, _ = controller
count = 0
def route(*_):
nonlocal count
count += 1
if count == 2:
raise runtime.Refused("route_changed")
return {}
monkeypatch.setattr(runtime, "route_verified", route)
with pytest.raises(runtime.Refused):
runtime.run(packet, "install")
directory = runtime.ATTEMPTS / packet["plan"]["attempt_id"]
assert (
json.loads((directory / "receipt.json").read_text())["status"]
== "installation_requires_inspection"
)
assert (
json.loads((directory / "files/receipt.json").read_text())["status"]
== "sources_installed_units_inactive_verified"
)
def test_unit_guard_rejects_enabled_failed_or_foreign_units(monkeypatch):
def check(**changes):
row = dict(
LoadState="loaded", ActiveState="inactive", SubState="dead", UnitFileState="disabled"
)
row.update(changes)
def command(args, **_):
if "list-jobs" in args:
return b""
if "list-units" in args:
return b"[]"
fragment = args[2] if "@" not in args[2] else "coulomb-social-backup-alert@.service"
values = dict(row, FragmentPath="/etc/systemd/system/" + fragment)
if changes.get("FragmentPath"):
values["FragmentPath"] = changes["FragmentPath"]
return "\n".join(k + "=" + v for k, v in values.items()).encode()
monkeypatch.setattr(runtime, "command", command)
return runtime.inactive_units()
assert len(check()) == 5
for change in [
dict(ActiveState="active"),
dict(UnitFileState="enabled"),
dict(LoadState="error"),
dict(FragmentPath="/tmp/foreign.service"),
]:
with pytest.raises(runtime.Refused):
check(**change)
def test_launcher_refuses_nonterminal_without_ssh(monkeypatch):
spec = importlib.util.spec_from_file_location(
"install_backup_launcher", ROOT / "scripts/install-backup-sources.py"
)
launcher = importlib.util.module_from_spec(spec)
spec.loader.exec_module(launcher)
monkeypatch.setattr(launcher, "prepare", lambda: ({"proposal_digest": "fixture"}, ""))
monkeypatch.setattr(launcher.sys, "stdin", SimpleNamespace(isatty=lambda: False))
monkeypatch.setattr(
launcher.subprocess, "run", lambda *_args, **_kwargs: pytest.fail("SSH forbidden")
)
with pytest.raises(SystemExit, match="Founder terminal"):
launcher.main(["--execute", "--expect-digest", "fixture"])
def test_real_locks_are_exclusive_preserved_and_released_on_partial_acquire(tmp_path, monkeypatch):
state = tmp_path / "state"
state.mkdir(mode=0o700)
paths = [state / name for name in ("backup.lock", "snapshot.lock")]
for path in paths:
path.write_bytes(b"retained lock bytes")
path.chmod(0o600)
before = [install.fingerprint(p) for p in paths]
monkeypatch.setattr(runtime, "STATE", state)
with runtime.locks():
for path in paths:
with path.open("rb") as stream:
with pytest.raises(BlockingIOError):
fcntl.flock(stream, fcntl.LOCK_EX | fcntl.LOCK_NB)
with paths[1].open("rb") as busy:
fcntl.flock(busy, fcntl.LOCK_EX | fcntl.LOCK_NB)
with pytest.raises(BlockingIOError), runtime.locks():
pytest.fail("second lock must refuse")
with paths[0].open("rb") as first:
fcntl.flock(first, fcntl.LOCK_EX | fcntl.LOCK_NB)
assert [install.fingerprint(p) for p in paths] == before
def test_lock_symlink_refuses_without_replacement(tmp_path, monkeypatch):
state = tmp_path / "state"
state.mkdir(mode=0o700)
target = state / "unrelated"
target.write_bytes(b"unchanged")
(state / "backup.lock").symlink_to(target)
monkeypatch.setattr(runtime, "STATE", state)
with pytest.raises(OSError), runtime.locks():
pytest.fail("symlink lock must refuse")
assert (state / "backup.lock").is_symlink() and target.read_bytes() == b"unchanged"
def test_queued_start_refuses_even_before_inactive_unit_checks(monkeypatch):
calls = []
def command(args, **_):
calls.append(args)
return b"123 coulomb-social-paired-backup.service start waiting\n"
monkeypatch.setattr(runtime, "command", command)
with pytest.raises(runtime.Refused, match="queued_coulomb_unit_job"):
runtime.inactive_units()
assert len(calls) == 1