Watch
0
0
Fork
You've already forked hyperhive
0

swarm-otel: routes carry a route header and the readiness probe requires it

This commit is contained in:
atlas 2026-10-24 21:19:39 +02:00 • committed by mara
commit 7680b07fcb
3 changed files with 101 additions and 15 deletions

View file

@ -312,6 +312,9 @@ let
# different sockets.
receiverSockets = hiveSockets // lib.genAttrs ([ swarmTierName ] ++ serviceProducerNames) socketOf;
upstreamOf = sock: "http://unix:${sock}:/";
# Set by nginx on every response from a route, whatever its status, so the
# readiness check can tell a route the running nginx has from the catch-all.
routeHeader = "X-Swarm-Otel-Route";
allSockets = lib.attrValues receiverSockets;
# The gid the gateway's nginx runs as. Only the number crosses into the
@ -496,8 +499,18 @@ in
# serves `/v1/metrics` and knows nothing about hives. Without it
# the receiver sees `/<hive>/v1/metrics` and answers 404 to a
# request that authenticated perfectly.
#
# ⚠️ A location with an `add_header` of its own inherits none of the
# server-scope ones, so the security headers are repeated here.
lib.mapAttrs' (
owner: sock: lib.nameValuePair "/${owner}/" { proxyPass = upstreamOf sock; }
owner: sock:
lib.nameValuePair "/${owner}/" {
proxyPass = upstreamOf sock;
extraConfig = ''
${gatewayCfg.lib.securityHeaders}
add_header ${routeHeader} ${owner} always;
'';
}
) receiverSockets
// {
# There is no swarm-wide inbox, and a closed door is the honest
@ -698,10 +711,11 @@ in
# A collector that is up but not listening on a socket the gateway names
# answers every producer with a 502 and fails no unit. This one fails when
# any receiver socket refuses a connection for 30s, or when the running
# nginx cannot reach one, so the switch reports it. It runs as the
# nginx does not route to one, so the switch reports it. It runs as the
# gateway's user: a socket nginx cannot open fails too. On the socket any
# HTTP status counts as accepted. Through nginx 000, 502 and 504 fail and
# every other status passes, a collector's 4xx included.
# HTTP status counts as accepted. Through nginx, 000, 502 and 504 fail, and so
# does a response without its route's own header; any other status passes, a
# collector's 4xx included.
systemd.services.swarm-otel-sockets = {
description = "wait for every swarm collector receiver socket to accept a connection";
after = [
@ -734,15 +748,17 @@ in
done
# The running nginx, not its config. `--resolve` pins the vhost to
# this host's gateway.
# this host's gateway. The route header is what separates a route nginx
# has from its catch-all 404.
for route in ${lib.escapeShellArgs (lib.attrNames receiverSockets)}; do
for _ in $(seq 30); do
code=$(curl -sk --max-time 5 --resolve ${cfg.domain}:443:127.0.0.1 -o /dev/null -w '%{http_code}' \
"https://${cfg.domain}/$route/") || code=000
case $code in 000 | 502 | 504) ;; *) continue 2 ;; esac
out=$(curl -sk --max-time 5 --resolve ${cfg.domain}:443:127.0.0.1 -o /dev/null \
-w '%{http_code} %header{${lib.toLower routeHeader}}' "https://${cfg.domain}/$route/") || out=000
read -r code seen <<< "$out"
case $code in 000 | 502 | 504) ;; *) [ "$seen" = "$route" ] && continue 2 ;; esac
sleep 1
done
echo "https://${cfg.domain}/$route/ answered $code after 30s" >&2
echo "https://${cfg.domain}/$route/ answered $code, route header '$seen', after 30s" >&2
exit 1
done
'';