fix(#947): include manager in gateway agents.conf routing

This commit is contained in:
damocles 2026-06-01 17:43:20 +02:00 committed by mara
commit 11d74e1e17
2 changed files with 13 additions and 12 deletions

View file

@ -130,9 +130,11 @@ logical agent name → TCP web port:
Written alongside `agents.conf` on every topology change. Ports come from
`lifecycle::agent_web_port(name)` — a pure FNV-1a hash of the name,
reproducible from the name alone. The manager is excluded: the gateway
routes `/` directly to c0re's dashboard upstream, not through a
per-agent `/agent/<name>/` prefix.
reproducible from the name alone. The manager is excluded: it always
uses a unix socket (`HIVE_WEB_SOCKET` is unconditionally set for the
manager role), so its TCP port never appears in the fallback map.
The gateway routes `/agent/root/` to the manager's unix socket via
`agents.conf` alongside sub-agents.
The file doubles as a human-readable audit artifact — `cat agent-ports.json`
shows every registered sub-agent and its deterministic port assignment. TCP

View file

@ -10,7 +10,7 @@ use std::fmt::Write as _;
use std::path::PathBuf;
use crate::agent_sockets;
use crate::lifecycle::{self, MANAGER_NAME};
use crate::lifecycle;
const HOST_CONF_PATH: &str = "/var/lib/hyperhive/gateway/agents.conf";
@ -39,7 +39,8 @@ const PROXY_HEADER_BLOCK: &str = " proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;";
/// Render the nginx include body for `names`. Manager is filtered out.
/// Render the nginx include body for `names`. Manager is included so
/// `/agent/root/` is routable through the gateway.
///
/// When `frontend_dir` is `Some(path)` each agent gets split location
/// blocks:
@ -66,9 +67,6 @@ fn render(names: &[String], frontend_dir: Option<&str>) -> String {
\n# Gateway reloads nginx automatically on each update (systemd path unit).\n",
);
for name in names {
if name == MANAGER_NAME {
continue;
}
let port = lifecycle::agent_web_port(name);
let upstream = if agent_sockets::ready_marker_for(name).exists() {
format!(
@ -235,6 +233,7 @@ fn reload_gateway_nginx() {
#[cfg(test)]
mod tests {
use super::*;
use crate::lifecycle::MANAGER_NAME;
// ── legacy mode (frontend_dir = None) ──────────────────────────────
@ -247,13 +246,13 @@ mod tests {
}
#[test]
fn render_filters_manager() {
fn render_includes_manager() {
let names: Vec<String> = [MANAGER_NAME, "iris"]
.iter()
.map(|s| (*s).to_owned())
.collect();
let body = render(&names, None);
assert!(!body.contains(&format!("/agent/{MANAGER_NAME}/")));
assert!(body.contains(&format!("/agent/{MANAGER_NAME}/")));
assert!(body.contains("/agent/iris/"));
}
@ -358,13 +357,13 @@ mod tests {
}
#[test]
fn split_mode_filters_manager() {
fn split_mode_includes_manager() {
let names: Vec<String> = [MANAGER_NAME, "iris"]
.iter()
.map(|s| (*s).to_owned())
.collect();
let body = render(&names, Some(FAKE_FRONTEND));
assert!(!body.contains(&format!("/agent/{MANAGER_NAME}/")));
assert!(body.contains(&format!("/agent/{MANAGER_NAME}/")));
assert!(body.contains("/agent/iris/"));
}