From ea63b0d1504fa32b32791f76215d28b1f36a9bea Mon Sep 17 00:00:00 2001 From: atlas Date: Mon, 5 Oct 2026 02:27:33 +0200 Subject: [PATCH] module-eval: set the hive-controller option only in tests that need a hive The shared `hive` fixture turned `deploy.hive-controller.enable` on for every eval test. It is now off by default; `hiveWithController` turns it on, and the fixtures whose claims read the hive tier (hive-c0re, the queue-agent credential, the hive leaf, `deploy.hive-controller.*`, or an absence that is only meaningful with a hive present) use it. glue-nats-auth-bao-identity.nix still read the renamed `services.hyperhive.enable`, which printed the "Obsolete option" trace six times per CI run. The term is dropped: the pairing is gated on the queue and the minted PKI, as its sibling glue-nats-bao-identity.nix is. --- nix/host-modules/glue-nats-auth-bao-identity.nix | 2 +- nix/module-eval/bao-grants.nix | 9 +++++---- nix/module-eval/bao-matrix-reader.nix | 11 ++++++----- nix/module-eval/core-toggle.nix | 11 ++++++----- nix/module-eval/hive-tls.nix | 10 +++++----- nix/module-eval/lib.nix | 11 +++++++++-- nix/module-eval/matrix-core.nix | 5 +++-- nix/module-eval/nats-tls.nix | 9 ++++++--- nix/module-eval/secret-publisher.nix | 3 ++- nix/module-eval/swarm-services-switch.nix | 7 ++++--- 10 files changed, 47 insertions(+), 31 deletions(-) diff --git a/nix/host-modules/glue-nats-auth-bao-identity.nix b/nix/host-modules/glue-nats-auth-bao-identity.nix index 7c6cd7cb..1f4c708b 100644 --- a/nix/host-modules/glue-nats-auth-bao-identity.nix +++ b/nix/host-modules/glue-nats-auth-bao-identity.nix @@ -28,7 +28,7 @@ let pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null; in { - config = lib.mkIf (hyperhiveCfg.enable && deployCfg.nats.enable && haveMintedPki) { + config = lib.mkIf (deployCfg.nats.enable && haveMintedPki) { services.hyperhive.deploy.nats = { authBaoClientCertFile = lib.mkDefault "${pkiDir}/nats-auth.pem"; authBaoClientKeyFile = lib.mkDefault "${pkiDir}/nats-auth-key.pem"; diff --git a/nix/module-eval/bao-grants.nix b/nix/module-eval/bao-grants.nix index 984d9384..7dff3929 100644 --- a/nix/module-eval/bao-grants.nix +++ b/nix/module-eval/bao-grants.nix @@ -18,12 +18,13 @@ let ; }) hive + hiveWithController runGroup ; # The store, plus a placed bootstrap token: the only shape in which the # granter's own role can be written at all. - baoGrantHere = hive { + baoGrantHere = hiveWithController { deploy.bao.enable = true; deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token"; }; @@ -91,7 +92,7 @@ let # rather than one per reader: the claim they are *separate* principals is # only testable where all of them render at once — that is the deployment in # which two of them sharing a leaf would be invisible. - baoGrantWithConsumers = hive { + baoGrantWithConsumers = hiveWithController { deploy.bao.enable = true; deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token"; deploy.grafana.enable = true; @@ -109,7 +110,7 @@ let # The store with none of the readers beside it. Grafana and the collector are # simply off; the queue reader renders on any host holding # its leaf, which ./glue-bao-tls.nix mints here, so that leaf is taken away. - baoGrantNoReaders = hive { + baoGrantNoReaders = hiveWithController { deploy.bao.enable = true; deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token"; deploy.bao.queueAgentClientCertFile = lib.mkForce null; @@ -126,7 +127,7 @@ let # Every reader against a store it does not run, each with a leaf placed by # hand. The deployment in which there is no local policy unit to wait for. - baoRemoteReaders = hive { + baoRemoteReaders = hiveWithController { deploy.grafana.enable = true; deploy.swarm-otel.enable = true; deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; diff --git a/nix/module-eval/bao-matrix-reader.nix b/nix/module-eval/bao-matrix-reader.nix index 0abf90b1..32f3885d 100644 --- a/nix/module-eval/bao-matrix-reader.nix +++ b/nix/module-eval/bao-matrix-reader.nix @@ -18,12 +18,13 @@ let ; }) hive + hiveWithController runGroup ; # 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 = hive { + baoWithMatrix = hiveWithController { deploy.bao.enable = true; deploy.matrix.enable = true; }; @@ -38,7 +39,7 @@ let # hive, so an operator placing leaves by hand places one for it as well as # the hive's own. Naming only `clientCertFile` here would leave the unit # unrendered — which is what the arms below would then be asserting about. - baoRemoteReader = hive { + baoRemoteReader = hiveWithController { deploy.matrix.enable = true; deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; @@ -50,14 +51,14 @@ 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 = hive { deploy.matrix.enable = true; }; + matrixNoBaoIdentity = hiveWithController { deploy.matrix.enable = true; }; # 🩸 The hand-configured remote reader that named the hive's leaf and stopped. # `baoRemoteReader` above is the same deployment done right; this one holds # the hive's own leaf, so it demonstrably reads the store, and is missing the # queue reader's pair. Without the refusal it would render no queue reader # and say nothing — which is what the refusal arms below are about. - baoRemoteReaderMissingLeaves = hive { + baoRemoteReaderMissingLeaves = hiveWithController { deploy.matrix.enable = true; deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; @@ -65,7 +66,7 @@ let # The same omission on a hive that does NOT run a homeserver: the queue # refusal checks only the hive's own toggle. - remoteReaderNoMatrixMissingLeaves = hive { + remoteReaderNoMatrixMissingLeaves = hiveWithController { deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; }; diff --git a/nix/module-eval/core-toggle.nix b/nix/module-eval/core-toggle.nix index d572e151..e7e57456 100644 --- a/nix/module-eval/core-toggle.nix +++ b/nix/module-eval/core-toggle.nix @@ -18,6 +18,7 @@ let ; }) hive + hiveWithController runGroup baoNames baoStream @@ -26,9 +27,9 @@ let swarmServiceEnables ; - allLocal = hive { deploy.singleHostSwarm = true; }; + allLocal = hiveWithController { deploy.singleHostSwarm = true; }; - bare = hive { }; + bare = hiveWithController { }; # 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: @@ -44,7 +45,7 @@ let # 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. - noAgentQueue = hive { deploy.hive-controller.queue.agentNatsUrl = null; }; + noAgentQueue = hiveWithController { deploy.hive-controller.queue.agentNatsUrl = null; }; # On the forge's host: the runner is refused anywhere else. withCi = hive { @@ -487,7 +488,7 @@ let refusedHiveName = extra: lib.any (a: !a.assertion && lib.hasInfix "services.hyperhive.hiveName to be set" a.message) - (hive ({ hiveName = null; } // extra)).assertions; + (hiveWithController ({ hiveName = null; } // extra)).assertions; in refusedHiveName { } && refusedHiveName { @@ -504,7 +505,7 @@ let name = "a missing swarm.domain is refused on a hive and on a swarm-service host, and a host enabling nothing evaluates"; ok = let - noDomain = extra: hive ({ swarm.domain = null; } // extra); + noDomain = extra: hiveWithController ({ swarm.domain = null; } // extra); refusedSwarmDomain = extra: lib.any ( diff --git a/nix/module-eval/hive-tls.nix b/nix/module-eval/hive-tls.nix index edbc01c2..08775278 100644 --- a/nix/module-eval/hive-tls.nix +++ b/nix/module-eval/hive-tls.nix @@ -21,26 +21,26 @@ let nixosSystem ; }) - hive + hiveWithController runGroup ; # Every service on one host, so the store's granting unit renders the role # this leaf is issued through. - allLocal = hive { + allLocal = hiveWithController { deploy.singleHostSwarm = true; }; # 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. - shortTtl = hive { + shortTtl = hiveWithController { deploy.singleHostSwarm = true; deploy.bao.servicesPkiLeafTtlHours = 48; }; # Fronting none of the swarm's names, reading a store it does not run, # with the reader identity and the hive's own identity placed by hand. - noService = hive { + noService = hiveWithController { deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; deploy.bao.serverCaFile = "/etc/pki/bao-ca.pem"; @@ -50,7 +50,7 @@ let noSvcFetch = noService.systemd.services.swarm-services-cert.script; # The same hive with no store identity at all. - noIdentity = hive { }; + noIdentity = hiveWithController { }; noIdFetch = noIdentity.systemd.services.swarm-services-cert.script; # The `want_svc=0` branch of a rendered script, up to the `fi` that closes it (the diff --git a/nix/module-eval/lib.nix b/nix/module-eval/lib.nix index 1b219351..fd86633e 100644 --- a/nix/module-eval/lib.nix +++ b/nix/module-eval/lib.nix @@ -24,7 +24,7 @@ # a certificate needs something that *runs* it. A case needing a rendered # file must stub what it drags in (`deploy.swarm-ui.package = pkgs.emptyDirectory`). # -# `hive`/`agent`/`agentWith` builders, the shared cross-group helpers, and +# `hive`/`hiveWithController`/`agent`/`agentWith` builders, the shared cross-group helpers, and # the `runGroup` derivation builder live in ./lib.nix; each file below # defines only the fixtures and cases its own cluster needs. { @@ -59,7 +59,6 @@ let # just one level down. `recursiveUpdate` merges nested attrsets # all the way down instead. services.hyperhive = lib.recursiveUpdate { - deploy.hive-controller.enable = true; hiveName = "h1"; swarm.domain = "t.local"; swarm.hives.h1.domain = "h1.t.local"; @@ -68,6 +67,13 @@ let ]; }).config; + # `hive` with the hive-controller tier on (hive-c0re, the agent containers, + # `deploy.hive-controller.*`). Only a fixture whose claim reads that tier + # takes this; every other claim is about the swarm services and evaluates + # without a hive on the host. + hiveWithController = + extra: hive (lib.recursiveUpdate { deploy.hive-controller.enable = true; } extra); + # The other half of the tree. `nix/agent-modules/` is evaluated by nothing # else in this suite — every fixture above is a host — so a rendered # container config was only ever read by a real deploy. Same entry point @@ -206,6 +212,7 @@ let in { inherit hive; + inherit hiveWithController; inherit agent; inherit agentWith; inherit baoNames; diff --git a/nix/module-eval/matrix-core.nix b/nix/module-eval/matrix-core.nix index 9a2ccac0..348bc525 100644 --- a/nix/module-eval/matrix-core.nix +++ b/nix/module-eval/matrix-core.nix @@ -18,6 +18,7 @@ let ; }) hive + hiveWithController runGroup ; @@ -30,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 = hive { deploy.matrix.enable = true; }; + matrixNoBaoIdentity = hiveWithController { deploy.matrix.enable = true; }; # A hive host that does not run the homeserver: its hive-c0re reaches the # homeserver across the network. - matrixElsewhere = hive { }; + matrixElsewhere = hiveWithController { }; cases = [ { # Absence arm, and what makes the one above able to fail: with no leaf diff --git a/nix/module-eval/nats-tls.nix b/nix/module-eval/nats-tls.nix index 8dce5e68..22bb5c6a 100644 --- a/nix/module-eval/nats-tls.nix +++ b/nix/module-eval/nats-tls.nix @@ -19,6 +19,7 @@ let ; }) hive + hiveWithController runGroup bridgePorts ; @@ -28,7 +29,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 = hive { + allLocal = hiveWithController { deploy.singleHostSwarm = true; }; @@ -58,11 +59,13 @@ let deploy.swarm-controller.baoClientCertFile = "/var/lib/swarm-controller/bao-client.pem"; deploy.swarm-controller.baoClientKeyFile = "/var/lib/swarm-controller/bao-client-key.pem"; }; - remote = hive (lib.recursiveUpdate remoteSecrets { deploy.swarm-controller.enable = true; }); + remote = hiveWithController ( + lib.recursiveUpdate remoteSecrets { deploy.swarm-controller.enable = true; } + ); # The same hive given its status secret by hand, which is what turns # publishing on away from the IdP's host. - remotePublishing = hive ( + remotePublishing = hiveWithController ( lib.recursiveUpdate remoteSecrets { deploy.hive-controller.statusPublish.clientSecretFile = "/var/lib/secrets/hive-h1.secret"; } diff --git a/nix/module-eval/secret-publisher.nix b/nix/module-eval/secret-publisher.nix index 0dc28e87..21d8ec32 100644 --- a/nix/module-eval/secret-publisher.nix +++ b/nix/module-eval/secret-publisher.nix @@ -18,6 +18,7 @@ let ; }) hive + hiveWithController runGroup ; @@ -80,7 +81,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 = hive { + baoWithMatrix = hiveWithController { deploy.bao.enable = true; deploy.matrix.enable = true; }; diff --git a/nix/module-eval/swarm-services-switch.nix b/nix/module-eval/swarm-services-switch.nix index b1e00b3c..17f590ff 100644 --- a/nix/module-eval/swarm-services-switch.nix +++ b/nix/module-eval/swarm-services-switch.nix @@ -18,6 +18,7 @@ let ; }) hive + hiveWithController runGroup bridgePortsOrNone swarmServiceEnables @@ -90,7 +91,7 @@ let # The swarm's shared services hosted HERE without the all-local mode — a # services box with hives elsewhere, the shape ./host-modules/ # swarm-required-services.nix documents the switch for. - swarmServicesHere = hive { deploy.allSwarmServices = true; }; + swarmServicesHere = hiveWithController { deploy.allSwarmServices = true; }; # Same, with one of those services placed on another host. Every derivation # in that module is `mkDefault` so this stays expressible. @@ -105,7 +106,7 @@ let upstream = "https://example.invalid/tool"; dest = "mirrors/tool"; }; - controllerWithMirrors = hive { + controllerWithMirrors = hiveWithController { deploy.swarm-controller.enable = true; deploy.forgejo.enable = true; deploy.forgejo.mirrors = [ aMirror ]; @@ -428,7 +429,7 @@ let # which is what keeps "asserted by whoever needs it" from being a # setting the operator cannot turn off. name = "an explicit gateway.enable = false wins over the modules asserting it"; - ok = !(hive { gateway.enable = false; }).services.nginx.enable; + ok = !(hiveWithController { gateway.enable = false; }).services.nginx.enable; } { name = "declared forge mirrors reach the swarm-controller, not hive-c0re";