Files
hermes-agent/evals/update_unit_client_budget.py
Teknium 50cd1190ef test: pass snapshot listings to the catch-up restart budget probe
Main now snapshots systemd unit listings before stopping old processes
and passes them into _restart_systemd_gateway_units_best_effort; the
catch-up budget test and eval call the new two-argument shape while
still asserting the unit's stop+start budget reaches the client timeout.
2026-09-07 08:20:09 -07:00

66 lines
3.1 KiB
Python

"""Real disposable user-systemd transaction, never a production gateway."""
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
import time
repo, tag, output = sys.argv[1:4]
catchup = sys.argv[4:] == ["--catchup"]
if not tag.isalnum():
raise SystemExit("tag must be alphanumeric")
home = Path(tempfile.mkdtemp(prefix=f"updater-{tag}-"))
allowed = {key: os.environ[key] for key in ("PATH", "XDG_RUNTIME_DIR", "DBUS_SESSION_BUS_ADDRESS") if key in os.environ}
os.environ.clear()
os.environ.update(allowed, HOME=str(home), HERMES_HOME=str(home / "hermes"))
sys.path.insert(0, repo)
from hermes_cli import update_cmd_fleet as fleet
_systemctl_reset_and_restart = fleet._systemctl_reset_and_restart
actual_systemctl = fleet._systemctl
def scoped_systemctl(argv, *, timeout):
if "list-units" in argv:
if "--user" not in argv:
return subprocess.CompletedProcess(argv, 0, "", "")
argv = [arg for arg in argv if arg not in ("hermes-gateway*", "hermes-serve*")] + [unit + ".service"]
return actual_systemctl(argv, timeout=timeout)
if catchup:
# Bound discovery to our transient unit; never enumerate user services.
fleet._systemctl = scoped_systemctl
unit = f"hermes-serve-audit-089c35aa-{tag}"
cmd = ["systemctl", "--user"]
def run(args):
return subprocess.run(args, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=65)
def pid():
return run(cmd + ["show", unit, "--property=MainPID", "--value"]).stdout.strip()
record: dict[str, object] = {"repo": repo, "tag": tag, "unit": unit, "home": str(home), "tier": "native disposable systemd service"}
try:
start = run(["systemd-run", "--user", "--unit", unit, f"--property=ExecStop=/bin/sleep {31 if catchup else 16}", "--property=TimeoutStopSec=45", "--property=TimeoutStartSec=30", "/bin/sleep", "infinity"])
assert start.returncode == 0, start.stderr
old = pid()
began = time.monotonic()
try:
if catchup:
failed = []
fleet._restart_systemd_gateway_units_best_effort(failed, list(fleet._systemd_gateway_unit_listings()))
record.update(failed_units=failed)
else:
result = _systemctl_reset_and_restart(cmd, unit)
record.update(returncode=result.returncode, stderr=result.stderr)
except subprocess.TimeoutExpired as exc:
record.update(timeout=exc.timeout)
record["elapsed"] = time.monotonic() - began
deadline = time.monotonic() + 20
while (pid() in (old, "0", "")) and time.monotonic() < deadline:
time.sleep(.1)
record.update(old_pid=old, new_pid=pid(), active=run(cmd + ["is-active", unit]).stdout.strip())
missing = _systemctl_reset_and_restart(cmd, unit + "-missing")
record["missing_unit_error_preserved"] = missing.returncode != 0 and bool(missing.stderr)
finally:
record["cleanup_stop_rc"] = run(cmd + ["stop", unit]).returncode
run(cmd + ["reset-failed", unit])
record["inactive_after_cleanup"] = run(cmd + ["is-active", unit]).returncode != 0
Path(output).write_text(json.dumps(record, indent=2), encoding="utf-8")
print(json.dumps(record, indent=2))