Watch
0
0
Fork
You've already forked hyperhive
0

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.
This commit is contained in:
atlas 2026-10-05 02:27:33 +02:00 • committed by mara
commit ea63b0d150
10 changed files with 47 additions and 31 deletions

View file

@ -28,7 +28,7 @@ let
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null; pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
in in
{ {
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.nats.enable && haveMintedPki) { config = lib.mkIf (deployCfg.nats.enable && haveMintedPki) {
services.hyperhive.deploy.nats = { services.hyperhive.deploy.nats = {
authBaoClientCertFile = lib.mkDefault "${pkiDir}/nats-auth.pem"; authBaoClientCertFile = lib.mkDefault "${pkiDir}/nats-auth.pem";
authBaoClientKeyFile = lib.mkDefault "${pkiDir}/nats-auth-key.pem"; authBaoClientKeyFile = lib.mkDefault "${pkiDir}/nats-auth-key.pem";

View file

@ -18,12 +18,13 @@ let
; ;
}) })
hive hive
hiveWithController
runGroup runGroup
; ;
# The store, plus a placed bootstrap token: the only shape in which the # The store, plus a placed bootstrap token: the only shape in which the
# granter's own role can be written at all. # granter's own role can be written at all.
baoGrantHere = hive { baoGrantHere = hiveWithController {
deploy.bao.enable = true; deploy.bao.enable = true;
deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token"; 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 # 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 # only testable where all of them render at once — that is the deployment in
# which two of them sharing a leaf would be invisible. # which two of them sharing a leaf would be invisible.
baoGrantWithConsumers = hive { baoGrantWithConsumers = hiveWithController {
deploy.bao.enable = true; deploy.bao.enable = true;
deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token"; deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token";
deploy.grafana.enable = true; deploy.grafana.enable = true;
@ -109,7 +110,7 @@ let
# The store with none of the readers beside it. Grafana and the collector are # The store with none of the readers beside it. Grafana and the collector are
# simply off; the queue reader renders on any host holding # 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. # 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.enable = true;
deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token"; deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token";
deploy.bao.queueAgentClientCertFile = lib.mkForce null; 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 # 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. # hand. The deployment in which there is no local policy unit to wait for.
baoRemoteReaders = hive { baoRemoteReaders = hiveWithController {
deploy.grafana.enable = true; deploy.grafana.enable = true;
deploy.swarm-otel.enable = true; deploy.swarm-otel.enable = true;
deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; deploy.bao.clientCertFile = "/etc/pki/bao-client.pem";

View file

@ -18,12 +18,13 @@ let
; ;
}) })
hive hive
hiveWithController
runGroup runGroup
; ;
# The store and a service that reads from it, versus the store alone. The # 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. # pair is what makes the reader's absence arm mean anything.
baoWithMatrix = hive { baoWithMatrix = hiveWithController {
deploy.bao.enable = true; deploy.bao.enable = true;
deploy.matrix.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 # 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 # the hive's own. Naming only `clientCertFile` here would leave the unit
# unrendered — which is what the arms below would then be asserting about. # unrendered — which is what the arms below would then be asserting about.
baoRemoteReader = hive { baoRemoteReader = hiveWithController {
deploy.matrix.enable = true; deploy.matrix.enable = true;
deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; deploy.bao.clientCertFile = "/etc/pki/bao-client.pem";
deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.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 # 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 # needs it, and defining it here rather than importing keeps each group's
# fixture set its own, as ./lib.nix asks. # 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. # 🩸 The hand-configured remote reader that named the hive's leaf and stopped.
# `baoRemoteReader` above is the same deployment done right; this one holds # `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 # 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 # 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. # and say nothing — which is what the refusal arms below are about.
baoRemoteReaderMissingLeaves = hive { baoRemoteReaderMissingLeaves = hiveWithController {
deploy.matrix.enable = true; deploy.matrix.enable = true;
deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; deploy.bao.clientCertFile = "/etc/pki/bao-client.pem";
deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.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 # The same omission on a hive that does NOT run a homeserver: the queue
# refusal checks only the hive's own toggle. # refusal checks only the hive's own toggle.
remoteReaderNoMatrixMissingLeaves = hive { remoteReaderNoMatrixMissingLeaves = hiveWithController {
deploy.bao.clientCertFile = "/etc/pki/bao-client.pem"; deploy.bao.clientCertFile = "/etc/pki/bao-client.pem";
deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem";
}; };

View file

@ -18,6 +18,7 @@ let
; ;
}) })
hive hive
hiveWithController
runGroup runGroup
baoNames baoNames
baoStream baoStream
@ -26,9 +27,9 @@ let
swarmServiceEnables 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`) # 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: # 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 # 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. # 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. # On the forge's host: the runner is refused anywhere else.
withCi = hive { withCi = hive {
@ -487,7 +488,7 @@ let
refusedHiveName = refusedHiveName =
extra: extra:
lib.any (a: !a.assertion && lib.hasInfix "services.hyperhive.hiveName to be set" a.message) 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 in
refusedHiveName { } refusedHiveName { }
&& 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"; name = "a missing swarm.domain is refused on a hive and on a swarm-service host, and a host enabling nothing evaluates";
ok = ok =
let let
noDomain = extra: hive ({ swarm.domain = null; } // extra); noDomain = extra: hiveWithController ({ swarm.domain = null; } // extra);
refusedSwarmDomain = refusedSwarmDomain =
extra: extra:
lib.any ( lib.any (

View file

@ -21,26 +21,26 @@ let
nixosSystem nixosSystem
; ;
}) })
hive hiveWithController
runGroup runGroup
; ;
# Every service on one host, so the store's granting unit renders the role # Every service on one host, so the store's granting unit renders the role
# this leaf is issued through. # this leaf is issued through.
allLocal = hive { allLocal = hiveWithController {
deploy.singleHostSwarm = true; deploy.singleHostSwarm = true;
}; };
# The same host with the role's lifetime moved, so a threshold that is a # 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. # number of its own shows up as one that did not move with it.
shortTtl = hive { shortTtl = hiveWithController {
deploy.singleHostSwarm = true; deploy.singleHostSwarm = true;
deploy.bao.servicesPkiLeafTtlHours = 48; deploy.bao.servicesPkiLeafTtlHours = 48;
}; };
# Fronting none of the swarm's names, reading a store it does not run, # 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. # 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.clientCertFile = "/etc/pki/bao-client.pem";
deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem"; deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem";
deploy.bao.serverCaFile = "/etc/pki/bao-ca.pem"; deploy.bao.serverCaFile = "/etc/pki/bao-ca.pem";
@ -50,7 +50,7 @@ let
noSvcFetch = noService.systemd.services.swarm-services-cert.script; noSvcFetch = noService.systemd.services.swarm-services-cert.script;
# The same hive with no store identity at all. # The same hive with no store identity at all.
noIdentity = hive { }; noIdentity = hiveWithController { };
noIdFetch = noIdentity.systemd.services.swarm-services-cert.script; 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 # The `want_svc=0` branch of a rendered script, up to the `fi` that closes it (the

View file

@ -24,7 +24,7 @@
# a certificate needs something that *runs* it. A case needing a rendered # 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`). # 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 # the `runGroup` derivation builder live in ./lib.nix; each file below
# defines only the fixtures and cases its own cluster needs. # defines only the fixtures and cases its own cluster needs.
{ {
@ -59,7 +59,6 @@ let
# just one level down. `recursiveUpdate` merges nested attrsets # just one level down. `recursiveUpdate` merges nested attrsets
# all the way down instead. # all the way down instead.
services.hyperhive = lib.recursiveUpdate { services.hyperhive = lib.recursiveUpdate {
deploy.hive-controller.enable = true;
hiveName = "h1"; hiveName = "h1";
swarm.domain = "t.local"; swarm.domain = "t.local";
swarm.hives.h1.domain = "h1.t.local"; swarm.hives.h1.domain = "h1.t.local";
@ -68,6 +67,13 @@ let
]; ];
}).config; }).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 # 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 # 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 # container config was only ever read by a real deploy. Same entry point
@ -206,6 +212,7 @@ let
in in
{ {
inherit hive; inherit hive;
inherit hiveWithController;
inherit agent; inherit agent;
inherit agentWith; inherit agentWith;
inherit baoNames; inherit baoNames;

View file

@ -18,6 +18,7 @@ let
; ;
}) })
hive hive
hiveWithController
runGroup runGroup
; ;
@ -30,11 +31,11 @@ let
# The same hive with the identity taken away, which separates "a homeserver # The same hive with the identity taken away, which separates "a homeserver
# is deployed" from "this host can authenticate to the store". # 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 # A hive host that does not run the homeserver: its hive-c0re reaches the
# homeserver across the network. # homeserver across the network.
matrixElsewhere = hive { }; matrixElsewhere = hiveWithController { };
cases = [ cases = [
{ {
# Absence arm, and what makes the one above able to fail: with no leaf # Absence arm, and what makes the one above able to fail: with no leaf

View file

@ -19,6 +19,7 @@ let
; ;
}) })
hive hive
hiveWithController
runGroup runGroup
bridgePorts bridgePorts
; ;
@ -28,7 +29,7 @@ let
# Every service on one host. The queue, the store and every in-tree client of the queue # 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. # are all here, so the scan below reads each of them.
allLocal = hive { allLocal = hiveWithController {
deploy.singleHostSwarm = true; deploy.singleHostSwarm = true;
}; };
@ -58,11 +59,13 @@ let
deploy.swarm-controller.baoClientCertFile = "/var/lib/swarm-controller/bao-client.pem"; deploy.swarm-controller.baoClientCertFile = "/var/lib/swarm-controller/bao-client.pem";
deploy.swarm-controller.baoClientKeyFile = "/var/lib/swarm-controller/bao-client-key.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 # The same hive given its status secret by hand, which is what turns
# publishing on away from the IdP's host. # publishing on away from the IdP's host.
remotePublishing = hive ( remotePublishing = hiveWithController (
lib.recursiveUpdate remoteSecrets { lib.recursiveUpdate remoteSecrets {
deploy.hive-controller.statusPublish.clientSecretFile = "/var/lib/secrets/hive-h1.secret"; deploy.hive-controller.statusPublish.clientSecretFile = "/var/lib/secrets/hive-h1.secret";
} }

View file

@ -18,6 +18,7 @@ let
; ;
}) })
hive hive
hiveWithController
runGroup runGroup
; ;
@ -80,7 +81,7 @@ let
# The store and a service that reads from it, versus the store alone. The # 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. # pair is what makes the reader's absence arm mean anything.
baoWithMatrix = hive { baoWithMatrix = hiveWithController {
deploy.bao.enable = true; deploy.bao.enable = true;
deploy.matrix.enable = true; deploy.matrix.enable = true;
}; };

