coulomb-social/tests/test_source_date_supervisor.py
tegwick d9da608e65 Add private author-managed attachments with retained revisions
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
2026-10-31 19:38:10 +01:00

555 lines
24 KiB
Python

"""Supervisor lifecycle against a synthetic Kubernetes/backup adapter, no network."""
import copy
import importlib.util
import json
import os
import stat
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location("source_date_supervisor",
ROOT / "scripts/source_date_supervisor.py")
supervisor = importlib.util.module_from_spec(spec)
spec.loader.exec_module(supervisor)
IMAGE = "forgejo.coulomb.social/coulomb/coulomb-social@sha256:" + "a" * 64
BASE = {"tables_sha256": "b" * 64, "files_sha256": "c" * 64,
"tables": 13, "rows": 112, "files": 82}
AFTER = {**BASE, "tables_sha256": "d" * 64, "files_sha256": "e" * 64}
class FakeNode(supervisor.Node):
"""Only control-plane/capture effects are simulated; production paths never open."""
def __init__(self):
self.dep = {"metadata": {"uid": "deployment-id", "resourceVersion": "17"}, "spec": {
"replicas": 1, "strategy": {"type": "Recreate"}, "template": {"spec": {
"containers": [{"name": "app", "image": IMAGE,
"env": [{"name": "CONTENT_ROOT", "value": "/app/var/content"}],
"envFrom": [{"secretRef": {"name": "synthetic-app-env"}}],
"volumeMounts": [{"name": "content",
"mountPath": "/app/var/content"}]}],
"volumes": [{"name": "content", "persistentVolumeClaim": {
"claimName": supervisor.CLAIM}}]}}}}
self.fp, self.helper, self.journal = BASE, None, "absent"
self.calls, self.fail, self.capture_count, self.apply_count = [], None, 0, 0
self.next_uid = 1
self.busy = False
self.sidecar = False
self.extra_writer = False
self.running_image = None
def trip(self, name):
if self.fail == name:
self.fail = None
raise supervisor.Refused("synthetic lost response: " + name)
def doc(self, *args):
kind = args[1]
if kind == "namespace":
return {"metadata": {"uid": "cluster-id"}}
if kind == "node":
return {"metadata": {"uid": "node-id", "name": "synthetic-node"},
"spec": {}, "status": {
"conditions": [{"type": "Ready", "status": "True"}]}}
if kind == "pvc":
return {"metadata": {"uid": "pvc-id"}, "status": {"phase": "Bound"}}
if kind in {"hpa", "applications.argoproj.io"}:
return {"items": []}
if kind == "deployment":
return copy.deepcopy(self.dep)
if kind == "replicaset":
return {"metadata": {"ownerReferences": [{"uid": "deployment-id"}]}}
if kind == "pods":
pods = []
if self.dep["spec"]["replicas"]:
pods.append({"metadata": {"name": "synthetic-app", "ownerReferences": [
{"kind": "ReplicaSet", "name": "synthetic-rs"}]}, "spec": {
"nodeName": "synthetic-node", "containers": [{"image": self.running_image
or self.dep["spec"]["template"]["spec"]["containers"][0]["image"]}],
"volumes": [{"persistentVolumeClaim": {
"claimName": supervisor.CLAIM}}]}, "status": {
"phase": "Running", "conditions": [
{"type": "Ready", "status": "True"}],
"containerStatuses": [{"imageID": self.dep["spec"]["template"][
"spec"]["containers"][0]["image"]}]}})
if self.helper:
pods.append(copy.deepcopy(self.helper))
if self.extra_writer:
pods.append({"metadata": {"name": "unreviewed-writer"}, "spec": {"volumes": [
{"persistentVolumeClaim": {"claimName": supervisor.CLAIM}}]},
"status": {"phase": "Running"}})
return {"items": pods}
raise AssertionError(args)
def command(self, args, payload=None, **kwargs):
self.calls.append(args[:4])
if args[:2] == ["helm", "history"]:
return supervisor.canonical([{"revision": 18, "status": "deployed"}])
if args[:3] == ["helm", "get", "manifest"]:
return b"synthetic helm manifest"
if args[:2] == ["kubectl", "exec"]:
message = json.loads(payload)
mode = message["mode"]
if mode == "prepare":
return supervisor.canonical({"fingerprint": self.fp,
"storage_constraint_verified": True,
"expected_after": AFTER})
if mode == "probe":
return supervisor.canonical({"fingerprint": self.fp})
if mode == "inspect":
status = "operation_busy" if self.busy else self.journal
return supervisor.canonical({"status": status,
"fingerprint": self.fp})
if mode == "reconcile":
assert self.journal == "pending"
self.journal, self.fp = "rolled_back", BASE
return supervisor.canonical({"status": self.journal, "fingerprint": self.fp})
assert mode == "apply"
self.apply_count += 1
if self.fail == "apply_pending":
self.journal = "pending"
self.trip("apply_pending")
self.journal, self.fp = "completed", AFTER
self.trip("apply_reply")
return supervisor.canonical({"status": "completed", "whole_store_postflight": True,
"fingerprint": self.fp})
if args[:2] == ["kubectl", "scale"]:
current = next(a for a in args if a.startswith("--current-replicas=")).split("=")[1]
assert int(current) == self.dep["spec"]["replicas"]
wanted = next(a for a in args if a.startswith("--replicas=")).split("=")[1]
self.dep["spec"]["replicas"] = int(wanted)
return b""
if args[:2] == ["kubectl", "create"]:
pod = json.loads(payload)
if self.sidecar:
pod["spec"]["containers"].append({"name": "injected"})
if "--dry-run=server" not in args:
assert self.dep["spec"]["replicas"] == 0 and not self.helper
pod["metadata"]["uid"] = "helper-" + str(self.next_uid)
self.next_uid += 1
pod["status"] = {"phase": "Running"}
self.helper = copy.deepcopy(pod)
self.trip("create_reply")
return supervisor.canonical(pod)
if args[:3] == ["kubectl", "delete", "--raw"]:
assert json.loads(payload)["preconditions"]["uid"] == self.helper["metadata"]["uid"]
self.helper = None
self.trip("delete_reply")
return b""
if args[:3] == ["kubectl", "rollout", "status"]:
self.trip("resume_reply")
return b""
assert args[:2] == ["kubectl", "wait"], args
return b""
def verify_backup_installation(self, target):
self.trip("helper_hash")
def verify_local_node(self, node):
pass # synthetic cluster/root adapter, never a native machine-id claim
def installation_pins(self):
return {"installed_helpers": {name: "f" * 64 for name in supervisor.HELPERS},
"public_recipient": "age1synthetic"}
def capture(self, target):
self.capture_count += 1
self.trip("capture")
return {"status": "synthetic-capture"}
def lock_backups(self, stack):
self.trip("backup_lock")
def helper_directory(self, path):
path.mkdir(mode=0o700, exist_ok=True)
def read_state(self, path):
return json.loads(path.read_bytes())
@pytest.fixture
def prepared(tmp_path):
node = FakeNode()
target = {"cluster_uid": "cluster-id", "deployment_uid": "deployment-id", "node_uid": "node-id",
"node_name": "synthetic-node", "pvc_uid": "pvc-id", "image": IMAGE,
"deployment_spec_sha256": supervisor.sha(supervisor.canonical(node.dep["spec"])),
"helm_revision": 18,
"helm_manifest_sha256": supervisor.sha(b"synthetic helm manifest"),
"public_recipient": "age1synthetic", "installed_helpers": {
name: "f" * 64 for name in supervisor.HELPERS}}
capsule = tmp_path / "capsule"
capsule.mkdir(mode=0o700)
for name in supervisor.MEMBERS:
(capsule / name).write_text("synthetic source")
(capsule / "plan.json").write_text(json.dumps({
"schema": "coulomb.source-date-backfill-plan.v1", "blockers": [],
"prepared_change_set_valid": True}))
manifest = {"schema": "coulomb.supervised-date-capsule.v1", "baseline": BASE, "target": target,
"attempt_id": "1" * 32, "requires_founder_attended_execution": True,
"files": {name: supervisor.sha((capsule / name).read_bytes())
for name in supervisor.MEMBERS}}
(capsule / "manifest.json").write_bytes(supervisor.canonical(manifest))
digest = "sha256:" + supervisor.sha(supervisor.canonical(manifest))
frozen = supervisor.Capsule(capsule, digest)
root = tmp_path / "state"
return supervisor.Supervisor(frozen, node, state_root=root), node, capsule, digest
def test_success_captures_fences_applies_checks_and_resumes_once(prepared):
operation, node, _, _ = prepared
assert not operation.state_root.exists()
operation.preflight()
assert not operation.state_root.exists() and node.capture_count == 0
result = operation.execute()
assert result["phase"] == "completed" and result["correction_status"] == "completed"
assert node.capture_count == node.apply_count == 1
assert node.dep["spec"]["replicas"] == 1 and not node.helper
assert result["helper_evidence_root_custody_verified"]
with pytest.raises(supervisor.Refused, match="prior_attempt"):
operation.execute()
assert node.apply_count == 1
@pytest.mark.parametrize("failure", ["apply_reply", "apply_pending",
"create_reply", "delete_reply"])
def test_uncertainty_preserves_fence_and_recovers_without_new_capture_or_apply(prepared, failure):
operation, node, _, _ = prepared
node.fail = failure
with pytest.raises(supervisor.Refused):
operation.execute()
assert node.dep["spec"]["replicas"] == 0
applied = node.apply_count
result = operation.recover()
assert result["phase"] == "completed" and node.dep["spec"]["replicas"] == 1
assert node.capture_count == 1 and node.apply_count == applied
assert not node.helper
if failure == "apply_pending":
assert result["correction_status"] == "rolled_back"
def test_live_original_operation_blocks_recovery_and_never_restarts(prepared):
operation, node, _, _ = prepared
node.fail = "apply_pending"
with pytest.raises(supervisor.Refused):
operation.execute()
node.busy = True
with pytest.raises(supervisor.Refused, match="still_running"):
operation.recover()
assert node.capture_count == node.apply_count == 1 and node.dep["spec"]["replicas"] == 0
def test_lost_resume_reply_preserves_new_productive_writes(prepared):
operation, node, _, _ = prepared
node.fail = "resume_reply"
with pytest.raises(supervisor.Refused):
operation.execute()
assert node.dep["spec"]["replicas"] == 1
node.fp = {**AFTER, "rows": 113} # legitimate write after the application resumed
result = operation.recover()
assert result["post_resume_writes_preserved"] and node.fp["rows"] == 113
assert node.capture_count == node.apply_count == 1
@pytest.mark.parametrize("problem", ["pod_injection", "foreign_writer",
"spec_drift", "helper_hash", "pod_image"])
def test_preflight_refusals_never_capture_or_scale(prepared, problem):
operation, node, _, _ = prepared
if problem == "pod_injection":
node.sidecar = True
elif problem == "foreign_writer":
node.extra_writer = True
elif problem == "spec_drift":
node.dep["spec"]["template"]["spec"]["containers"][0]["image"] += "0"
elif problem == "pod_image":
node.running_image = IMAGE + "0"
else:
node.fail = "helper_hash"
with pytest.raises(supervisor.Refused):
operation.execute()
assert node.capture_count == node.apply_count == 0 and node.dep["spec"]["replicas"] == 1
def test_changed_capsule_refuses_before_any_backend_access(prepared):
_, _, path, digest = prepared
(path / "wire.py").write_text("changed")
with pytest.raises(supervisor.Refused, match="member_changed"):
supervisor.Capsule(path, digest)
def test_unrelated_data_drift_after_uncertain_result_never_resumes(prepared):
operation, node, _, _ = prepared
node.fail = "apply_reply"
with pytest.raises(supervisor.Refused):
operation.execute()
node.fp = {**AFTER, "rows": 999}
with pytest.raises(supervisor.Refused, match="dataset_requires_owner_review"):
operation.recover()
assert node.dep["spec"]["replicas"] == 0 and node.helper
assert node.capture_count == node.apply_count == 1
def test_replaced_helper_is_never_deleted_or_used_for_recovery(prepared):
operation, node, _, _ = prepared
node.fail = "apply_reply"
with pytest.raises(supervisor.Refused):
operation.execute()
node.helper["metadata"]["uid"] = "foreign-helper"
with pytest.raises(supervisor.Refused, match="helper_replaced"):
operation.recover()
assert node.dep["spec"]["replicas"] == 0
assert node.helper["metadata"]["uid"] == "foreign-helper"
def test_terminal_original_helper_is_uid_deleted_and_recreated_only_for_reconciliation(prepared):
operation, node, _, _ = prepared
node.fail = "apply_reply"
with pytest.raises(supervisor.Refused):
operation.execute()
node.helper["status"]["phase"] = "Succeeded"
result = operation.recover()
assert result["correction_status"] == "completed" and not node.helper
assert node.next_uid == 3 and node.capture_count == node.apply_count == 1
def test_capsule_preparation_is_private_fresh_and_has_a_new_attempt_id(prepared, tmp_path):
import runpy
import sys
operation, _, source, _ = prepared
sys.path.insert(0, str(ROOT / "scripts"))
try:
prepare = runpy.run_path(str(ROOT / "scripts/prepare-source-date-capsule.py"))["prepare"]
finally:
sys.path.pop(0)
baseline, target = tmp_path / "baseline.json", tmp_path / "target.json"
baseline.write_bytes(supervisor.canonical(BASE))
target.write_bytes(supervisor.canonical(operation.target))
proofs = []
for name in ("first", "second"):
output = tmp_path / name
proof = prepare(source / "plan.json", baseline, target, output)
capsule = supervisor.Capsule(output, proof["proposal_digest"])
assert capsule.manifest["requires_founder_attended_execution"]
assert output.stat().st_mode & 0o777 == 0o700
assert all(p.stat().st_mode & 0o777 == 0o600 for p in output.iterdir())
proofs.append(proof["proposal_digest"])
with pytest.raises(ValueError, match="Fresh private"):
prepare(source / "plan.json", baseline, target, output)
assert proofs[0] != proofs[1]
@pytest.fixture
def native_observer_tmp():
# The native observer deliberately requires /tmp, regardless of TMPDIR.
import tempfile
with tempfile.TemporaryDirectory(prefix="csoc-observer-test-", dir="/tmp") as directory:
yield Path(directory)
@pytest.mark.parametrize("changed_image", [False, True])
def test_observer_prepares_without_capturing_or_refuses_missing_release(
prepared, native_observer_tmp, monkeypatch, changed_image):
import runpy
import sys
operation, node, source, _ = prepared
sys.path.insert(0, str(ROOT / "scripts"))
try:
observer = runpy.run_path(str(ROOT / "scripts/observe-source-date-target.py"))["observe"]
finally:
sys.path.pop(0)
monkeypatch.setattr(supervisor.os, "geteuid", lambda: 0) # simulated root context only
output = native_observer_tmp / "observed-capsule"
if changed_image:
node.dep["spec"]["template"]["spec"]["containers"][0]["image"] += "0"
with pytest.raises(RuntimeError, match="prerequisite_missing"):
observer(source / "plan.json", output, image=IMAGE, cluster_uid="cluster-id",
deployment_uid="deployment-id", pvc_uid="pvc-id", node=node)
assert not output.exists()
else:
proof = observer(source / "plan.json", output, image=IMAGE, cluster_uid="cluster-id",
deployment_uid="deployment-id", pvc_uid="pvc-id", node=node)
capsule = supervisor.Capsule(output, proof["proposal_digest"])
assert capsule.baseline == BASE and capsule.target == operation.target
assert proof["baseline_rechecked"] and not proof["live_changes"]
assert node.capture_count == node.apply_count == 0 and node.dep["spec"]["replicas"] == 1
def test_closeout_failure_recovers_only_custody_and_preserves_new_writes(prepared, monkeypatch):
operation, node, _, _ = prepared
seal = node.seal_helper_evidence
def lost_closeout(path, expected):
assert node.dep['spec']['replicas'] == 1 and node.helper is None
seal(path, expected)
raise supervisor.Refused('synthetic lost custody response')
monkeypatch.setattr(node, 'seal_helper_evidence', lost_closeout)
with pytest.raises(supervisor.Refused, match='lost custody'):
operation.execute()
assert operation.state['failed_phase'] == 'custody_closeout_started'
node.fp = {**AFTER, 'rows': 115}
before = list(node.calls)
monkeypatch.setattr(node, 'seal_helper_evidence', seal)
result = operation.recover()
assert result['phase'] == 'completed' and result['helper_evidence_root_custody_verified']
assert node.fp['rows'] == 115 and node.capture_count == node.apply_count == 1
assert all(call[:2] not in (['kubectl', 'create'], ['kubectl', 'scale'],
['kubectl', 'exec']) for call in node.calls[len(before):])
with pytest.raises(supervisor.Refused, match='completed_attempt'):
operation.recover()
def test_uncertain_helper_evidence_is_not_sealed_before_resume(prepared, monkeypatch):
from unittest.mock import Mock
operation, node, _, _ = prepared
seal = Mock()
monkeypatch.setattr(node, 'seal_helper_evidence', seal)
node.fail = 'apply_pending'
with pytest.raises(supervisor.Refused):
operation.execute()
seal.assert_not_called()
assert node.helper and node.dep['spec']['replicas'] == 0
def evidence_directory(tmp_path):
private = tmp_path / 'private'
private.mkdir(mode=0o700)
for name, data in {'execute.lock': b'', 'journal.json': b'synthetic private journal',
'result.json': b'synthetic result'}.items():
file = private / name
file.write_bytes(data)
file.chmod(0o600)
return private
def test_evidence_sealing_preserves_bytes_paths_and_modes(tmp_path):
private = evidence_directory(tmp_path)
node = supervisor.Node()
before = node.helper_evidence(private)
node.seal_helper_evidence(private, before)
assert node.helper_evidence(private) == before
for path in [private, *private.iterdir()]:
info = path.stat()
assert (info.st_uid, info.st_gid) == (os.geteuid(), os.getegid())
assert stat.S_IMODE(info.st_mode) == (0o700 if path.is_dir() else 0o600)
@pytest.mark.parametrize('problem', ['changed_bytes', 'extra', 'symlink', 'hardlink',
'broad', 'busy', 'fifo'])
def test_evidence_closeout_refuses_drift_before_any_chown(tmp_path, monkeypatch, problem):
from unittest.mock import Mock
private = evidence_directory(tmp_path)
node = supervisor.Node()
before = node.helper_evidence(private)
item = private / 'result.json'
lock = None
if problem == 'changed_bytes':
item.write_bytes(b'changed')
elif problem == 'extra':
(private / 'unexpected.json').write_bytes(b'unexpected')
elif problem in {'symlink', 'hardlink', 'fifo'}:
item.unlink()
if problem == 'symlink':
item.symlink_to(private / 'journal.json')
elif problem == 'hardlink':
os.link(private / 'journal.json', item)
else:
os.mkfifo(item, 0o600)
elif problem == 'broad':
item.chmod(0o644)
else:
lock = (private / 'execute.lock').open()
supervisor.fcntl.flock(lock, supervisor.fcntl.LOCK_EX | supervisor.fcntl.LOCK_NB)
chown = Mock()
monkeypatch.setattr(supervisor.os, 'fchown', chown)
try:
with pytest.raises((supervisor.Refused, OSError)):
node.seal_helper_evidence(private, before)
chown.assert_not_called()
finally:
if lock:
lock.close()
@pytest.mark.parametrize('overlap', ['helper', 'ancestor', 'child'])
def test_closeout_refuses_existing_helper_or_host_mount(prepared, monkeypatch, overlap):
from unittest.mock import Mock
operation, node, _, _ = prepared
operation.execute()
original = node.doc
evidence_path = operation.directory / 'private'
pod = {'metadata': {'name': operation.helper if overlap == 'helper' else 'foreign'},
'spec': {'volumes': []}}
if overlap != 'helper':
path = evidence_path.parent if overlap == 'ancestor' else evidence_path / 'journal.json'
pod['spec']['volumes'] = [{'hostPath': {'path': str(path)}}]
def documents(*args):
result = original(*args)
if args[:2] == ('get', 'pods'):
result['items'].append(pod)
return result
monkeypatch.setattr(node, 'doc', documents)
seal = Mock()
monkeypatch.setattr(node, 'seal_helper_evidence', seal)
with pytest.raises(supervisor.Refused, match='still_'):
operation.complete()
seal.assert_not_called()
def test_partial_helper_ownership_transfer_resumes_from_fingerprints(tmp_path, monkeypatch):
from types import SimpleNamespace
private = evidence_directory(tmp_path)
node = supervisor.Node()
real_stat = os.fstat
owners = {p.stat().st_ino: (10001, 10001) for p in [private, *private.iterdir()]}
changed = []
def observed(fd):
info = real_stat(fd)
values = {name: getattr(info, name) for name in dir(info) if name.startswith('st_')}
values['st_uid'], values['st_gid'] = owners.get(info.st_ino, (info.st_uid, info.st_gid))
return SimpleNamespace(**values)
def transfer(fd, uid, gid):
inode = real_stat(fd).st_ino
owners[inode] = (uid, gid)
changed.append(inode)
if len(changed) == 1:
raise OSError('synthetic interruption after first ownership change')
monkeypatch.setattr(supervisor.os, 'fstat', observed)
monkeypatch.setattr(supervisor.os, 'fchown', transfer)
expected = node.helper_evidence(private)
with pytest.raises(OSError, match='interruption'):
node.seal_helper_evidence(private, expected)
assert len(set(owners.values())) == 2
node.seal_helper_evidence(private, expected)
assert set(owners.values()) == {(os.geteuid(), os.getegid())}
assert node.helper_evidence(private) == expected
def test_second_lost_reply_during_recovery_uses_latest_phase(prepared):
operation, node, _, _ = prepared
node.fail = 'apply_reply'
with pytest.raises(supervisor.Refused):
operation.execute()
node.fail = 'resume_reply'
with pytest.raises(supervisor.Refused):
operation.recover()
assert node.dep['spec']['replicas'] == 1
assert operation.state['phase'] == 'resume_started'
node.fp = {**AFTER, 'rows': 116}
result = operation.recover()
assert result['phase'] == 'completed' and result['post_resume_writes_preserved']
assert node.fp['rows'] == 116 and node.capture_count == node.apply_count == 1