nix: re-run the host's store fetches on a ten-minute timer
Seven host oneshots fetch a secret from the swarm secret store into a file their consumer loads at start: swarm-bao-grafana-oidc, swarm-bao-otel-oidc, swarm-bao-matrix-oidc, swarm-bao-ci-runner, swarm-bao-forge-oidc, swarm-bao-hive-otel-oidc and swarm-bao-forwarder-oidc. Each was RemainAfterExit with no timer, so it ran at boot or deploy and never again: a secret re-minted in the store reached its consumer only through an operator restarting the fetch. lib/store-refresh-timer.nix gives each one a timer (OnUnitInactiveSec 10min, RandomizedDelaySec 1min, the agent's own store re-read interval), and each drops RemainAfterExit so a timer start is not a no-op. Each script already writes the file only when secret_differs, and refresh_consumer/refresh_unit restart the consumer only when the file is newer than its last start, so a run reading an unchanged value restarts nothing. Two docs said these units do not run on a timer; credentials.md's ci-runner row said the runner token is not re-pulled. Part of #5017 (slice T).
This commit is contained in:
parent
c2dc18413b
commit
2f05e2db28
12 changed files with 146 additions and 13 deletions
|
|
@ -73,6 +73,43 @@ let
|
|||
vals = lib.concatMap (s: builtins.attrValues (s.serviceConfig or { })) (builtins.attrValues svcs);
|
||||
in
|
||||
builtins.foldl' (acc: v: builtins.seq v acc) true vals;
|
||||
|
||||
# The host fetches that restart their consumer when the store's value
|
||||
# changes (../host-modules/lib/refresh-consumer.nix), each on a fixture
|
||||
# that renders it.
|
||||
storeRefreshed = [
|
||||
{
|
||||
unit = "swarm-bao-grafana-oidc";
|
||||
machine = allLocal;
|
||||
}
|
||||
{
|
||||
unit = "swarm-bao-otel-oidc";
|
||||
machine = allLocal;
|
||||
}
|
||||
{
|
||||
unit = "swarm-bao-matrix-oidc";
|
||||
machine = allLocal;
|
||||
}
|
||||
{
|
||||
unit = "swarm-bao-forge-oidc";
|
||||
machine = allLocal;
|
||||
}
|
||||
{
|
||||
unit = "swarm-bao-hive-otel-oidc";
|
||||
machine = allLocal;
|
||||
}
|
||||
{
|
||||
unit = "swarm-bao-forwarder-oidc";
|
||||
machine = allLocal;
|
||||
}
|
||||
{
|
||||
unit = "swarm-bao-ci-runner";
|
||||
machine = withCi;
|
||||
}
|
||||
];
|
||||
|
||||
remainsAfterExit = u: u.serviceConfig.RemainAfterExit or false;
|
||||
|
||||
cases = [
|
||||
{
|
||||
# The domain is the stub's swarm domain, which both fixtures share.
|
||||
|
|
@ -621,6 +658,42 @@ let
|
|||
}
|
||||
&& lib.all (a: a.assertion) (noDomain { deploy.hive-controller.enable = false; }).assertions;
|
||||
}
|
||||
];
|
||||
{
|
||||
# Control: a store fetch with no timer still renders `RemainAfterExit`,
|
||||
# so the cases below read a real value rather than a missing one.
|
||||
name = "Grafana's admin-password fetch stays active after it exits and has no timer";
|
||||
ok =
|
||||
allLocal.systemd.services ? swarm-bao-grafana-admin
|
||||
&& remainsAfterExit allLocal.systemd.services.swarm-bao-grafana-admin
|
||||
&& !(allLocal.systemd.timers ? swarm-bao-grafana-admin);
|
||||
}
|
||||
{
|
||||
# A timer whose unit does not exist fails to load, so each timer is
|
||||
# gated exactly as its fetch is.
|
||||
name = "a host running none of the store-refreshed fetches renders none of their timers";
|
||||
ok = lib.all (
|
||||
{ unit, ... }: !(bare.systemd.services ? ${unit}) && !(bare.systemd.timers ? ${unit})
|
||||
) storeRefreshed;
|
||||
}
|
||||
]
|
||||
# Starting a unit that stays `active (exited)` is a no-op, so a timer aimed
|
||||
# at one fires and fetches nothing.
|
||||
++ map (
|
||||
{ unit, machine }:
|
||||
{
|
||||
name = "${unit} re-runs on a ten-minute timer and does not stay active after it exits";
|
||||
ok =
|
||||
let
|
||||
s = machine.systemd.services;
|
||||
t = machine.systemd.timers;
|
||||
in
|
||||
s ? ${unit}
|
||||
&& t ? ${unit}
|
||||
&& !(remainsAfterExit s.${unit})
|
||||
&& lib.elem "timers.target" t.${unit}.wantedBy
|
||||
&& t.${unit}.timerConfig.OnUnitInactiveSec == "10min"
|
||||
&& (t.${unit}.timerConfig.Unit or "${unit}.service") == "${unit}.service";
|
||||
}
|
||||
) storeRefreshed;
|
||||
in
|
||||
runGroup "core-toggle" cases
|
||||
|
|
|
|||
Loading…
Reference in a new issue