module-eval: pin authelia's rename and its repointed readers

Two arms with different jobs. `usersFile` is configured through its old
path, so dropping the rename entry fails the eval. `hostClientSecretDir`
cannot be configured at all — it is read-only and derived — so its arm
asserts that a consumer renders the derived path, which is what breaks
if a reader is left pointing at the namespace the option moved out of.

Seven of those reads went through an `autheliaCfg` alias rather than a
full path, where a path-shaped grep does not see them. That, not the
rename, was the failure this change could have shipped.

The two read-only options get no rename entry. A rename module
contributes a definition to its target and a read-only option refuses a
second one, so a shim for either makes every evaluation fail with "set
multiple times", naming the option's own declaration as the rival. Of
the 38 distinct leaves the rename table already targets, these are the
only two declared read-only.
This commit is contained in:
atlas 2026-09-07 15:20:37 +02:00 committed by mara
commit 18de4b48c7
2 changed files with 41 additions and 8 deletions

View file

@ -97,14 +97,15 @@ in
[ "services" "hyperhive" "swarm" "authelia" "usersFile" ]
[ "services" "hyperhive" "deploy" "authelia" "usersFile" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "authelia" "hostClientSecretDir" ]
[ "services" "hyperhive" "deploy" "authelia" "hostClientSecretDir" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "authelia" "hostUsersFile" ]
[ "services" "hyperhive" "deploy" "authelia" "hostUsersFile" ]
)
# `hostClientSecretDir` and `hostUsersFile` moved in the same commit and
# deliberately have NO entry here. A rename module contributes a
# *definition* to its target, and both are `readOnly`, which refuses a
# second one — so a shim for either makes every evaluation fail with
# "set multiple times", naming the option's own default as the rival
# definition. Nothing could have set them anyway: both are derived and
# read-only, so the shim's only possible customer is a reader of the old
# path, and that reader now gets "option does not exist" instead, which
# says the same thing sooner.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "nats" "enable" ]
[ "services" "hyperhive" "deploy" "nats" "enable" ]

View file

@ -118,6 +118,24 @@ let
swarm.nats.calloutIssuerSeedFile = "/run/secrets/nats-issuer.seed";
};
# Seventh split slice. Only `usersFile` has a rename entry: the other two
# movers are `readOnly`, and a rename module contributes a definition, which
# a read-only option refuses — see ./host-modules/deploy.nix. So the two arms
# below have different jobs. `usersFile` tests the rename; the nats one tests
# that a reader repointed to the new namespace still renders the derived
# path, which is the failure this slice could actually have shipped — seven
# of those reads went through an alias a path-shaped grep cannot see.
autheliaOldPath = hive {
deploy.authelia.enable = true;
deploy.nats.enable = true;
swarm.authelia.usersFile = "/var/lib/test-authelia/users.yml";
swarm.nats.autoGenerateCallout = false;
swarm.nats.calloutUserPublicKey = "UTESTUSERPUBKEYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
swarm.nats.calloutIssuerPublicKey = "ATESTISSUERPUBKEYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
swarm.nats.calloutUserSeedFile = "/run/secrets/nats-user.seed";
swarm.nats.calloutIssuerSeedFile = "/run/secrets/nats-issuer.seed";
};
controllerOldPath = hive {
deploy.swarm-controller.enable = true;
swarm.controller.socketPath = "/run/test-ctrl/ctrl.sock";
@ -351,6 +369,20 @@ let
rule: lib.hasInfix "/run/test-grafana-sock" rule
) grafanaOldPath.systemd.tmpfiles.rules;
}
{
name = "a config written against the pre-rename authelia usersFile still reaches the bridge";
ok =
autheliaOldPath.containers.swarm-authelia.config.systemd.services.swarm-authelia-bridge.environment.SWARM_AUTHELIA_BRIDGE_USERS_FILE
== "/var/lib/test-authelia/users.yml";
}
{
# Not a rename test. `hostClientSecretDir` is `readOnly`, so the fixture
# cannot define it; what can break is a reader left pointing at the
# namespace it moved out of. Five modules read this through an
# `autheliaCfg` alias, where a path-shaped grep does not see it.
name = "a consumer of authelia's host client-secret dir renders it from the deploy namespace";
ok = lib.hasInfix "/var/lib/nixos-containers/swarm-authelia/var/lib/authelia-swarm/oidc-clients/" autheliaOldPath.systemd.services.swarm-nats-auth-secrets.script;
}
{
# The gateway's per-name issuer choice. If this ever collapses to a
# constant, every swarm-service vhost serves a certificate its CA