diff --git a/nix/checks.nix b/nix/checks.nix index 8c47b8ca..bc04772b 100644 --- a/nix/checks.nix +++ b/nix/checks.nix @@ -12,6 +12,13 @@ }: let inherit (rust) cleanSrc cargoArtifacts nativeBuildInputs; + + # Bound here, once, so every cluster handed it reads the same evaluation of + # each fixture; see ./module-eval/shared.nix. + moduleEvalShared = import ./module-eval/shared.nix { + inherit pkgs self nixosSystem; + inherit (pkgs) lib; + }; in { formatting = treefmt-eval.config.build.check self; @@ -30,14 +37,17 @@ in module-eval-core-toggle = import ./module-eval/core-toggle.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-swarm-services-switch = import ./module-eval/swarm-services-switch.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-nats-authelia = import ./module-eval/nats-authelia.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; # Executes the queue server's config render, which `module-eval-nats-authelia` # only sees as a store path: the callout xkey it writes is a build output. @@ -48,58 +58,72 @@ in module-eval-nats-tls = import ./module-eval/nats-tls.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-hive-tls = import ./module-eval/hive-tls.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-matrix-core = import ./module-eval/matrix-core.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-grafana = import ./module-eval/grafana.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-forge-placement = import ./module-eval/forge-placement.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-bao-basics = import ./module-eval/bao-basics.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-bao-matrix-reader = import ./module-eval/bao-matrix-reader.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-bao-grants = import ./module-eval/bao-grants.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-bao-controller = import ./module-eval/bao-controller.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-secret-publisher = import ./module-eval/secret-publisher.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-bao-otel-collector = import ./module-eval/bao-otel-collector.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-swarm-otel-core = import ./module-eval/swarm-otel-core.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-swarm-otel-identity = import ./module-eval/swarm-otel-identity.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-hive-otel = import ./module-eval/hive-otel.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-name-guards = import ./module-eval/name-guards.nix { inherit pkgs self nixosSystem; @@ -108,6 +132,7 @@ in module-eval-agent-otel = import ./module-eval/agent-otel.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; # Not a subsystem cluster like its siblings but a single shared file, and # its own derivation for that reason: both journald receivers import one @@ -121,6 +146,7 @@ in module-eval-agent-queue-bao = import ./module-eval/agent-queue-bao.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-agent-forge-bao = import ./module-eval/agent-forge-bao.nix { inherit pkgs self nixosSystem; @@ -147,14 +173,17 @@ in module-eval-agent-github-bao = import ./module-eval/agent-github-bao.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-agent-memory = import ./module-eval/agent-memory.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-agent-plugins = import ./module-eval/agent-plugins.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; module-eval-agent-runtime = import ./module-eval/agent-runtime.nix { inherit pkgs self nixosSystem; @@ -171,6 +200,7 @@ in module-eval-agent-matrix = import ./module-eval/agent-matrix.nix { inherit pkgs self nixosSystem; inherit (pkgs) lib; + shared = moduleEvalShared; }; # Clippy via crane's first-class `cargoClippy` builder. Reuses the diff --git a/nix/module-eval/agent-github-bao.nix b/nix/module-eval/agent-github-bao.nix index 6b787911..0f7af1e3 100644 --- a/nix/module-eval/agent-github-bao.nix +++ b/nix/module-eval/agent-github-bao.nix @@ -9,6 +9,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -27,12 +28,10 @@ let baoAddr = "https://bao.t.local:8200"; # A store, and the integration on by default. - agentGithubBao = agentWith { - services.hyperhive.agent.bao.addr = baoAddr; - }; + agentGithubBao = shared.agentWithBao; # No store: the absence arm, and what makes the cases above able to fail. - agentGithubNoBao = agentWith { }; + agentGithubNoBao = shared.agentBare; # A store, and the integration switched off. agentGithubOff = agentWith { diff --git a/nix/module-eval/agent-matrix.nix b/nix/module-eval/agent-matrix.nix index 5366637e..ec673f92 100644 --- a/nix/module-eval/agent-matrix.nix +++ b/nix/module-eval/agent-matrix.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -37,7 +38,7 @@ let # matrix on with no `main` at all. agentMatrix = agent { matrix.url = "https://chat.t.local"; }; - agentNoMatrix = agent { }; + agentNoMatrix = shared.agentBare; agentMatrixExternalOnly = agent { matrixAccounts.ccc = { diff --git a/nix/module-eval/agent-memory.nix b/nix/module-eval/agent-memory.nix index 639309c5..92be5450 100644 --- a/nix/module-eval/agent-memory.nix +++ b/nix/module-eval/agent-memory.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -17,12 +18,11 @@ let nixosSystem ; }) - agent runGroup agentHarness ; - agentDefault = agent { }; + agentDefault = shared.agentBare; agentBashDaemon = machine: machine.systemd.services.hive-bash-daemon; agentSubagentDaemon = machine: machine.systemd.services.hive-subagent-daemon; diff --git a/nix/module-eval/agent-otel.nix b/nix/module-eval/agent-otel.nix index 8c596e1d..ad7df33b 100644 --- a/nix/module-eval/agent-otel.nix +++ b/nix/module-eval/agent-otel.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -44,7 +45,7 @@ let otel.protocol = "grpc"; }; - agentNoOtel = agent { }; + agentNoOtel = shared.agentBare; agentSettings = machine: machine.services.opentelemetry-collector.settings; cases = [ diff --git a/nix/module-eval/agent-plugins.nix b/nix/module-eval/agent-plugins.nix index f71e13b4..b4e332d7 100644 --- a/nix/module-eval/agent-plugins.nix +++ b/nix/module-eval/agent-plugins.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -27,7 +28,7 @@ let # outright — the two arms below plus the unset default (read directly off # `bare`-shaped `agent { }`, no fixture of its own needed) are the three # cases that shape has to hold. - agentPluginsDefault = agent { }; + agentPluginsDefault = shared.agentBare; agentPluginsAdded = agent { claudePlugins = [ "foo@bar" ]; }; diff --git a/nix/module-eval/agent-queue-bao.nix b/nix/module-eval/agent-queue-bao.nix index 85f11f42..34acf91c 100644 --- a/nix/module-eval/agent-queue-bao.nix +++ b/nix/module-eval/agent-queue-bao.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -18,7 +19,6 @@ let ; }) agent - agentWith runGroup agentHarness ; @@ -31,14 +31,14 @@ let queue.tokenEndpoint = "https://auth.t.local/api/oidc/token"; }; - agentNoQueue = agent { }; + agentNoQueue = shared.agentBare; # The agent side of the swarm secret store. The address is the whole switch — # it is both what generates the login check and what that check points at — # so it and the empty fixture beside it are the two arms worth having. - agentBao = agentWith { services.hyperhive.agent.bao.addr = "https://bao.t.local:8200"; }; + agentBao = shared.agentWithBao; - agentNoBao = agentWith { }; + agentNoBao = shared.agentBare; agentBaoIdentity = machine: machine.systemd.services.hive-agent-bao-identity; cases = [ diff --git a/nix/module-eval/bao-basics.nix b/nix/module-eval/bao-basics.nix index d8ec368d..a1d74de4 100644 --- a/nix/module-eval/bao-basics.nix +++ b/nix/module-eval/bao-basics.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -41,7 +42,7 @@ let deploy.bao.serverKeyFile = "/etc/pki/bao-key.pem"; }; - autheliaOnly = hive { deploy.authelia.enable = true; }; + inherit (shared) autheliaOnly; baoWithForge = hive { deploy.bao.enable = true; diff --git a/nix/module-eval/bao-controller.nix b/nix/module-eval/bao-controller.nix index 0654ecae..04d21ca3 100644 --- a/nix/module-eval/bao-controller.nix +++ b/nix/module-eval/bao-controller.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -43,7 +44,7 @@ let # The controller with no store, which is every spread deployment. Nothing # mints here, so the pairing must leave the paths unset rather than name # files this host will never have. - controllerNoStore = hive { deploy.swarm-controller.enable = true; }; + controllerNoStore = shared.controllerAlone; # A deployment that pinned `gatewayHost` away from its own swarm-domain # default (`hive-matrix-service.nix`'s own doc: "To use the per-hive name diff --git a/nix/module-eval/bao-grants.nix b/nix/module-eval/bao-grants.nix index 4532e08f..793c8a79 100644 --- a/nix/module-eval/bao-grants.nix +++ b/nix/module-eval/bao-grants.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -39,9 +40,7 @@ let # The store with no bootstrap token: the steady state once the granter is set # up, and the state of a store host that has never named one. - baoGranterNoToken = hive { - deploy.bao.enable = true; - }; + baoGranterNoToken = shared.baoOnly; # The store with the granter's pair taken away: the deployment that writes # its grants some other way. diff --git a/nix/module-eval/bao-matrix-reader.nix b/nix/module-eval/bao-matrix-reader.nix index 051c775e..6b505092 100644 --- a/nix/module-eval/bao-matrix-reader.nix +++ b/nix/module-eval/bao-matrix-reader.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -24,10 +25,7 @@ let # The store and a service that reads from it, versus the store alone. The # pair is what makes the reader's absence arm mean anything. - baoWithMatrix = hiveWithController { - deploy.bao.enable = true; - deploy.matrix.enable = true; - }; + inherit (shared) baoWithMatrix; # A hive that reads from a store it does not run: no `deploy.bao.enable`, so # nothing here mints a leaf and the operator names one placed by hand. The @@ -51,7 +49,7 @@ let # store nor a hand-placed leaf. The absence arm for the matrix-ctl cases below # needs it, and defining it here rather than importing keeps each group's # fixture set its own, as ./lib.nix asks. - matrixNoBaoIdentity = hiveWithController { deploy.matrix.enable = true; }; + inherit (shared) matrixNoBaoIdentity; # 🩸 The hand-configured remote reader that named the hive's leaf and stopped. # `baoRemoteReader` above is the same deployment done right; this one holds diff --git a/nix/module-eval/bao-otel-collector.nix b/nix/module-eval/bao-otel-collector.nix index f94a1c8a..d255a884 100644 --- a/nix/module-eval/bao-otel-collector.nix +++ b/nix/module-eval/bao-otel-collector.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -36,14 +37,7 @@ let # The store with and without a collector on the same host. `scrapeTargets` # is only ever read by a local collector, so the metrics endpoint is a # function of the pairing rather than of the store. - baoWithCollector = hive { - deploy.bao.enable = true; - deploy.swarm-otel.enable = true; - # A second job declared as bare `host:port`, so the pair of cases below - # reads one rendered scrape list: the store's entry carries a path, this - # one carries none. - swarm.otel.scrapeTargets.plain = "127.0.0.1:9999"; - }; + inherit (shared) baoWithCollector; # The SPLIT topology, and the reason it is not called `baoNoCollector` any # more: a swarm always runs every swarm-level service somewhere, so this host @@ -51,21 +45,12 @@ let # host, never that the swarm has none. Both of those read the same locally, # and only one of them is a real deployment — which is what the two gates # this PR removed each mistook for "there is nothing to forward to". - baoOtelElsewhere = hive { - deploy.bao.enable = true; - deploy.swarm-otel.enable = false; - }; + baoOtelElsewhere = shared.baoOnly; # The same split, plus a HIVE collector on the store's host — the pairing the # first gate read as permission to forward. It is a third collector with a # different job, so it changes neither whether the store forwards nor where. - # `clientSecretFile` is what ../host-modules/otel.nix's identity assertion - # demands of any hive with the tier on. - baoWithHiveOtel = hive { - deploy.bao.enable = true; - otel.enable = true; - otel.clientSecretFile = "/var/lib/hive-otel-oidc/client.secret"; - }; + inherit (shared) baoWithHiveOtel; # The forwarder INSIDE the store's container, not the host's collector and # not the swarm tier's — three collectors in this tree, and only this one can @@ -444,7 +429,7 @@ let name = "the forwarder's client is registered wherever authelia runs"; ok = let - autheliaOnly = hive { deploy.authelia.enable = true; }; + inherit (shared) autheliaOnly; clients = autheliaOnly.services.hyperhive.swarm.authelia.oidc.clients; c = lib.findFirst (c: c.id == "swarm-bao-collector") null clients; in diff --git a/nix/module-eval/core-toggle.nix b/nix/module-eval/core-toggle.nix index 605897c9..1eb84e82 100644 --- a/nix/module-eval/core-toggle.nix +++ b/nix/module-eval/core-toggle.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -27,9 +28,9 @@ let swarmServiceEnables ; - allLocal = hiveWithController { deploy.singleHostSwarm = true; }; + inherit (shared) allLocal; - bare = hiveWithController { }; + bare = shared.bareWithController; # The same stub with the central toggle (`deploy.hive-controller.enable`) # off. Paired with `bare` below to pin the defaults that do not read it: @@ -41,7 +42,9 @@ let # nothing on" host the swarm-service absences below read: none of the # per-service deployment toggles derives from the hive being on, so it # renders the same absences `bare` does. - centralToggleOff = hive { deploy.hive-controller.enable = false; }; + centralToggleOff = shared.bare; + + inherit (shared) forgeHere; # Every hive defaults the agents' queue address to the queue's name, so the # only hive without one is a hive told to have none. @@ -123,12 +126,7 @@ let # (./forge-placement.nix), never the central toggle: a forge host with # the toggle off still publishes it. name = "the forge's controller-token publisher does not consult the central toggle"; - ok = - (hive { - deploy.hive-controller.enable = false; - deploy.forgejo.enable = true; - }).systemd.services - ? swarm-bao-forge-publisher; + ok = forgeHere.systemd.services ? swarm-bao-forge-publisher; } { name = "a hive that does not host the swarm's shared services runs none of them"; @@ -233,7 +231,6 @@ let name = "a gateway's services leaf asks for only the swarm names that host fronts"; ok = let - forgeHere = hive { deploy.forgejo.enable = true; }; s = forgeHere.services.hyperhive.swarm.serviceDomains; u = forgeHere.systemd.services.swarm-services-cert.script; in @@ -251,11 +248,7 @@ let let s = h: h.services.hyperhive.swarm.serviceDomains; in - s (hive { - deploy.forgejo.enable = true; - }) == s bare - && s allLocal == s bare - && lib.elem "grafana.t.local" (s bare); + s forgeHere == s bare && s allLocal == s bare && lib.elem "grafana.t.local" (s bare); } { # The narrowing's floor: no names left means no request, rather than a diff --git a/nix/module-eval/forge-placement.nix b/nix/module-eval/forge-placement.nix index b958bf4d..12ee4e1f 100644 --- a/nix/module-eval/forge-placement.nix +++ b/nix/module-eval/forge-placement.nix @@ -11,6 +11,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -26,10 +27,9 @@ let runGroup ; - bare = hive { }; + inherit (shared) bare; allLocal = hive { deploy.singleHostSwarm = true; }; - servicesHere = hive { deploy.allSwarmServices = true; }; - forgeHere = hive { deploy.forgejo.enable = true; }; + inherit (shared) servicesHere forgeHere; # The shared services here, the forge somewhere else. Every derivation in # ../host-modules/swarm-required-services.nix is `mkDefault`, so this stays diff --git a/nix/module-eval/grafana.nix b/nix/module-eval/grafana.nix index 33f20ca3..5de0206e 100644 --- a/nix/module-eval/grafana.nix +++ b/nix/module-eval/grafana.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -247,7 +248,7 @@ let name = "hive identities and the token endpoint do not depend on which host runs what"; ok = let - autheliaNoQueue = hive { deploy.authelia.enable = true; }; + autheliaNoQueue = shared.autheliaOnly; in lib.elem "hive-h1" (map (c: c.id) autheliaNoQueue.services.hyperhive.swarm.authelia.oidc.clients) && diff --git a/nix/module-eval/hive-otel.nix b/nix/module-eval/hive-otel.nix index 93554ef0..773eaaa6 100644 --- a/nix/module-eval/hive-otel.nix +++ b/nix/module-eval/hive-otel.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -59,31 +60,16 @@ let lib.findFirst ( c: c.job_name == job ) null hiveOtelSettings.receivers.prometheus.config.scrape_configs; - # Duplicated from bao-otel-collector.nix — a case here needs it too. # The store with and without a collector on the same host. `scrapeTargets` # is only ever read by a local collector, so the metrics endpoint is a # function of the pairing rather than of the store. - baoWithCollector = hive { - deploy.bao.enable = true; - deploy.swarm-otel.enable = true; - # A second job declared as bare `host:port`, so the pair of cases below - # reads one rendered scrape list: the store's entry carries a path, this - # one carries none. - swarm.otel.scrapeTargets.plain = "127.0.0.1:9999"; - }; + inherit (shared) baoWithCollector; - # Also duplicated from bao-otel-collector.nix, where it is the fixture for - # the store standing beside a HIVE collector. That co-location is exactly - # the topology the port case below needs: two collectors, one netns, and - # nothing but their self-telemetry ports keeping them apart. - # `clientSecretFile` is what ../host-modules/otel.nix's identity assertion - # demands of any hive with the tier on. - baoWithHiveOtel = hive { - deploy.bao.enable = true; - otel.enable = true; - otel.clientSecretFile = "/var/lib/hive-otel-oidc/client.secret"; - }; + # The store standing beside a HIVE collector: the topology the port case + # below needs, two collectors in one netns with nothing but their + # self-telemetry ports keeping them apart. + inherit (shared) baoWithHiveOtel; # The collector INSIDE the store's container — neither the host's nor the # swarm tier's. diff --git a/nix/module-eval/hive-tls.nix b/nix/module-eval/hive-tls.nix index 686b7709..cbb2d5c6 100644 --- a/nix/module-eval/hive-tls.nix +++ b/nix/module-eval/hive-tls.nix @@ -11,6 +11,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -29,9 +30,7 @@ let # Every service on one host, so the store's granting unit renders the role # this leaf is issued through. - allLocal = hiveWithController { - deploy.singleHostSwarm = true; - }; + inherit (shared) allLocal; # The same host with the role's lifetime moved, so a threshold that is a # number of its own shows up as one that did not move with it. @@ -66,7 +65,7 @@ let noPairFetch = noServiceNoPair.systemd.services.swarm-services-cert.script; # The same hive with no store identity at all. - noIdentity = hiveWithController { }; + noIdentity = shared.bareWithController; noIdFetch = noIdentity.systemd.services.swarm-services-cert.script; # A gateway on a host with no hive and no store, fronting no swarm name, diff --git a/nix/module-eval/matrix-core.nix b/nix/module-eval/matrix-core.nix index 348bc525..ee79531f 100644 --- a/nix/module-eval/matrix-core.nix +++ b/nix/module-eval/matrix-core.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -18,7 +19,6 @@ let ; }) hive - hiveWithController runGroup ; @@ -31,11 +31,11 @@ let # The same hive with the identity taken away, which separates "a homeserver # is deployed" from "this host can authenticate to the store". - matrixNoBaoIdentity = hiveWithController { deploy.matrix.enable = true; }; + inherit (shared) matrixNoBaoIdentity; # A hive host that does not run the homeserver: its hive-c0re reaches the # homeserver across the network. - matrixElsewhere = hiveWithController { }; + matrixElsewhere = shared.bareWithController; cases = [ { # Absence arm, and what makes the one above able to fail: with no leaf diff --git a/nix/module-eval/nats-authelia.nix b/nix/module-eval/nats-authelia.nix index cc9783f7..df227e53 100644 --- a/nix/module-eval/nats-authelia.nix +++ b/nix/module-eval/nats-authelia.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -93,9 +94,9 @@ let # `swarm.authelia.url` set by hand. The whole point of the fixture is what it # does *not* say: no IdP address is given and none is derived from # co-location. - autheliaNotColocated = hive { }; + autheliaNotColocated = shared.bare; # The same swarm's IdP host, for the arm that the two agree. - autheliaColocated = hive { deploy.authelia.enable = true; }; + autheliaColocated = shared.autheliaOnly; cases = [ { diff --git a/nix/module-eval/nats-tls.nix b/nix/module-eval/nats-tls.nix index 30ee858b..c0301447 100644 --- a/nix/module-eval/nats-tls.nix +++ b/nix/module-eval/nats-tls.nix @@ -7,6 +7,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -29,9 +30,7 @@ let # Every service on one host. The queue, the store and every in-tree client of the queue # are all here, so the scan below reads each of them. - allLocal = hiveWithController { - deploy.singleHostSwarm = true; - }; + inherit (shared) allLocal; # The same host on the mesh. allLocalMesh = hive { diff --git a/nix/module-eval/secret-publisher.nix b/nix/module-eval/secret-publisher.nix index b661cabd..fa03067c 100644 --- a/nix/module-eval/secret-publisher.nix +++ b/nix/module-eval/secret-publisher.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -18,7 +19,6 @@ let ; }) hive - hiveWithController runGroup ; @@ -46,7 +46,7 @@ let # The same IdP with the identity taken away. Minting the secrets is not being # able to publish them, and this is the arm that separates the two. - secretPublisherNoIdentity = hive { deploy.authelia.enable = true; }; + secretPublisherNoIdentity = shared.autheliaOnly; # Duplicated from grafana.nix — a case here needs it too. # The same UI with the IdP on ANOTHER host and a store leaf placed by hand. @@ -61,28 +61,17 @@ let deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; }; - # Duplicated from swarm-otel-core.nix — a case here needs it too. # The same collector with the IdP on ANOTHER host and a store leaf placed by # hand. Identical to the fixture above in everything the delivery path # reads, which is the point. - otelBaoRemoteAuthelia = hive { - deploy.swarm-otel.enable = true; - swarm.authelia.url = "https://auth.example.invalid"; - deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; - deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; - }; - # Duplicated from core-toggle.nix — a case here needs it too. + otelBaoRemoteAuthelia = shared.otelReaderMissingLeaf; - bare = hive { }; - # Duplicated from bao-matrix-reader.nix — a case here needs it too. + inherit (shared) bare; # The store and a service that reads from it, versus the store alone. The # pair is what makes the reader's absence arm mean anything. - baoWithMatrix = hiveWithController { - deploy.bao.enable = true; - deploy.matrix.enable = true; - }; + inherit (shared) baoWithMatrix; cases = [ { # Each hive's hive-c0re reads this exact path diff --git a/nix/module-eval/shared.nix b/nix/module-eval/shared.nix new file mode 100644 index 00000000..428b92c0 --- /dev/null +++ b/nix/module-eval/shared.nix @@ -0,0 +1,96 @@ +# The fixtures more than one `checks.module-eval-*` cluster reads, each +# evaluated once. ../checks.nix imports this file once and hands the result to +# every cluster that uses one; a cluster rebinds the fixture under its own +# local name. +# +# ⚠️ Sharing only pays when ../checks.nix instantiates this file ONCE: a +# cluster that ran `import ./shared.nix` itself would get its own copy of +# every fixture, the same as defining them locally. The cost is that each +# fixture here stays live for the whole evaluation rather than one cluster's. +# +# Holds only configs that are identical across their users. A cluster whose +# claim needs one more option set defines its own fixture. +{ + pkgs, + lib, + self, + nixosSystem, +}: +let + inherit + (import ./lib.nix { + inherit + pkgs + lib + self + nixosSystem + ; + }) + hive + hiveWithController + agent + agentWith + ; +in +{ + agentBare = agent { }; + + agentWithBao = agentWith { services.hyperhive.agent.bao.addr = "https://bao.t.local:8200"; }; + + bare = hive { }; + + bareWithController = hiveWithController { }; + + allLocal = hiveWithController { deploy.singleHostSwarm = true; }; + + autheliaOnly = hive { deploy.authelia.enable = true; }; + + forgeHere = hive { deploy.forgejo.enable = true; }; + + baoOnly = hive { deploy.bao.enable = true; }; + + servicesHere = hive { deploy.allSwarmServices = true; }; + + controllerAlone = hive { deploy.swarm-controller.enable = true; }; + + matrixNoBaoIdentity = hiveWithController { deploy.matrix.enable = true; }; + + baoWithMatrix = hiveWithController { + deploy.bao.enable = true; + deploy.matrix.enable = true; + }; + + # A second job declared as bare `host:port`, so a case can read one rendered + # scrape list where the store's entry carries a path and this one carries + # none. + baoWithCollector = hive { + deploy.bao.enable = true; + deploy.swarm-otel.enable = true; + swarm.otel.scrapeTargets.plain = "127.0.0.1:9999"; + }; + + # `clientSecretFile` is what ../host-modules/otel.nix's identity assertion + # demands of any hive with the tier on. + baoWithHiveOtel = hive { + deploy.bao.enable = true; + otel.enable = true; + otel.clientSecretFile = "/var/lib/hive-otel-oidc/client.secret"; + }; + + # The swarm collector with the IdP on another host and the store leaf placed + # by hand, without and with the collector's own OIDC pair. + otelReaderMissingLeaf = hive { + deploy.swarm-otel.enable = true; + swarm.authelia.url = "https://auth.example.invalid"; + deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; + deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; + }; + otelReaderNamedInFull = hive { + deploy.swarm-otel.enable = true; + swarm.authelia.url = "https://auth.example.invalid"; + deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; + deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; + deploy.bao.otelOidcClientCertFile = "/etc/pki/bao-otel-oidc.pem"; + deploy.bao.otelOidcClientKeyFile = "/etc/pki/bao-otel-oidc-key.pem"; + }; +} diff --git a/nix/module-eval/swarm-otel-core.nix b/nix/module-eval/swarm-otel-core.nix index 1d18b372..4c4c6194 100644 --- a/nix/module-eval/swarm-otel-core.nix +++ b/nix/module-eval/swarm-otel-core.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -53,14 +54,7 @@ let # The same collector with the IdP on ANOTHER host and a store leaf placed by # hand. Identical to the fixture above in everything the delivery path # reads, which is the point. - otelBaoRemoteAuthelia = hive { - deploy.swarm-otel.enable = true; - swarm.authelia.url = "https://auth.example.invalid"; - deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; - deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; - deploy.bao.otelOidcClientCertFile = "/etc/pki/bao-otel-oidc.pem"; - deploy.bao.otelOidcClientKeyFile = "/etc/pki/bao-otel-oidc-key.pem"; - }; + otelBaoRemoteAuthelia = shared.otelReaderNamedInFull; cases = [ { diff --git a/nix/module-eval/swarm-otel-identity.nix b/nix/module-eval/swarm-otel-identity.nix index 99dcd677..58b34d1c 100644 --- a/nix/module-eval/swarm-otel-identity.nix +++ b/nix/module-eval/swarm-otel-identity.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -41,24 +42,12 @@ let # regression rather than the supported degrade `otelNoIdentity` stands for. The # two fixtures differ in exactly that one pair, which is what lets the arms # below separate a refusal from a degrade. - otelReaderMissingLeaf = hive { - deploy.swarm-otel.enable = true; - swarm.authelia.url = "https://auth.example.invalid"; - deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; - deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; - }; + inherit (shared) otelReaderMissingLeaf; # The same deployment named in full, which must stay buildable. Pairs with the # fixture above so the refusal is pinned to the omission and not to reading a # remote store at all. - otelReaderNamedInFull = hive { - deploy.swarm-otel.enable = true; - swarm.authelia.url = "https://auth.example.invalid"; - deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; - deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; - deploy.bao.otelOidcClientCertFile = "/etc/pki/bao-otel-oidc.pem"; - deploy.bao.otelOidcClientKeyFile = "/etc/pki/bao-otel-oidc-key.pem"; - }; + inherit (shared) otelReaderNamedInFull; # Did the module refuse this host, and over which option. An assertion is a # config VALUE until something forces it — `.config` never throws — so a diff --git a/nix/module-eval/swarm-services-switch.nix b/nix/module-eval/swarm-services-switch.nix index 46a3b015..1b9d9b21 100644 --- a/nix/module-eval/swarm-services-switch.nix +++ b/nix/module-eval/swarm-services-switch.nix @@ -6,6 +6,7 @@ lib, self, nixosSystem, + shared, }: let inherit @@ -26,25 +27,15 @@ let # A dedicated services host with hives elsewhere: the swarm-services # toggle and no hive, the recipe docs/swarm/services.md gives. - swarmServicesOnly = hive { - deploy.hive-controller.enable = false; - deploy.allSwarmServices = true; - }; + swarmServicesOnly = shared.servicesHere; # A services host with hives elsewhere and the store elsewhere too: no hive # and no store identity minted here. - swarmServicesOnlyNoStore = hive { - deploy.hive-controller.enable = false; - deploy.allSwarmServices = true; - deploy.bao.enable = false; - }; + swarmServicesOnlyNoStore = swarmServicesBaoElsewhere; # A dedicated secret-store host: no hive, and the store's browser UI the # only swarm name its gateway fronts. - storeOnlyNoHive = hive { - deploy.hive-controller.enable = false; - deploy.bao.enable = true; - }; + storeOnlyNoHive = shared.baoOnly; # The homeserver on a host without a hive. matrixNoHive = hive { @@ -123,7 +114,7 @@ let deploy.forgejo.enable = true; deploy.forgejo.mirrors = [ aMirror ]; }; - forgeOnly = hive { deploy.forgejo.enable = true; }; + forgeOnly = shared.forgeHere; forgeWithController = hive { deploy.swarm-controller.enable = true; deploy.forgejo.enable = true; @@ -138,7 +129,7 @@ let # The controller with every shared service on another host, and the same # controller hosting them all: the swarm UI's links must not tell the two # apart. - controllerAlone = hive { deploy.swarm-controller.enable = true; }; + inherit (shared) controllerAlone; controllerWithServices = hive { deploy.swarm-controller.enable = true; deploy.allSwarmServices = true;