From 09fe94a8ca18d3e00510099604c60a7cb52e8018 Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 30 Aug 2026 04:14:20 +0200 Subject: [PATCH] deploy: name the swarm collector swarm-otel, not otel Per review: `deploy.otel` does not imply swarm level, and there is a hive-tier collector too -- `services.hyperhive.otel.enable`, which every hive runs. The tier is the entire distinction between the two, so the name has to carry it, matching `deploy.swarm-controller` and `deploy.swarm-ui`. 15 swarm-tier references renamed across 8 files. The hive-tier collector's 16 references are deliberately untouched, verified as a control on the same command. Three spellings needed three different patterns, all inside this one rename: the dotted path; the `mkRenamedOptionModule` target written as a nix LIST (`[ ... "deploy" "otel" "enable" ]`), which no dotted grep can match; and prose in docs/observability.md spelled `deploy.otel` with neither `.enable` nor a leading dot. Unanchored `deploy\.otel\b` is the only pattern that finds all three. --- docs/observability.md | 2 +- nix/host-modules/deploy.nix | 15 ++++++++------- nix/host-modules/otel.nix | 4 ++-- nix/host-modules/swarm-authelia.nix | 2 +- nix/host-modules/swarm-controller.nix | 4 ++-- nix/host-modules/swarm-grafana.nix | 2 +- nix/host-modules/swarm-nats.nix | 2 +- nix/host-modules/swarm-otel.nix | 12 ++++++------ nix/host-modules/swarm-required-services.nix | 2 +- nix/host-modules/swarm.nix | 2 +- 10 files changed, 24 insertions(+), 23 deletions(-) diff --git a/docs/observability.md b/docs/observability.md index 13b3d649..cb2f2b21 100644 --- a/docs/observability.md +++ b/docs/observability.md @@ -80,7 +80,7 @@ the host is: | | runs where | receives from | does | |---|---|---|---| | **hive tier** — `otel.enable` | every hive with agents | that hive's agents, on the bridge | forwards to the swarm tier. Holds no credential, picks no destination | -| **swarm tier** — `deploy.otel` | once per swarm | every hive's collector | writes the swarm's store and exports upstream | +| **swarm tier** — `deploy.swarm-otel` | once per swarm | every hive's collector | writes the swarm's store and exports upstream | An all-local host runs both, and needs nothing said about the hop between them. diff --git a/nix/host-modules/deploy.nix b/nix/host-modules/deploy.nix index a07e62d3..8ae9ae71 100644 --- a/nix/host-modules/deploy.nix +++ b/nix/host-modules/deploy.nix @@ -67,7 +67,7 @@ in ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "otel" "enable" ] - [ "services" "hyperhive" "deploy" "otel" "enable" ] + [ "services" "hyperhive" "deploy" "swarm-otel" "enable" ] ) ]; @@ -131,7 +131,7 @@ in ''; }; - otel.enable = lib.mkOption { + swarm-otel.enable = lib.mkOption { type = lib.types.bool; default = false; description = '' @@ -142,11 +142,12 @@ in metrics pair it feeds: a swarm has one of these, and it belongs wherever the shared services live rather than on every hive. - ⚠️ Not to be confused with - {option}`services.hyperhive.otel.enable`, the **hive-tier** - collector, which every hive runs and which is a different option. - A hive that does not run the swarm collector still runs its own, - and reaches this one by name at + Named `swarm-otel` rather than `otel` because there are two + collectors and the tier is the whole distinction: + {option}`services.hyperhive.otel.enable` is the **hive-tier** one, + which every hive runs. A bare `deploy.otel` would not say which + it meant. A hive that does not run the swarm collector still runs + its own, and reaches this one by name at {option}`services.hyperhive.swarm.otel.domain`. ''; }; diff --git a/nix/host-modules/otel.nix b/nix/host-modules/otel.nix index 84fcd7f5..19a44f6a 100644 --- a/nix/host-modules/otel.nix +++ b/nix/host-modules/otel.nix @@ -61,7 +61,7 @@ in their own containers can reach. That collector forwards to the swarm's - ({option}`services.hyperhive.deploy.otel.enable`), which holds the + ({option}`services.hyperhive.deploy.swarm-otel.enable`), which holds the upstream credential and writes the swarm's store. So an agent never sees the credential, and neither does this tier. @@ -81,7 +81,7 @@ in has left the swarm. Read by the swarm's collector - ({option}`services.hyperhive.deploy.otel.enable`), which is the only + ({option}`services.hyperhive.deploy.swarm-otel.enable`), which is the only tier that holds the upstream credential. An agent is handed the *first* hop instead — this hive's own collector — so this value is never given to a container. diff --git a/nix/host-modules/swarm-authelia.nix b/nix/host-modules/swarm-authelia.nix index 14ca2659..4c8ba9e7 100644 --- a/nix/host-modules/swarm-authelia.nix +++ b/nix/host-modules/swarm-authelia.nix @@ -1010,7 +1010,7 @@ in # the option it gates are visibly the same path: gating the wrong one is # not a build error, it is a target that is always declared or never is. services.hyperhive.swarm.otel.scrapeTargets = - lib.mkIf config.services.hyperhive.deploy.otel.enable + lib.mkIf config.services.hyperhive.deploy.swarm-otel.enable { authelia = "127.0.0.1:${toString cfg.metricsPort}"; }; diff --git a/nix/host-modules/swarm-controller.nix b/nix/host-modules/swarm-controller.nix index dd80856b..fbc5a3f3 100644 --- a/nix/host-modules/swarm-controller.nix +++ b/nix/host-modules/swarm-controller.nix @@ -174,14 +174,14 @@ let # so the two ends agree on exactly one path, `/v1/metrics`, arrived at # from opposite directions. # - # Gated on `deployCfg.otel.enable` alone (a swarm-wide fact, not "enabled + # Gated on `deployCfg.swarm-otel.enable` alone (a swarm-wide fact, not "enabled # HERE"): the collector is reachable by name wherever it runs, so a # controller not co-located with it now exports over https instead of # exporting nothing — the graceful-absence case left is "no swarm-otel # anywhere in this swarm at all," which `forgeEnv` already models the # same way. otelSwarmCfg = config.services.hyperhive.swarm.otel; - otelEnv = lib.optionalAttrs deployCfg.otel.enable { + otelEnv = lib.optionalAttrs deployCfg.swarm-otel.enable { OTEL_EXPORTER_OTLP_ENDPOINT = "https://${otelSwarmCfg.domain}/${otelSwarmCfg.producerName}"; # Checked by `swarm-otel.nix`'s `oidc/${producerName}` authenticator # against exactly this controller's own registered audience (see the diff --git a/nix/host-modules/swarm-grafana.nix b/nix/host-modules/swarm-grafana.nix index 9e58922c..4c4b159b 100644 --- a/nix/host-modules/swarm-grafana.nix +++ b/nix/host-modules/swarm-grafana.nix @@ -409,7 +409,7 @@ in # Declared here rather than in the collector's module, per that option's # rule: an entry exists where the service that named it runs. services.hyperhive.swarm.otel.scrapeTargets = - lib.mkIf config.services.hyperhive.deploy.otel.enable + lib.mkIf config.services.hyperhive.deploy.swarm-otel.enable { grafana = "127.0.0.1:${toString cfg.metricsPort}"; }; diff --git a/nix/host-modules/swarm-nats.nix b/nix/host-modules/swarm-nats.nix index 2c454f0e..8e98cac2 100644 --- a/nix/host-modules/swarm-nats.nix +++ b/nix/host-modules/swarm-nats.nix @@ -536,7 +536,7 @@ in # and only this one reads `scrapeTargets`. Written in full so the gate # and the option it gates are visibly the same path. services.hyperhive.swarm.otel.scrapeTargets = - lib.mkIf config.services.hyperhive.deploy.otel.enable + lib.mkIf config.services.hyperhive.deploy.swarm-otel.enable { nats = "127.0.0.1:${toString cfg.metricsPort}"; }; diff --git a/nix/host-modules/swarm-otel.nix b/nix/host-modules/swarm-otel.nix index 9c017675..baf64d13 100644 --- a/nix/host-modules/swarm-otel.nix +++ b/nix/host-modules/swarm-otel.nix @@ -180,7 +180,7 @@ let collectLogs = logExporterNames != [ ]; in { - # `enable` moved to `services.hyperhive.deploy.otel.enable` — see ./deploy.nix. + # `enable` moved to `services.hyperhive.deploy.swarm-otel.enable` — see ./deploy.nix. # ⚠️ That is the SWARM collector. The per-hive one keeps its own # `services.hyperhive.otel.enable` (./otel.nix) and is a different # option entirely — every hive runs that one. @@ -435,8 +435,8 @@ in }; }; - config = lib.mkIf (config.services.hyperhive.enable && deployCfg.otel.enable) { - # The gateway name, inside `deployCfg.otel.enable` — that guard is the load-bearing + config = lib.mkIf (config.services.hyperhive.enable && deployCfg.swarm-otel.enable) { + # The gateway name, inside `deployCfg.swarm-otel.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. @@ -601,7 +601,7 @@ in # samples and drops them — which looks healthy and loses data. assertion = otelCfg.endpoint != "" || deployCfg.victoriametrics.enable; message = '' - services.hyperhive.deploy.otel.enable is true but this collector + services.hyperhive.deploy.swarm-otel.enable is true but this collector has nowhere to send what it receives: services.hyperhive.otel.endpoint is empty and services.hyperhive.deploy.victoriametrics.enable is false. @@ -633,7 +633,7 @@ in # collector would listen on nothing while looking configured. assertion = hyperhiveCfg.swarm.hives != { }; message = '' - services.hyperhive.deploy.otel.enable is true but + services.hyperhive.deploy.swarm-otel.enable is true but services.hyperhive.swarm.hives is empty: ingest is authenticated per hive, so an empty roster means this collector accepts nothing from anyone. @@ -745,7 +745,7 @@ in # issuer URL evaluates cleanly and refuses every hive at runtime. assertion = autheliaCfg.url != null; message = '' - services.hyperhive.deploy.otel.enable is true but + services.hyperhive.deploy.swarm-otel.enable is true but services.hyperhive.swarm.authelia.url is null: every hive authenticates to this collector as itself, and the token comes from the swarm's identity provider. diff --git a/nix/host-modules/swarm-required-services.nix b/nix/host-modules/swarm-required-services.nix index 53308974..8e8e5fc1 100644 --- a/nix/host-modules/swarm-required-services.nix +++ b/nix/host-modules/swarm-required-services.nix @@ -79,7 +79,7 @@ in # The swarm collector that feeds the metrics pair, and the only tier # holding the upstream credential. ⚠️ NOT the per-hive collector below, # which every hive runs. - config.services.hyperhive.deploy.otel.enable = lib.mkDefault swarmCfg.enableRequiredServices; + config.services.hyperhive.deploy.swarm-otel.enable = lib.mkDefault swarmCfg.enableRequiredServices; # The metrics pair, deriving together on purpose: a store with no UI is # unreadable and a UI with no store is empty, so there is no sensible diff --git a/nix/host-modules/swarm.nix b/nix/host-modules/swarm.nix index bd50d95d..9bedc776 100644 --- a/nix/host-modules/swarm.nix +++ b/nix/host-modules/swarm.nix @@ -60,7 +60,7 @@ let # failed every POST and dropped the samples. ++ lib.optional deployCfg.grafana.enable swarmCfg.grafana.domain ++ lib.optional deployCfg.victoriametrics.enable swarmCfg.victoriametrics.domain - ++ lib.optional deployCfg.otel.enable swarmCfg.otel.domain + ++ lib.optional deployCfg.swarm-otel.enable swarmCfg.otel.domain # VictoriaLogs' vhost is new (was previously unpublished entirely — see # swarm-victorialogs.nix's file-top comment) and needs the same # membership every gateway-published swarm service needs: absent from