swarm-bao-agent-pki: decide openbao's restart on the shared locale-independent compare
`swarm-bao-agent-pki` carried its own copy of the mtime-vs-start compare, with the same `stat -c %.6Y | tr -d .` read: on a comma-locale host the `-le` errored and, under `seal = "pkcs11"`, openbao was restarted on every run of the unit. The compare moves out of `refresh_unit` into `written_after_start` (0 newer, 1 not newer, 2 unreadable and logged at `err`), and both `refresh_unit` and the agent-pki unit call it. The agent-pki unit cannot call `refresh_unit` itself: it restarts with `restart`, not `try-restart`, runs a fail-closed check of the bundle between the compare and the restart, and under shamir prints the step instead of restarting. It acts only on 0, so an unreadable time skips the restart as `refresh_unit` does. `script-test-refresh-consumer` covers the three return codes in the agent-pki call shape; `module-eval-bao-basics` asserts both seal arms decide on that call and that no `%.6Y` read is left in the unit. Refs #5230
This commit is contained in:
parent
be7eaa8181
commit
8d554477ca
5 changed files with 67 additions and 24 deletions
|
|
@ -31,15 +31,15 @@
|
|||
[ ! -f "$1" ] || [ "$(< "$1")" != "$2" ]
|
||||
}
|
||||
|
||||
# <unit> <path> [systemctl option…]. Nothing when <unit> last entered
|
||||
# `active` after <path> was last written. Otherwise a running <unit> is
|
||||
# restarted and a failed one — it may have hit its start limit without the
|
||||
# credential — is reset and started; one stopped on purpose stays stopped.
|
||||
# The options are passed to every `systemctl` call, which is how
|
||||
# `refresh_consumer` reaches into a container. `--no-block` because a
|
||||
# caller ordered `Before=` its consumer must not wait on a job that waits
|
||||
# on the caller (see `swarm-services-cert`'s propagation in ../hive-tls.nix).
|
||||
refresh_unit() {
|
||||
# <unit> <path> [systemctl option…]. Returns 0 when <path> was last
|
||||
# written after <unit> last entered `active` (or <unit> never has), 1 when
|
||||
# it was not, and 2, logged at `err`, when either time does not read. Both
|
||||
# are bare microsecond counts, with no decimal separator for a locale to
|
||||
# change. A caller acts only on 0: the mtime stays, so the first run that
|
||||
# can read both still acts, where acting on doubt acts on every run. The
|
||||
# options are passed to `systemctl`, which is how a caller reaches into a
|
||||
# container.
|
||||
written_after_start() {
|
||||
local unit="$1" path="$2" started started_us=0 written_us
|
||||
shift 2
|
||||
# Empty for a unit that has never been active, which `date` would
|
||||
|
|
@ -49,17 +49,25 @@
|
|||
started_us="$(date -u -d "$started" +%s%6N)" || started_us=
|
||||
fi
|
||||
written_us="$(date -u -r "$path" +%s%6N)" || written_us=
|
||||
# Both are bare microsecond counts, with no decimal separator for a
|
||||
# locale to change. One that is not is logged at `err` and nothing is
|
||||
# restarted: the mtime stays, so the first run that can read both still
|
||||
# restarts <unit>, where restarting on doubt restarts it on every run.
|
||||
if [[ ! "$started_us" =~ ^[0-9]+$ || ! "$written_us" =~ ^[0-9]+$ ]]; then
|
||||
echo "<3>cannot compare the mtime of $path ('$written_us') with the last start of $unit ('$started') — leaving $unit as it is" >&2
|
||||
return 0
|
||||
return 2
|
||||
fi
|
||||
if [ "$written_us" -le "$started_us" ]; then
|
||||
return 0
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# <unit> <path> [systemctl option…]. Nothing unless `written_after_start`.
|
||||
# Otherwise a running <unit> is restarted and a failed one — it may have
|
||||
# hit its start limit without the credential — is reset and started; one
|
||||
# stopped on purpose stays stopped. `--no-block` because a caller ordered
|
||||
# `Before=` its consumer must not wait on a job that waits on the caller
|
||||
# (see `swarm-services-cert`'s propagation in ../hive-tls.nix).
|
||||
refresh_unit() {
|
||||
local unit="$1" path="$2"
|
||||
shift 2
|
||||
written_after_start "$unit" "$path" "$@" || return 0
|
||||
if systemctl "$@" is-failed --quiet "$unit"; then
|
||||
echo "$path changed after $unit last started, and $unit had failed — starting it"
|
||||
systemctl "$@" reset-failed "$unit"
|
||||
|
|
|
|||
|
|
@ -3429,6 +3429,7 @@ in
|
|||
echo "wrote ${agentCaCachePath}"
|
||||
fi
|
||||
|
||||
${refreshConsumer}
|
||||
${composeListenerBundle}
|
||||
compose_listener_bundle
|
||||
|
||||
|
|
@ -3437,11 +3438,12 @@ in
|
|||
echo "openbao in $machine is not running; it reads ${listenerClientCaPath} when it starts"
|
||||
exit 0
|
||||
fi
|
||||
started="$(systemctl --machine="$machine" show --timestamp=us+utc -p ActiveEnterTimestamp --value openbao.service)"
|
||||
started_us="$(date -u -d "$started" +%s%6N)"
|
||||
written_us="$(stat -c %.6Y ${listenerClientCaPath} | tr -d .)"
|
||||
if [ "$written_us" -le "$started_us" ]; then
|
||||
newer=0
|
||||
written_after_start openbao.service ${listenerClientCaPath} --machine="$machine" || newer=$?
|
||||
if [ "$newer" = 1 ]; then
|
||||
echo "openbao started after ${listenerClientCaPath} last changed; nothing to pick up"
|
||||
fi
|
||||
if [ "$newer" != 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue