`services.hyperhive.enable` and `services.hyperhive.c0re.enable` are gone. One switch, `services.hyperhive.deploy.hive-controller.enable` (default false, as the old toggle was), now gates hive-c0re and hive-priv. Both old paths are `mkRenamedOptionModule` shims in deploy.nix, so a host config that still sets either evaluates as before and gets a rename warning. Every other read of the old toggle is resolved, including the 29 made through the `hyperhiveCfg`/`hiveCfg` aliases: - Dropped: each swarm service and its glue keeps only its own deploy toggle (authelia, bao and its PKI glue, grafana, victorialogs, victoriametrics, the secret publisher, swarm-ca, the OIDC client rows, the controller/nats/matrix-ctl/publisher/services-issuer identities), the forge, and the `domain` deprecation warning. - To deploy.hive-controller.enable: the queue-agent credential reader and its assertion, which feed hive-c0re and write under its state dir, plus their policy-order entry; the network identity assertions; hive-tls's two writes into hive-c0re's environment. - hive-tls runs where the gateway runs self-signed (`gateway.enable && useSelfSigned`), not on every host. - The matrix appservice-token reader and its assertion stay on `deploy.matrix.enable` plus their client-identity checks. They read deploy.matrix's token file and registration script; their deploy.bao inputs are the client-half options a hive sets to read a store it does not run, so gating on deploy.bao.enable would drop the tested remote-reader case. - The `hiveName` assertion moves from hive-network.nix to hyperhive.nix and fires wherever the hive, the store or the homeserver runs: each turns the name into an identifier with no fallback. On a host with `deploy.allSwarmServices` and no hive, the documented services-host recipe, authelia, bao, grafana, victorialogs, victoriametrics, the OIDC client rows and the hive CA now render; before, the old toggle being off left them out. Refs #4500
219 lines
8.5 KiB
Nix
219 lines
8.5 KiB
Nix
# `checks.module-eval-swarm-services-switch` — 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
|
|
bridgePortsOrNone
|
|
swarmServiceEnables
|
|
;
|
|
|
|
# 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;
|
|
};
|
|
|
|
# The same switch with every service it owns placed elsewhere, so the only
|
|
# thing that can enable the gateway/resolver/bridge here is that toggle's
|
|
# own module — each service module asserts them from its own `enable`.
|
|
swarmServicesSwitchAlone = hive {
|
|
deploy.hive-controller.enable = false;
|
|
deploy.allSwarmServices = true;
|
|
deploy.matrix.enable = false;
|
|
otel.enable = false;
|
|
deploy.authelia.enable = false;
|
|
deploy.nats.enable = false;
|
|
deploy.swarm-otel.enable = false;
|
|
deploy.victoriametrics.enable = false;
|
|
deploy.grafana.enable = false;
|
|
deploy.victorialogs.enable = false;
|
|
deploy.bao.enable = false;
|
|
deploy.forgejo.enable = false;
|
|
};
|
|
|
|
# Ports asked for on such a host. The request is the whole condition: the
|
|
# firewall hole exists because an operator named a port, not because the
|
|
# hive is running, and an agent reaching a host service is a claim about
|
|
# the host's own listeners either way.
|
|
exposedPortsNoHive = hive {
|
|
deploy.hive-controller.enable = false;
|
|
network.exposeHostPorts = [ 5432 ];
|
|
};
|
|
|
|
# The swarm UI where its own toggle is on — the control the absence arm
|
|
# needs, since nothing else in this suite renders this vhost and an arm
|
|
# saying "it is not there" would hold just as well if it were never there.
|
|
# Package stubbed per this file's header: the vhost roots at it.
|
|
swarmUiHere = hive {
|
|
deploy.swarm-ui.enable = true;
|
|
deploy.swarm-ui.package = pkgs.emptyDirectory;
|
|
};
|
|
|
|
# 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; };
|
|
|
|
# Same, with one of those services placed on another host. Every derivation
|
|
# in that module is `mkDefault` so this stays expressible.
|
|
swarmServicesBaoElsewhere = hive {
|
|
deploy.allSwarmServices = true;
|
|
deploy.bao.enable = false;
|
|
};
|
|
|
|
# The swarm-wide forge objects are the controller's (forge/objects.rs), so
|
|
# the mirror list and the org avatar go to its unit, not hive-c0re's.
|
|
aMirror = {
|
|
upstream = "https://example.invalid/tool";
|
|
dest = "mirrors/tool";
|
|
};
|
|
controllerWithMirrors = hive {
|
|
deploy.swarm-controller.enable = true;
|
|
deploy.forgejo.enable = true;
|
|
deploy.forgejo.mirrors = [ aMirror ];
|
|
c0re.orgAvatarPng = "/etc/marker-avatar.png";
|
|
};
|
|
mirrorsNoController = hive {
|
|
deploy.forgejo.enable = true;
|
|
deploy.forgejo.mirrors = [ aMirror ];
|
|
};
|
|
cases = [
|
|
{
|
|
# `lib.all` over an empty set holds vacuously, so the roster is counted
|
|
# before it is read: a helper that lost a member would otherwise turn
|
|
# this case green by measuring nothing.
|
|
name = "hosting the swarm's shared services turns on every service that switch owns";
|
|
ok =
|
|
let
|
|
es = swarmServiceEnables swarmServicesHere;
|
|
in
|
|
lib.length (lib.attrNames es) == 9 && lib.all lib.id (lib.attrValues es);
|
|
}
|
|
{
|
|
# The switch fills in for an operator who has not spoken and yields to
|
|
# one who has — that is what keeps a shared service placeable on a host
|
|
# of its own. A plain assignment or `mkForce` would satisfy both cases
|
|
# above and break this one. `nats` is the control: without it the case
|
|
# also passes on a fixture where nothing came on at all.
|
|
name = "placing one shared service elsewhere survives the switch that would enable it";
|
|
ok =
|
|
let
|
|
es = swarmServiceEnables swarmServicesBaoElsewhere;
|
|
in
|
|
!es.bao && es.nats;
|
|
}
|
|
{
|
|
# The controller sits on the OTHER tier: `singleHostSwarm` places it
|
|
# (./host-modules/local-defaults.nix) and swarm-ui follows the
|
|
# controller. Pinned so that moving a service between tiers is a
|
|
# decision someone makes rather than a merge nobody reads.
|
|
name = "hosting the swarm's shared services does not make a hive the swarm's control plane";
|
|
ok =
|
|
!swarmServicesHere.services.hyperhive.deploy.swarm-controller.enable
|
|
&& !swarmServicesHere.services.hyperhive.deploy.swarm-ui.enable;
|
|
}
|
|
{
|
|
# Control for the arm above, and the one place this slice is not inert:
|
|
# the ports are opened because they were named, on a host that never
|
|
# turned the hive on. The bridge firewall is the host's own, so there is
|
|
# nothing here for the hive toggle to have been protecting.
|
|
name = "a named exposeHostPorts opens its bridge port on the host's own say-so";
|
|
ok = builtins.elem 5432 (bridgePortsOrNone exposedPortsNoHive);
|
|
}
|
|
{
|
|
name = "the swarm UI claims the swarm apex where this host serves it";
|
|
ok = swarmUiHere.services.nginx.virtualHosts ? "t.local";
|
|
}
|
|
{
|
|
# The swarm-services toggle enables them explicitly, from its own
|
|
# module rather than from any of their defaults.
|
|
name = "the swarm-services toggle turns on the gateway, resolver and bridge by itself";
|
|
ok =
|
|
swarmServicesSwitchAlone.services.hyperhive.gateway.enable
|
|
&& swarmServicesSwitchAlone.services.hyperhive.gateway.dns.enable
|
|
&& swarmServicesSwitchAlone.services.hyperhive.network.enable;
|
|
}
|
|
{
|
|
# Each service is gated on its own deployment toggle alone, so a host
|
|
# that runs no hive still runs every shared service the switch turns
|
|
# on, plus the plumbing and the self-signed CA they are served through.
|
|
# Nothing starts the hive's own daemons. `hive-c0re` is checked by what
|
|
# starts it rather than by key: ../host-modules/hive-network.nix's bridge
|
|
# block writes to its environment wherever the bridge is up.
|
|
name = "a services-only host runs the swarm's shared services and no hive";
|
|
ok =
|
|
let
|
|
c = swarmServicesOnly.containers;
|
|
s = swarmServicesOnly.systemd.services;
|
|
h = swarmServicesOnly.services.hyperhive;
|
|
in
|
|
lib.all (m: c ? ${m}) [
|
|
"swarm-authelia"
|
|
"swarm-bao"
|
|
"swarm-grafana"
|
|
"swarm-victorialogs"
|
|
"swarm-victoriametrics"
|
|
"swarm-otel"
|
|
"swarm-nats"
|
|
"hive-matrix"
|
|
"hive-forge"
|
|
]
|
|
&& h.gateway.enable
|
|
&& h.gateway.dns.enable
|
|
&& h.network.enable
|
|
&& s ? hive-tls-ca
|
|
&& s ? swarm-bao-pki
|
|
&& (s.hive-c0re.wantedBy or [ ]) == [ ]
|
|
&& !(swarmServicesOnly.systemd.sockets ? hive-c0re)
|
|
&& !(swarmServicesOnly.systemd.sockets ? hive-priv)
|
|
&& !(s ? swarm-bao-queue-agent);
|
|
}
|
|
{
|
|
# An operator's explicit `false` beats every `mkDefault` assertion,
|
|
# 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;
|
|
}
|
|
{
|
|
name = "declared forge mirrors reach the swarm-controller, not hive-c0re";
|
|
ok =
|
|
builtins.fromJSON controllerWithMirrors.systemd.services.swarm-controller.environment.SWARM_CONTROLLER_FORGE_MIRRORS
|
|
== [ aMirror ]
|
|
&& !(controllerWithMirrors.systemd.services.hive-c0re.environment ? HYPERHIVE_FORGE_MIRRORS);
|
|
}
|
|
{
|
|
name = "forge mirrors on a host without the controller warn that nothing seeds them";
|
|
ok =
|
|
let
|
|
warns = cfg: lib.any (lib.hasInfix "declare them on the swarm-controller's host") cfg.warnings;
|
|
in
|
|
warns mirrorsNoController && !(warns controllerWithMirrors);
|
|
}
|
|
{
|
|
name = "the old c0re org-avatar option lands on the controller's unit";
|
|
ok =
|
|
controllerWithMirrors.systemd.services.swarm-controller.environment.SWARM_CONTROLLER_CONFIG_ORG_AVATAR_PNG
|
|
== "/etc/marker-avatar.png"
|
|
&& !(controllerWithMirrors.systemd.services.hive-c0re.environment ? HIVE_ORG_AVATAR_PNG);
|
|
}
|
|
];
|
|
in
|
|
runGroup "swarm-services-switch" cases
|