Watch
0
0
Fork
You've already forked hyperhive
0

swarm-otel: declare forgejo's published scrape target on every host

The forgejo entry in `swarm.otel.publishedScrapeTargets` was declared
under `deploy.forgejo.enable`, so it existed only in the forge host's
evaluation. Its two readers evaluate elsewhere: the collector renders
its prometheus jobs from its own host's list, and authelia registers
the collector client's audience from its own host's list. With the
forge, the collector and authelia on different hosts, the collector
had no forgejo job and authelia would have refused its token.

Declare the entry unconditionally in swarm-otel-service.nix, keyed on
`swarm.forge.domain`, which every host evaluates identically. A swarm
with no forge scrapes a name that answers nothing and shows a failing
job, the same trade the collector and grafana OIDC clients already make.

Single-host render is unchanged: the entry already existed there.

Part of #5205
This commit is contained in:
atlas 2026-10-13 10:24:17 +02:00 • committed by mara
commit 6c1ab87f68
4 changed files with 82 additions and 23 deletions

View file

@ -77,7 +77,7 @@ let
};
# A collector whose ONLY scrape work is published: loopback targets forced
# empty, one published job declared. Unreachable in a real deploy today —
# empty, published jobs only. Unreachable in a real deploy today —
# the module seeds `scrapeTargets.collector` under its own `enable`, so the
# loopback set is never empty on its own — which is exactly why the arm
# below needs a fixture that takes that seeding away. `mkForce` is what
@ -97,6 +97,11 @@ let
deploy.authelia.enable = true;
swarm.hives.h2.domain = "h2.t.local";
};
# authelia with neither the forge nor the collector beside it.
inherit (shared) autheliaOnly;
forgeMetricsUrl = "https://forge.t.local/metrics";
cases = [
{
# The collector's non-assertion, the deliberate mirror of Grafana's
@ -152,6 +157,45 @@ let
&& (s.receivers ? prometheus)
&& builtins.elem "prometheus" s.service.pipelines."metrics/swarm".receivers;
}
{
# The collector reads its own host's target list, so a forge entry
# declared only where the forge runs is a job no split deploy renders.
# `otelRemoteAuthelia` runs neither the forge nor authelia.
name = "a collector on a host without the forge scrapes the forge by name";
ok =
let
s = otelSettings otelRemoteAuthelia;
job = lib.findFirst (j: j.job_name == "forgejo") null s.receivers.prometheus.config.scrape_configs;
in
!otelRemoteAuthelia.services.hyperhive.deploy.forgejo.enable
&& job != null
&& job.static_configs == [ { targets = [ "forge.t.local" ]; } ]
&& job.metrics_path == "/metrics"
&& job.oauth2.endpoint_params.audience == forgeMetricsUrl
&& builtins.elem "prometheus" s.service.pipelines."metrics/swarm".receivers;
}
{
# The other half: authelia refuses a token for an audience its client
# was not registered for, and registers it from its OWN host's list.
name = "authelia on a host without the forge or the collector admits the collector's forge scrape";
ok =
let
h = autheliaOnly.services.hyperhive;
client = lib.findFirst (c: c.id == h.swarm.otel.clientId) null h.swarm.authelia.oidc.clients;
rules =
autheliaOnly.containers.swarm-authelia.config.services.authelia.instances.swarm.settings.access_control.rules;
in
!h.deploy.forgejo.enable
&& !h.deploy.swarm-otel.enable
&& client != null
&& builtins.elem forgeMetricsUrl client.audience
&& builtins.elem {
domain = "forge.t.local";
resources = [ "^/metrics$" ];
policy = "one_factor";
subject = [ "oauth2:client:${h.swarm.otel.clientId}" ];
} rules;
}
{
# Read against the roster the fixture declares rather than against
# names spelled here: an arm naming `h1` passes on a single-hive