feat(#3125): reshape the hive-to-swarm OTEL hop by domain
Drops swarm.otel.url (a loopback default an operator had to override on a split host) in favor of swarm.otel.domain -- the same gateway.localNames + nginx-vhost-through-the-gateway shape every other swarm service (authelia, grafana, victoriametrics, ui) already uses. The hive tier's exporter now reaches it as https://<domain> unconditionally, resolved locally by dnsmasq on a co-located host and over the real network otherwise, instead of a config knob nobody sets until they hit the silent drop. Costs CA trust on the hive tier: otel.nix wires lib/hive-ca-trust.nix's trustBundle with hostUnit = true on the opentelemetry-collector host unit, the same flag #3441/#3442 added for swarm-controller and hive-c0re. mara, #3125 comment 58363: "go c".
This commit is contained in:
parent
28623e5eff
commit
d2fb4bff79
5 changed files with 96 additions and 37 deletions
|
|
@ -32,6 +32,14 @@ let
|
|||
swarmCfg = config.services.hyperhive.swarm;
|
||||
otelCfg = config.services.hyperhive.otel;
|
||||
vmCfg = config.services.hyperhive.swarm.victoriametrics;
|
||||
hyperhiveCfg = config.services.hyperhive;
|
||||
gatewayCfg = hyperhiveCfg.gateway;
|
||||
swarmDomain = hyperhiveCfg.swarm.domain;
|
||||
|
||||
# Total on a null swarm domain for the same reason every sibling module is:
|
||||
# the required-domain assertion in hive-network.nix should be what an
|
||||
# operator sees, not a coercion error from here.
|
||||
domainBase = if swarmDomain == null then "invalid" else swarmDomain;
|
||||
in
|
||||
{
|
||||
options.services.hyperhive.swarm.otel = {
|
||||
|
|
@ -47,8 +55,8 @@ in
|
|||
wherever the shared services live rather than on every hive.
|
||||
|
||||
A hive that does not run it still runs its own hive-tier collector
|
||||
(`services.hyperhive.otel.enable`) and points it here with
|
||||
{option}`services.hyperhive.swarm.otel.url`.
|
||||
(`services.hyperhive.otel.enable`) and reaches this one by name, at
|
||||
{option}`services.hyperhive.swarm.otel.domain`.
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
@ -99,29 +107,47 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
url = lib.mkOption {
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "http://127.0.0.1:${toString cfg.port}";
|
||||
defaultText = lib.literalExpression ''"http://127.0.0.1:''${toString config.services.hyperhive.swarm.otel.port}"'';
|
||||
default = "otel.${domainBase}";
|
||||
defaultText = lib.literalExpression ''"otel.''${services.hyperhive.swarm.domain}"'';
|
||||
description = ''
|
||||
Where the **hive** tier sends what it receives — this collector's
|
||||
OTLP/HTTP base URL.
|
||||
Name the gateway serves this on. A sibling of the swarm's other
|
||||
service names, so the swarm-services sub-CA can issue for it — see
|
||||
`hive-tls.nix` for why a service name being a sibling rather than a
|
||||
child decides which CA may sign it.
|
||||
|
||||
The default addresses it on loopback, which is correct while the
|
||||
two tiers share a host: every swarm container runs in the host's
|
||||
network namespace, so a swarm service is reachable there exactly
|
||||
as the metrics store already is.
|
||||
|
||||
⚠️ That default is a *default*, not an assumption baked into the
|
||||
exporter. A hive whose swarm collector runs elsewhere sets this to
|
||||
that host's address, and nothing else changes — a loopback literal
|
||||
written directly into the exporter would have made the split-host
|
||||
case a code change instead of a config one.
|
||||
This is what the **hive** tier's exporter reaches — the hive
|
||||
collector is a plain producer against this name exactly like every
|
||||
other client of a swarm service, resolved locally by dnsmasq on a
|
||||
co-located host and over the real network otherwise. There is no
|
||||
separate loopback-vs-remote knob to get wrong: `swarm-nats` is the
|
||||
deliberate exception to this pattern (its cross-hive reach is the
|
||||
wireguard mesh, not the gateway), everything else in this swarm
|
||||
addresses its siblings by name.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.services.hyperhive.enable && cfg.enable) {
|
||||
# The gateway name, inside `cfg.enable` — that guard is the load-bearing
|
||||
# part. Every hive in a swarm may know this collector exists, but only
|
||||
# the host that RUNS it may claim the name; a client hive declaring the
|
||||
# vhost would answer for a service it does not have.
|
||||
services.hyperhive.gateway.localNames = [ cfg.domain ];
|
||||
|
||||
# OTLP/HTTP, not a browsable UI, but the same reverse-proxy shape as
|
||||
# every sibling swarm service: TLS terminates here, then plain http to
|
||||
# the co-located container over loopback (shared netns, like the store
|
||||
# this collector writes to).
|
||||
services.nginx.virtualHosts."${cfg.domain}" = (gatewayCfg.lib.tlsFor cfg.domain) // {
|
||||
listen = gatewayCfg.lib.listen;
|
||||
extraConfig = gatewayCfg.lib.securityHeaders;
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:${toString cfg.port}";
|
||||
};
|
||||
};
|
||||
|
||||
assertions = [
|
||||
{
|
||||
# The tier exists to hold the upstream credential and to write the
|
||||
|
|
|
|||
Loading…
Reference in a new issue