feat(egress): add 'hermes egress health' subcommand - #71135
Conversation
Adds a lightweight health check for the iron-proxy daemon. Usage: hermes egress health # one-shot check hermes egress health --watch # poll until healthy hermes egress health --timeout 30 # fail after 30s Exit codes: 0 proxy is up and listening 1 pid exists but port not accepting connections 2 proxy not running or not configured The liveness report uses the configured bind host from _read_http_listen_from_config() rather than a hardcoded 127.0.0.1. On Linux the daemon binds the docker bridge gateway (e.g. 172.17.0.1) so sandboxes can reach it; a loopback-only probe would report a perfectly healthy daemon as dead. get_status() already probes that host for status.listening, and we surface the same host:port in the output so the reported address matches what was tested. Falls back to loopback only when no proxy.yaml exists (matching get_status()). Closes the health-endpoint follow-up raised in PR NousResearch#30179 review. 9 tests added in test_proxy_cli_health.py.
Add the health subcommand to the CLI reference tree and Failure modes section so operators can diagnose proxy state quickly. Companion to the feat(egress) health subcommand implementation.
|
Nice — thanks @erhnysr, this gives me the clean base I wanted. Now that the reland (#70848) is in and |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for adding a focused operator-facing liveness command. The configured-bind-host approach matches current get_status() behavior (agent/proxy_sources/iron_proxy.py:2335-2375) and avoids the documented Docker bridge false-negative.
Problems
hermes_cli/proxy_cli.py:829returns immediately whenever--watchis absent, sohermes egress health --timeout 30never waits. That conflicts with the PR's argument help (hermes_cli/proxy_cli.py:145) and documented standalone example (website/docs/user-guide/egress/iron-proxy.md:243).- The command inventory in
website/docs/reference/cli-commands.md:651-708also needs the new health subcommands; it currently proceeds fromstatusdirectly todisable.
Suggested changes
- Make a positive timeout enter the polling path, or reject timeout without watch, and add a regression test for that contract.
- Update the CLI reference alongside the user-guide page.
Automated hermes-sweeper review.
| ) | ||
| return 0 | ||
|
|
||
| if not watch: |
There was a problem hiding this comment.
--timeout is advertised as usable on its own, but this early return bypasses the deadline loop, so hermes egress health --timeout 30 exits after one probe. Please either make a positive timeout imply polling or reject it without --watch, with a regression test for the chosen contract.
|
Confirmed @teknium1's read — @erhnysr the cleanest fix is to let a positive # A positive --timeout implies polling even without --watch, so the
# documented `hermes egress health --timeout 30` waits for the
# deadline instead of returning after a single probe.
if not watch and timeout <= 0:
return _check()That reuses the existing |
cmd_health returned after a single probe whenever --watch was absent, so 'hermes egress health --timeout 30' never entered the deadline loop even though the arg help and iron-proxy.md advertise it as standalone. Let a positive --timeout imply polling (Bartok9's suggested fix), reusing the existing deadline and --watch loop unchanged. - regression tests: --timeout N without --watch polls to healthy, and polls to the deadline then exits non-zero when never healthy - docs: add 'hermes egress health' + --watch/--timeout to the CLI command inventory in cli-commands.md (was missing between status and disable)
|
Thanks @teknium1 — both points fixed in 9cd7635. 1. # A positive --timeout implies polling even without --watch, so the
# documented `hermes egress health --timeout 30` waits for the deadline
# instead of returning after a single probe.
if not watch and timeout <= 0:
return _check()This reuses the existing 2. Regression tests (
3. CLI reference. Added Full suite green: |
Summary
Adds a
hermes egress healthsubcommand — a lightweight liveness check for the iron-proxy daemon, requested by @teknium1 as a standalone follow-up to the iron-proxy egress feature (#30179) now that it has re-landed (#70848).Exit codes are script/cron-friendly:
012The bind-host detail (@teknium1's review note)
The liveness report reads the configured bind host via
_read_http_listen_from_config()instead of hardcoding127.0.0.1. This matters:172.17.0.1) so sandboxes can reach it viahost.docker.internal:host-gateway. A loopback-only probe would report a perfectly healthy daemon as dead.get_status()already probes this host forstatus.listening; the command surfaces the samehost:portin its output so the reported address matches what was actually tested. It falls back to loopback only when noproxy.yamlexists (matchingget_status()'s own fallback).Scope
Deliberately minimal — only the CLI command, its tests, and docs. No changes to the re-landed
iron_proxy.py, so this sits cleanly on top of the reland and does not conflict with @Bartok9's stack (#35149 / #35187 / #35188), which is queued to build on top of this once it lands.hermes_cli/proxy_cli.py—healthsubparser +cmd_healthtests/hermes_cli/test_proxy_cli_health.py— 9 tests (exit codes,--watch,--timeout,KeyboardInterrupt, and the bind-host reporting: docker-bridge host reported not loopback, and loopback fallback when unconfigured)website/docs/user-guide/egress/iron-proxy.md— CLI reference tree + Failure-modes entryTesting
ruff checkclean on the changed files.Closes the health-endpoint follow-up raised in the #30179 review.