fix(swarm-otel): reserve the swarm tier's component names from hive names
The collector names components `<kind>/<owner>` — a hive name for the per-hive pipelines, the literal `swarm` for the swarm tier's own. Both land in one attrset via `//`, so a hive named `swarm` replaced the swarm tier's parts and lost its own: its receiver kept accepting pushes into a pipeline that routed nowhere, and its samples lost the `hive` stamp that makes attribution unforgeable. Zero failed assertions. The reserved name is now bound once and interpolated at each swarm-tier use, so the guard checks the same string the config emits rather than a copy of it. A second swarm-tier pipeline joins the list and inherits the check without touching the assertion.
This commit is contained in:
parent
8178b0b55c
commit
4de4878e74
1 changed files with 47 additions and 3 deletions
|
|
@ -43,6 +43,21 @@ let
|
|||
|
||||
autheliaCfg = hyperhiveCfg.swarm.authelia;
|
||||
|
||||
# The collector names its components `<kind>/<owner>`, where `<owner>` is a
|
||||
# hive name for the per-hive pipelines and this literal for the swarm tier's
|
||||
# own. The two share one namespace and are merged with `//`, so a hive named
|
||||
# this would silently REPLACE the swarm tier's parts — and lose its own
|
||||
# pipeline in the process, while its receiver keeps accepting pushes.
|
||||
#
|
||||
# Bound once and interpolated at every swarm-tier use below, so the assertion
|
||||
# that reserves it is checking the same string the config emits. A literal
|
||||
# repeated at each site would let the guard and the config drift apart, which
|
||||
# is the failure this guard exists to prevent.
|
||||
swarmTierName = "swarm";
|
||||
# Every `<owner>` this module claims for itself. One entry today; a second
|
||||
# swarm-tier pipeline would be added here and inherit the check for free.
|
||||
reservedOwners = [ swarmTierName ];
|
||||
|
||||
# `attrNames` is sorted, so this is a function of the hive SET and not of
|
||||
# the order anyone wrote it in.
|
||||
#
|
||||
|
|
@ -286,6 +301,33 @@ in
|
|||
List the swarm's hives.
|
||||
'';
|
||||
}
|
||||
{
|
||||
# A hive whose name is one this module claims for itself collides in
|
||||
# the collector's component namespace, and `//` resolves it silently:
|
||||
# the swarm tier's parts win, that hive's `metrics/<name>` pipeline
|
||||
# disappears, and its `resource/<name>` stamp goes with it — so the
|
||||
# hive still connects and pushes into nothing, unlabelled.
|
||||
#
|
||||
# Checked rather than documented for the same reason the port range
|
||||
# is: the names are all known at evaluation time, and the runtime
|
||||
# symptom is a hive that looks healthy and reports no metrics, with
|
||||
# nothing in any log naming the cause.
|
||||
assertion = !lib.any (h: lib.elem h reservedOwners) (lib.attrNames hyperhiveCfg.swarm.hives);
|
||||
message = ''
|
||||
services.hyperhive.swarm.hives contains ${
|
||||
lib.concatMapStringsSep ", " (h: "'${h}'") (
|
||||
lib.filter (h: lib.elem h reservedOwners) (lib.attrNames hyperhiveCfg.swarm.hives)
|
||||
)
|
||||
}, which the swarm collector reserves for its own pipelines.
|
||||
|
||||
The collector names components `<kind>/<owner>` and uses the hive
|
||||
name as the owner, so such a hive would silently replace the
|
||||
swarm tier's parts and lose its own — it would keep accepting
|
||||
pushes into a pipeline that routes nowhere.
|
||||
|
||||
Rename the hive. Reserved: ${lib.concatMapStringsSep ", " (n: "'${n}'") reservedOwners}.
|
||||
'';
|
||||
}
|
||||
{
|
||||
# A hive proves who it is with a token this provider mints, so
|
||||
# there is no version of this collector that runs without one.
|
||||
|
|
@ -512,9 +554,9 @@ in
|
|||
# not have. Keeping it out of them makes the absence
|
||||
# structural rather than something to remember to strip.
|
||||
// lib.optionalAttrs (cfg.scrapeTargets != { }) {
|
||||
"metrics/swarm" = {
|
||||
"metrics/${swarmTierName}" = {
|
||||
receivers = [ "prometheus" ];
|
||||
processors = [ "resource/swarm" ];
|
||||
processors = [ "resource/${swarmTierName}" ];
|
||||
exporters = exporterNames;
|
||||
};
|
||||
};
|
||||
|
|
@ -602,8 +644,10 @@ in
|
|||
# an invented one (a sentinel, the local hive's name) would be
|
||||
# queried as though it meant something.
|
||||
// lib.optionalAttrs (cfg.scrapeTargets != { }) {
|
||||
"resource/swarm".attributes = [
|
||||
"resource/${swarmTierName}".attributes = [
|
||||
{
|
||||
# The metric LABEL, a different namespace from the
|
||||
# component name above — deliberately not interpolated.
|
||||
key = "swarm";
|
||||
value = swarmDisplayName;
|
||||
action = "upsert";
|
||||
|
|
|
|||
Loading…
Reference in a new issue