nix: address swarm-bao's journal forwarder by swarm name

The forwarder pointed at the hive bridge address and was gated on the
hive's `otel.enable`, so it existed only where a hive collector stood
beside it. It now exports to `swarm.otel.domain` — the gateway-served
name that resolves locally when co-located and over the network
otherwise — on the swarm tier's own producer route, and is gated on
`deploy.swarm-otel.enable` like its sibling `scrapeHere`.

Refs #4526
This commit is contained in:
atlas 2026-09-19 18:32:13 +02:00 committed by mara
commit ca8fc4ca64
2 changed files with 66 additions and 37 deletions

View file

@ -455,20 +455,31 @@ let
scrapeHere = deployCfg.swarm-otel.enable;
# Whether this container forwards its own journal, and it is gated on the
# HIVE collector rather than the swarm one: the forwarder below pushes to the
# first hop, and with `otel.enable` off nothing listens at that address at
# all — a collector aimed at it would retry forever while looking healthy.
shipJournal = hyperhiveCfg.otel.enable;
# Whether this container forwards its own journal, gated exactly like
# `scrapeHere` above: the store is a swarm-tier component, so what decides
# this is the swarm collector existing, never which hive happens to stand
# beside it. On the hive flag it rendered no forwarder at all on a host that
# runs the swarm's own collector but not a hive's.
shipJournal = deployCfg.swarm-otel.enable;
# The first hop for anything running on this host, byte for byte what
# hive-c0re hands every agent (`firstHop` in ./hive-c0re/environment.nix).
# Plain http with no credential is the tier boundary working as designed, not
# an omission: the hive's collector is the only thing here that holds one,
# and presenting it to the swarm is its job. This container shares the host
# netns (`privateNetwork = false` below), so the bridge address is reachable
# from inside it without the firewall hole an agent container needs.
otelFirstHop = "http://${networkCfg.bridgeIp}:${toString hyperhiveCfg.otel.collector.port}";
# The swarm collector's own name, served by the gateway — the address
# `swarm.otel.domain` exists to be, resolved by dnsmasq on a co-located host
# and over the real network otherwise, with no loopback-vs-remote knob to get
# wrong. The path selects WHICH receiver: this container is a swarm-level
# producer and not a hive, so it is the swarm tier's own route, the same one
# ./swarm-controller.nix exports to. `/` on that vhost answers 404.
#
# The port suffix follows ./hive-forge/default.nix: elided on the canonical
# 443 so this renders byte for byte like the tier's other two consumers of
# this name, present when an operator moved the gateway's TLS port.
otelFirstHop = "https://${swarmOtelCfg.domain}${gatewayPortSuffix}/${swarmOtelCfg.producerName}";
swarmOtelCfg = hyperhiveCfg.swarm.otel;
gatewayPortSuffix =
let
p = hyperhiveCfg.gateway.httpsPort;
in
lib.optionalString (p != 443) ":${toString p}";
# Non-zero is what SERVES the endpoint at all — the switch is a duration, not
# a boolean, so a zero here is an openbao that answers 404 on a listener
@ -1637,7 +1648,7 @@ in
"none"
]);
message = ''
services.hyperhive.otel.enable is on, so ${cfg.machine} forwards its
services.hyperhive.deploy.swarm-otel.enable is on, so ${cfg.machine} forwards its
own journal but services.journald.storage is
"${config.services.journald.storage}" in this container.
@ -1705,8 +1716,8 @@ in
];
# `otlphttp` unconditionally, unlike the agent forwarder's
# protocol-derived exporter name: the first hop's receiver
# speaks OTLP/HTTP protobuf whatever the hive's *upstream*
# protocol-derived exporter name: the swarm receiver speaks
# OTLP/HTTP protobuf whatever this tier's *upstream*
# protocol is, which is the same reason
# ./hive-c0re/environment.nix pins the protocol it hands out.
# `endpoint` is a BASE the exporter appends `/v1/logs` to.