Watch
0
0
Fork
You've already forked hyperhive
0

swarm-bao: re-run the operator viewer unit when the granter step succeeds

swarm-bao-operator-viewer-policy exits 0 while the granter may not
configure auth/oidc, so it never retries on its own. On 2026-09-29 the
operator fixed the granter (swarm-bao-granter-role succeeded at 15:29Z),
but the viewer unit had last run on 2026-09-28 19:11Z on that exit-0
branch. auth/oidc/config and the viewer role stayed unwritten and OIDC
login failed until a manual restart.

The granter unit now restarts the viewer unit from ExecStartPost, which
runs only after its script exits 0. Restart rather than start, because
the viewer unit is RemainAfterExit and a start would be a no-op.
--no-block, because the viewer unit is ordered after the granter and a
blocking restart would deadlock. The link is one-way, so the viewer's
own Restart=on-failure never re-runs the granter.

OnSuccess= would not fire (the granter stays active under
RemainAfterExit), and Wants=/PartOf= either no-op on an active unit or
also propagate a failed restart and every stop.

The viewer's log message no longer tells the operator to restart it.

Refs #4772
This commit is contained in:
atlas 2026-09-29 17:45:58 +02:00
commit 22c96282b9
3 changed files with 35 additions and 5 deletions

View file

@ -2717,6 +2717,17 @@ in
token_ttl=15m \
token_max_ttl=15m
'';
# Runs only once the script above exits 0, which is when the granter
# may first configure `auth/oidc`; the viewer unit exits 0 without
# doing so before then, and nothing else re-runs it. A restart because
# it is `RemainAfterExit`, where a start is a no-op.
#
# `--no-block`: the viewer unit is ordered after this one, so a
# blocking restart waits on a job that cannot run until this unit is
# active, which it is not until this command returns.
postStart = ''
systemctl restart --no-block swarm-bao-operator-viewer-policy.service
'';
};
# The swarm's first grant, written from the HOST. Every API listener but
@ -3348,6 +3359,7 @@ in
# has run with a bootstrap token that carries `sys/auth/oidc`. Until then
# this writes the policy and stops with exit 0: nothing is broken, the UI
# still takes a token, and a day of retries would change nothing.
# `swarm-bao-granter-role` restarts this unit each time it succeeds.
systemd.services.swarm-bao-operator-viewer-policy = lib.mkIf haveGranter {
description = "write the bao UI's OIDC login: the operator viewer policy, the oidc config and its role";
after = [
@ -3388,9 +3400,8 @@ in
*)
echo "the granter may not configure auth/oidc yet (capabilities: $caps), so the UI's OIDC login stays off and token login is unchanged." >&2
echo "one-time step, as root on this host (docs/getting-started/setup.md):" >&2
${lib.concatMapStringsSep "\n" (l: "echo ${lib.escapeShellArg " ${l}"} >&2") (
granterSetupSteps ++ [ "systemctl restart swarm-bao-operator-viewer-policy" ]
)}
${lib.concatMapStringsSep "\n" (l: "echo ${lib.escapeShellArg " ${l}"} >&2") granterSetupSteps}
echo "swarm-bao-granter-role re-runs this unit once it succeeds." >&2
exit 0
;;
esac

View file

@ -1484,7 +1484,25 @@ let
&& at probe < at "exit 0"
&& at "exit 0" < at "bao kv get"
&& at "bao kv get" < at "bao write auth/oidc/config"
&& lib.hasInfix "systemctl restart swarm-bao-operator-viewer-policy" s;
&& lib.hasInfix "swarm-bao-granter-role re-runs this unit once it succeeds." s
&& !(lib.hasInfix "systemctl restart swarm-bao-operator-viewer-policy" s);
}
{
# The granter step is what lets the viewer unit configure oidc, so its
# success restarts that unit: a restart since the unit is
# `RemainAfterExit`, `--no-block` since it is ordered after the granter.
# The ordering is one-way, so the viewer's retries never reach back.
name = "a successful granter step restarts the viewer unit without blocking";
ok =
let
s = baoGrantHere.systemd.services;
g = s.swarm-bao-granter-role;
v = s.swarm-bao-operator-viewer-policy;
in
lib.hasInfix "systemctl restart --no-block swarm-bao-operator-viewer-policy.service" g.postStart
&& lib.elem "swarm-bao-granter-role.service" v.after
&& !(lib.elem "swarm-bao-operator-viewer-policy.service" g.after)
&& !(v ? postStart && lib.hasInfix "swarm-bao-granter-role" v.postStart);
}
{
# The client secret is on stdin, never an argument in /proc; the rest is