test(827): observe file-backed D4 generations
This commit is contained in:
@@ -4,11 +4,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import secrets
|
||||
import socket
|
||||
import struct
|
||||
from collections.abc import Callable, Mapping
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -24,13 +26,32 @@ def emit(log: Path, value: dict[str, Any]) -> None:
|
||||
out.write(json.dumps(value, sort_keys=True) + "\n")
|
||||
|
||||
|
||||
def load_generation_functions(
|
||||
path: Path,
|
||||
) -> tuple[Callable[[Mapping[str, str]], int], Callable[[Mapping[str, str]], int]]:
|
||||
spec = importlib.util.spec_from_file_location("d4_lease_generation", path)
|
||||
if spec is None or spec.loader is None:
|
||||
raise ValueError("generation module is unavailable")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
reader = getattr(module, "read_runtime_generation", None)
|
||||
bumper = getattr(module, "bump_runtime_generation", None)
|
||||
if not callable(reader) or not callable(bumper):
|
||||
raise ValueError("generation module has no read/bump functions")
|
||||
return reader, bumper
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--socket", required=True)
|
||||
parser.add_argument("--log", required=True)
|
||||
parser.add_argument("--generation-module", required=True, type=Path)
|
||||
ns = parser.parse_args()
|
||||
socket_path = Path(ns.socket)
|
||||
log_path = Path(ns.log)
|
||||
read_runtime_generation, bump_runtime_generation = load_generation_functions(
|
||||
ns.generation_module
|
||||
)
|
||||
socket_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
os.chmod(socket_path.parent, 0o700)
|
||||
socket_path.unlink(missing_ok=True)
|
||||
@@ -45,6 +66,17 @@ def main() -> None:
|
||||
# The gated launcher registers its own exec-preserved PID here. This is
|
||||
# deliberately volatile fixture state; nothing is written outside root.
|
||||
launcher_sessions: dict[tuple[int, int], str] = {}
|
||||
generation_files: dict[tuple[int, int], Path] = {}
|
||||
|
||||
def generation_environment(identity: tuple[int, int]) -> dict[str, str]:
|
||||
state_path = generation_files.get(identity)
|
||||
if state_path is None or state_path.parent != socket_path.parent:
|
||||
raise ValueError("generation file is outside the fixture root")
|
||||
return {"MOSAIC_LEASE_GENERATION_FILE": str(state_path)}
|
||||
|
||||
def file_generation(identity: tuple[int, int]) -> int:
|
||||
return read_runtime_generation(generation_environment(identity))
|
||||
|
||||
emit(log_path, {"event": "listen", "pid": os.getpid(), "socket": str(socket_path)})
|
||||
|
||||
while True:
|
||||
@@ -64,12 +96,15 @@ def main() -> None:
|
||||
conn.sendall(b'{"ok":false,"code":"INVALID_GENERATION"}\n')
|
||||
continue
|
||||
session_id = launcher_sessions.setdefault(identity, secrets.token_hex(32))
|
||||
generation_file = socket_path.parent / f"generation-{session_id}.state"
|
||||
generation_files[identity] = generation_file
|
||||
record = {
|
||||
"event": "lease_anchor_registered",
|
||||
"peercred": {"pid": pid, "uid": uid, "gid": gid},
|
||||
"starttime_ticks": starttime,
|
||||
"runtime_generation": generation,
|
||||
"session_id_shape": "hex-256",
|
||||
"generation_file": str(generation_file),
|
||||
}
|
||||
emit(log_path, record)
|
||||
reply = {
|
||||
@@ -79,14 +114,31 @@ def main() -> None:
|
||||
}
|
||||
conn.sendall((json.dumps(reply, sort_keys=True) + "\n").encode())
|
||||
continue
|
||||
# The D4 extension requests this at each post-start lifecycle
|
||||
# boundary; the exact WI-3 helper mutates the launcher-created file.
|
||||
if request.get("action") == "bump-generation":
|
||||
generation = bump_runtime_generation(generation_environment(identity))
|
||||
record = {
|
||||
"event": "generation_state_bumped",
|
||||
"peercred": {"pid": pid, "uid": uid, "gid": gid},
|
||||
"starttime_ticks": starttime,
|
||||
"generation": generation,
|
||||
"generation_file": str(generation_files[identity]),
|
||||
"generation_source": "state-file",
|
||||
}
|
||||
emit(log_path, record)
|
||||
conn.sendall((json.dumps(record, sort_keys=True) + "\n").encode())
|
||||
continue
|
||||
if request.get("action") == "promote-probe":
|
||||
generation = generations.get(identity, 0)
|
||||
generation = file_generation(identity)
|
||||
lease_state[identity] = "VERIFIED"
|
||||
record = {
|
||||
"event": "probe_lease_promoted",
|
||||
"peercred": {"pid": pid, "uid": uid, "gid": gid},
|
||||
"starttime_ticks": starttime,
|
||||
"generation": generation,
|
||||
"generation_file": str(generation_files[identity]),
|
||||
"generation_source": "state-file",
|
||||
"new_lease_state": "VERIFIED",
|
||||
}
|
||||
emit(log_path, record)
|
||||
@@ -99,7 +151,7 @@ def main() -> None:
|
||||
if type(generation) is not int or generation < 0:
|
||||
conn.sendall(b'{"ok":false,"code":"INVALID_GENERATION"}\n')
|
||||
continue
|
||||
current_generation = generations.get(identity, 0)
|
||||
current_generation = file_generation(identity)
|
||||
current_lease = lease_state.get(identity, "NONE")
|
||||
if generation < current_generation:
|
||||
code = "STALE_GENERATION"
|
||||
@@ -115,6 +167,8 @@ def main() -> None:
|
||||
"starttime_ticks": starttime,
|
||||
"requested_generation": generation,
|
||||
"current_generation": current_generation,
|
||||
"generation_file": str(generation_files[identity]),
|
||||
"generation_source": "state-file",
|
||||
"lease_state": current_lease,
|
||||
"ok": code == "ALLOW",
|
||||
"code": code,
|
||||
@@ -144,7 +198,10 @@ def main() -> None:
|
||||
|
||||
old_generation = generations.get(identity, 0)
|
||||
old_lease = lease_state.get(identity, "NONE")
|
||||
new_generation = old_generation + 1
|
||||
new_generation = file_generation(identity)
|
||||
if new_generation <= old_generation:
|
||||
conn.sendall(b'{"ok":false,"code":"NON_MONOTONIC_STATE_FILE"}\n')
|
||||
continue
|
||||
generations[identity] = new_generation
|
||||
# Every lifecycle boundary revokes first. A start establishes a new
|
||||
# UNVERIFIED incarnation; it never inherits prior VERIFIED state.
|
||||
@@ -157,6 +214,8 @@ def main() -> None:
|
||||
"reason": request.get("reason"),
|
||||
"old_generation": old_generation,
|
||||
"new_generation": new_generation,
|
||||
"generation_file": str(generation_files[identity]),
|
||||
"generation_source": "state-file",
|
||||
"prior_lease": old_lease,
|
||||
"prior_lease_revoked": True,
|
||||
"new_lease_state": lease_state[identity],
|
||||
|
||||
Reference in New Issue
Block a user