nix: split module-eval into per-subsystem checks
The single module-eval derivation forced ~62 full nixosSystem fixtures live at once to compute its cases list: 10.6GB peak RSS / 5m25s to evaluate, by far the dominant cost in nix flake check. Splits it into 21 independent checks.module-eval-* derivations (1-7 fixtures each) sharing builders/helpers via module-eval/lib.nix, so no single derivation needs more than a handful of fixtures live at once. A few cases spanning two clusters carry a small duplicated fixture rather than threading shared state through lib.nix.
This commit is contained in:
parent
69b70a9c6f
commit
dc418a5223
24 changed files with 3760 additions and 2997 deletions
115
nix/module-eval/name-guards.nix
Normal file
115
nix/module-eval/name-guards.nix
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# `checks.module-eval-name-guards` — see ./lib.nix for the shared
|
||||
# rationale (why this suite exists, naming convention, "evaluates
|
||||
# not executes").
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
self,
|
||||
nixosSystem,
|
||||
}:
|
||||
let
|
||||
inherit
|
||||
(import ./lib.nix {
|
||||
inherit
|
||||
pkgs
|
||||
lib
|
||||
self
|
||||
nixosSystem
|
||||
;
|
||||
})
|
||||
hive
|
||||
runGroup
|
||||
;
|
||||
|
||||
# The hive-name guards, with the collector explicitly OFF. That is the whole
|
||||
# property: the guards live where `swarm.hives` is declared, so they run in a
|
||||
# deployment that has a secret store and no collector — which used to skip
|
||||
# them entirely, because they were assertions inside swarm-otel's own `mkIf`.
|
||||
#
|
||||
# ⚠️ `controllerCommonName` is overridden to a name containing NO reserved
|
||||
# fragment. Its default (`swarm-controller`) contains `swarm` and is caught
|
||||
# by the substring guard whatever the cert-auth arm does — so a fixture using
|
||||
# the default could not tell the two apart, and the arm under test would pass
|
||||
# on the neighbour's work.
|
||||
hiveNamedAfterCertSubject = hive {
|
||||
deploy.swarm-otel.enable = false;
|
||||
deploy.bao.controllerCommonName = "ctl";
|
||||
swarm.hives.ctl.domain = "ctl.t.local";
|
||||
};
|
||||
|
||||
# The control for both arms below: same shape, a roster nothing objects to.
|
||||
hiveNamesAllLegal = hive {
|
||||
deploy.swarm-otel.enable = false;
|
||||
deploy.bao.controllerCommonName = "ctl";
|
||||
};
|
||||
|
||||
# The reserved subjects are a LIST, and a list with one consulted element and
|
||||
# one dead one looks identical from the first element's case. This fixture
|
||||
# collides with the SECOND, leaving the controller's at its default.
|
||||
hiveNamedAfterPublisherSubject = hive {
|
||||
deploy.swarm-otel.enable = false;
|
||||
deploy.bao.secretPublisherCommonName = "pubctl";
|
||||
swarm.hives.pubctl.domain = "p.t.local";
|
||||
};
|
||||
|
||||
hiveNameWithComposedWord = hive {
|
||||
deploy.swarm-otel.enable = false;
|
||||
swarm.hives."h1-agent".domain = "a.t.local";
|
||||
};
|
||||
|
||||
# Markers from `lib/name-guards.nix`'s two `problem` strings. Matching the
|
||||
# problem rather than the `why` prose keeps the messages rewordable.
|
||||
equalityGuardFired =
|
||||
h: lib.any (a: !a.assertion && lib.hasInfix "has reserved name(s)" a.message) h.assertions;
|
||||
|
||||
fragmentGuardFired =
|
||||
h:
|
||||
lib.any (
|
||||
a: !a.assertion && lib.hasInfix "has name(s) containing a reserved word" a.message
|
||||
) h.assertions;
|
||||
cases = [
|
||||
{
|
||||
# `ctl` is in no deny list — it is reserved *because it is the subject a
|
||||
# cert-auth role accepts*, which is a value an operator sets, so a
|
||||
# literal deny entry could never have covered it.
|
||||
name = "a hive named after a cert-auth subject is refused, with the collector off";
|
||||
ok =
|
||||
equalityGuardFired hiveNamedAfterCertSubject
|
||||
&& lib.any (a: !a.assertion && lib.hasInfix "'ctl'" a.message) hiveNamedAfterCertSubject.assertions;
|
||||
}
|
||||
{
|
||||
# Every cert-auth subject is reserved, not just the first one in the
|
||||
# list. Without this case the second element could be dead and the case
|
||||
# above would still pass.
|
||||
name = "a hive named after the secret publisher's subject is refused too";
|
||||
ok =
|
||||
equalityGuardFired hiveNamedAfterPublisherSubject
|
||||
&& lib.any (
|
||||
a: !a.assertion && lib.hasInfix "'pubctl'" a.message
|
||||
) hiveNamedAfterPublisherSubject.assertions;
|
||||
}
|
||||
{
|
||||
# Without this the case above proves nothing: an arm that fires for every
|
||||
# roster is not a guard, and `hives` is non-empty in both fixtures.
|
||||
name = "a legal hive roster trips neither name guard";
|
||||
ok = !(equalityGuardFired hiveNamesAllLegal) && !(fragmentGuardFired hiveNamesAllLegal);
|
||||
}
|
||||
{
|
||||
# The substring guard came along in the move and has to still work.
|
||||
# `h1-agent` mints exactly the client id hive `h1`'s agents present.
|
||||
name = "a hive name containing a composed-identifier word is refused, with the collector off";
|
||||
ok = fragmentGuardFired hiveNameWithComposedWord;
|
||||
}
|
||||
{
|
||||
# ⚠️ The control that makes "with the collector off" mean anything. If a
|
||||
# fixture silently had swarm-otel enabled, all three cases above would
|
||||
# pass while testing the arrangement they exist to rule out.
|
||||
name = "the guard fixtures really do have the collector disabled";
|
||||
ok =
|
||||
!hiveNamedAfterCertSubject.services.hyperhive.deploy.swarm-otel.enable
|
||||
&& !hiveNamesAllLegal.services.hyperhive.deploy.swarm-otel.enable
|
||||
&& !hiveNameWithComposedWord.services.hyperhive.deploy.swarm-otel.enable;
|
||||
}
|
||||
];
|
||||
in
|
||||
runGroup "name-guards" cases
|
||||
Loading…
Reference in a new issue