Watch
0
0
Fork
You've already forked hyperhive
0

swarm-otel: route the gateway from the receiver list and check the sockets accept

Refs #4815
This commit is contained in:
atlas 2026-10-24 20:53:47 +02:00 • committed by mara
commit 1325f44707
3 changed files with 123 additions and 19 deletions

View file

@ -851,6 +851,75 @@ let
&& lib.elem "gateway-nginx" unit.SupplementaryGroups
&& unit.UMask == "0007";
}
{
# The routes the gateway forwards to are exactly the sockets the
# collector listens on, and none is a TCP address: a route left on
# loopback after the collector moved into its own netns returns a 502 to
# every producer while the collector runs. `verdict` is applied to
# broken copies of the routes too, so a check that cannot fail is caught.
name = "the gateway's collector routes are the collector's unix receivers and none is loopback";
ok =
let
host = serviceContainersWithCi;
otel = host.services.hyperhive.swarm.otel;
s = host.containers.swarm-otel.config.services.opentelemetry-collector.settings;
locs = host.services.nginx.virtualHosts.${otel.domain}.locations;
sorted = xs: lib.sort lib.lessThan (lib.unique xs);
unixReceivers = lib.filter (r: (r.protocols.http.transport or null) == "unix") (
lib.attrValues s.receivers
);
receiverSocks = sorted (map (r: r.protocols.http.endpoint) unixReceivers);
socketOfRoute =
p:
if lib.hasPrefix "http://unix:" p then
lib.removeSuffix ":/" (lib.removePrefix "http://unix:" p)
else
p;
verdict =
routes:
let
passes = lib.filter (p: p != null) (map (l: l.proxyPass or null) (lib.attrValues routes));
in
sorted (map socketOfRoute passes) == receiverSocks
&& !(lib.any (p: lib.hasInfix "127.0.0.1" p || lib.hasInfix "localhost" p) passes);
in
verdict locs
&& lib.elem "/run/swarm-otel/h1.sock" receiverSocks
&& lib.elem "/run/swarm-otel/${otel.producerName}.sock" receiverSocks
&& lib.elem "/run/swarm-otel/swarm-grafana.sock" receiverSocks
&&
lib.length unixReceivers == lib.length (lib.attrNames host.services.hyperhive.swarm.hives)
+ 1
+ lib.length (lib.attrNames otel.serviceProducers)
# Each broken copy must fail: a TCP route, a missing route, a route to
# a socket no receiver has.
&& !verdict (locs // { "/h1/".proxyPass = "http://127.0.0.1:4319/"; })
&& !verdict (lib.removeAttrs locs [ "/h1/" ])
&& !verdict (locs // { "/x/".proxyPass = "http://unix:/run/swarm-otel/x.sock:/"; });
}
{
# After the container, restarted with it, and run as the gateway's user,
# so a socket nginx cannot open fails the unit. Every receiver socket is
# in the script.
name = "the collector's receiver sockets are connect-checked after the container starts";
ok =
let
host = serviceContainersWithCi;
u = host.systemd.services.swarm-otel-sockets;
s = host.containers.swarm-otel.config.services.opentelemetry-collector.settings;
socks = map (r: r.protocols.http.endpoint) (
lib.filter (r: (r.protocols.http.transport or null) == "unix") (lib.attrValues s.receivers)
);
in
socks != [ ]
&& lib.all (sock: lib.hasInfix sock u.script) socks
&& !(lib.hasInfix "/run/swarm-otel/absent.sock" u.script)
&& u.serviceConfig.Type == "oneshot"
&& u.serviceConfig.User == host.services.nginx.user
&& lib.elem "container@swarm-otel.service" u.after
&& lib.elem "container@swarm-otel.service" u.partOf
&& lib.elem "multi-user.target" u.wantedBy;
}
{
# The DynamicUser layout keeps the history under /var/lib/private; the
# static user's StateDirectory is the plain path. The move runs as its