Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 2m27s
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
285 lines
11 KiB
Python
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
|