mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
test: reap session-leaked Hermes daemons on macOS too
The session-end reaper (78eff4628a, #106742) found leftover processes by
reading /proc/<pid>/environ, so on macOS it scanned nothing. There, e2e
tests that make a client start its own gateway (e.g. the C7 two-tenant
Desktop backend) leave a `gateway run --idle-exit` daemon running as a
launchd job, outside the pytest process tree. The test homes configure
an API server, so the gateway's idle exit keeps reporting
"messaging ['api_server']" and the daemon never exits: ~320 MB per run.
One dev Mac had 22 of them, 11-26 h old.
Read each process's exec-time environment through psutil on every POSIX
host instead, and run the reaper's own test on macOS. That test times
out on base on macOS and passes here; a real C7 run leaves no gateway
behind afterwards.
This commit is contained in:
1 parent
8e7cf55eb6
commit
d0001c5ba2
2 files changed
+14
-13
No files matched your search
+12
-11
@@ -1106,30 +1106,31 @@ def _remove_relocated_basetemp(config) -> None:
|
||||
def _processes_with_hermes_home_under(roots) -> list[int]:
|
||||
"""PIDs (never this process) whose environment names a ``HERMES_HOME`` inside one of *roots*.
|
||||
|
||||
Linux only (``/proc/<pid>/environ``; unreadable entries are another user's and skipped).
|
||||
Elsewhere the scan is empty: a test-spawned daemon there is the per-test guard's problem.
|
||||
POSIX (psutil's exec-time environment; another user's processes are unreadable and skipped).
|
||||
Windows is out of scope: no leak observed there, and SIGTERM there is TerminateProcess.
|
||||
"""
|
||||
if not sys.platform.startswith("linux") or not os.path.isdir("/proc"):
|
||||
if os.name != "posix":
|
||||
return []
|
||||
import psutil
|
||||
|
||||
resolved = [Path(r).resolve() for r in roots if r]
|
||||
me = os.getpid()
|
||||
found = []
|
||||
for entry in os.scandir("/proc"):
|
||||
if not entry.name.isdigit() or int(entry.name) == os.getpid():
|
||||
for proc in psutil.process_iter():
|
||||
if proc.pid == me:
|
||||
continue
|
||||
try:
|
||||
with open(f"/proc/{entry.name}/environ", "rb") as handle:
|
||||
environ = handle.read().split(b"\0")
|
||||
except OSError:
|
||||
home = proc.environ().get("HERMES_HOME")
|
||||
except (psutil.Error, OSError):
|
||||
continue
|
||||
home = next((item[12:] for item in environ if item.startswith(b"HERMES_HOME=")), None)
|
||||
if not home:
|
||||
continue
|
||||
try:
|
||||
path = Path(os.fsdecode(home)).resolve()
|
||||
path = Path(home).resolve()
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
if any(path == root or path.is_relative_to(root) for root in resolved):
|
||||
found.append(int(entry.name))
|
||||
found.append(proc.pid)
|
||||
return found
|
||||
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ def _detached(env_home, marker):
|
||||
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
|
||||
@pytest.mark.platforms("linux")
|
||||
@pytest.mark.platforms("linux", "macos")
|
||||
@pytest.mark.live_system_guard_bypass # signals only the two children this test spawned
|
||||
def test_session_teardown_terminates_daemons_under_the_session_basetemp_only(tmp_path):
|
||||
basetemp, elsewhere = tmp_path / "basetemp", tmp_path / "elsewhere"
|
||||
@@ -32,7 +32,7 @@ def test_session_teardown_terminates_daemons_under_the_session_basetemp_only(tmp
|
||||
inside = _detached(basetemp / "t0" / "hermes", "inside")
|
||||
outside = _detached(elsewhere, "outside")
|
||||
try:
|
||||
# Both children have exec'd with their HERMES_HOME (what the reaper reads from /proc).
|
||||
# Both children have exec'd with their HERMES_HOME (what the reaper reads via psutil environ()).
|
||||
wait_until(lambda: {inside.pid, outside.pid} <= set(suite_conftest._processes_with_hermes_home_under(
|
||||
[basetemp, elsewhere])), timeout=10, what='both detached children started')
|
||||
config = SimpleNamespace(_tmp_path_factory=SimpleNamespace(_basetemp=basetemp))
|
||||
|
||||
Reference in new issue
Block a user