View file

@ -18,6 +18,7 @@ let
; ;
}) })
hive hive
hiveWithController
runGroup runGroup
bridgePortsOrNone bridgePortsOrNone
swarmServiceEnables swarmServiceEnables
@ -90,7 +91,7 @@ let
# The swarm's shared services hosted HERE without the all-local mode — a # The swarm's shared services hosted HERE without the all-local mode — a
# services box with hives elsewhere, the shape ./host-modules/ # services box with hives elsewhere, the shape ./host-modules/
# swarm-required-services.nix documents the switch for. # 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 # Same, with one of those services placed on another host. Every derivation
# in that module is `mkDefault` so this stays expressible. # in that module is `mkDefault` so this stays expressible.
@ -105,7 +106,7 @@ let
upstream = "https://example.invalid/tool"; upstream = "https://example.invalid/tool";
dest = "mirrors/tool"; dest = "mirrors/tool";
}; };
controllerWithMirrors = hive { controllerWithMirrors = hiveWithController {
deploy.swarm-controller.enable = true; deploy.swarm-controller.enable = true;
deploy.forgejo.enable = true; deploy.forgejo.enable = true;
deploy.forgejo.mirrors = [ aMirror ]; deploy.forgejo.mirrors = [ aMirror ];
@ -428,7 +429,7 @@ let
# which is what keeps "asserted by whoever needs it" from being a # which is what keeps "asserted by whoever needs it" from being a
# setting the operator cannot turn off. # setting the operator cannot turn off.
name = "an explicit gateway.enable = false wins over the modules asserting it"; 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"; name = "declared forge mirrors reach the swarm-controller, not hive-c0re";