mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-23 03:27:50 +09:00
fix(fluxcd): keep the hook waiting on a release it cannot read
The pre-delete hook read a release's state once, so a single failed read was taken as the release being handled: its fence came off before helm-controller's uninstall, and the report counted it as released cleanly. A state read is now retried within the release's share of the window, a release whose state still cannot be read keeps its fence for the wait like one helm-controller has not handled, and the end of the wait reads each again before naming what the release was left to. A release still suspended after the sweep is named as suspended but not by flux-plunger, which also holds when no manager is recorded for the field at all. Assisted-by: LLM Signed-off-by: Alexey Artamonov <aleksei.artamonov@aenix.io>
This commit is contained in:
1 parent
568e6f5e18
commit
7011510456
2 files changed
+66
-19
No files matched your search
@@ -118,7 +118,8 @@ fences_left() {
|
||||
# for a reason other than pods (wait), the listing
|
||||
# (list), a listing after the first (relist), the
|
||||
# pod listing (pods), a release's ownership read
|
||||
# (<ns>@<name>.ownedread), unsuspend
|
||||
# (<ns>@<name>.ownedread), a release's state read
|
||||
# (<ns>@<name>.stateread), unsuspend
|
||||
# (<ns>@<name>.apply), unsuspend racing another
|
||||
# write (<ns>@<name>.applyconflict) or finalizer patch
|
||||
# (<ns>@<name>.patch);
|
||||
@@ -269,6 +270,7 @@ if [ "$verb" = get ]; then
|
||||
case "$tmpl" in *'ne .manager "flux-plunger"'*) [ -e "$rel.coowned" ] && echo other ;; esac
|
||||
exit 0 ;;
|
||||
*deletionTimestamp*)
|
||||
if take_fail "$rel.stateread"; then echo "fake: the state read timed out" >&2; exit 1; fi
|
||||
[ -e "$rel.deleting" ] || exit 0
|
||||
case "$tmpl" in *"$SUSPENDED_DELETING"*) if [ -e "$rel.suspended" ]; then printf 'suspended'; exit 0; fi ;; esac
|
||||
case "$tmpl" in *"$UNHANDLED"*) ;; *) exit 0 ;; esac
|
||||
@@ -492,7 +494,7 @@ run_hook() {
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(fences_left "$STATE")" -eq 0 ]
|
||||
grep -q -x "released $FENCE from tenant-x/app-x without an uninstall: another manager holds its suspension" "$tmp/out"
|
||||
grep -q -x "released $FENCE from tenant-x/app-x without an uninstall: it is still suspended, not by flux-plunger" "$tmp/out"
|
||||
grep -q -x 'released 1 of 1 fenced HelmReleases, 1 without an uninstall' "$tmp/out"
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
@@ -573,6 +575,44 @@ run_hook() {
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
|
||||
@test "a state read that fails once does not end the wait for helm-controller" {
|
||||
tmp=$(mktemp -d)
|
||||
prepare "$tmp"
|
||||
add_release "$STATE" tenant-a app-a "$FENCE"
|
||||
touch "$STATE/tenant-a@app-a.deleting"
|
||||
echo yes > "$STATE/tenant-a@app-a.owned"
|
||||
echo 3 > "$STATE/tenant-a@app-a.hcdelay"
|
||||
echo 1 > "$STATE/tenant-a@app-a.stateread.fail"
|
||||
|
||||
run_hook "$tmp"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(fences_left "$STATE")" -eq 0 ]
|
||||
[ "$(cat "$STATE/tenant-a@app-a.obs")" = "$(cat "$STATE/tenant-a@app-a.gen")" ]
|
||||
grep -q -x "released $FENCE from tenant-a/app-a" "$tmp/out"
|
||||
grep -q -x 'released 1 of 1 fenced HelmReleases, 0 without an uninstall' "$tmp/out"
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
|
||||
@test "a release whose state can never be read is still released inside the deadline, and says so" {
|
||||
tmp=$(mktemp -d)
|
||||
prepare "$tmp"
|
||||
add_release "$STATE" tenant-a app-a "$FENCE"
|
||||
touch "$STATE/tenant-a@app-a.deleting"
|
||||
echo yes > "$STATE/tenant-a@app-a.owned"
|
||||
echo 9999 > "$STATE/tenant-a@app-a.stateread.fail"
|
||||
|
||||
run_hook "$tmp"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(fences_left "$STATE")" -eq 0 ]
|
||||
grep -q -x "released $FENCE from tenant-a/app-a; whether helm-controller had handled it could not be read" "$tmp/out"
|
||||
# It kept the fence for the whole wait, as one helm-controller had not handled.
|
||||
[ "$(cat "$CLOCK")" -ge $((WINDOW - 30)) ]
|
||||
[ "$(cat "$CLOCK")" -lt "$DEADLINE" ]
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
|
||||
@test "a release helm-controller handles while the hook waits keeps its fence until the uninstall has run" {
|
||||
tmp=$(mktemp -d)
|
||||
prepare "$tmp"
|
||||
|
||||
@@ -347,12 +347,13 @@ spec:
|
||||
|
||||
# read_state <ns> <name>: how helm-controller stands with a release
|
||||
# being deleted. "suspended": it skips the uninstall of a suspended
|
||||
# release, and the suspension is another manager's. "unhandled":
|
||||
# release, and the suspension is not flux-plunger's. "unhandled":
|
||||
# its own finalizer no longer holds the release and it has not
|
||||
# observed the latest generation, so the fence is all that keeps
|
||||
# the release until it runs the uninstall. Empty otherwise.
|
||||
# the release until it runs the uninstall. Empty otherwise. Fails
|
||||
# when the release cannot be read.
|
||||
read_state() {
|
||||
kubectl --namespace "$1" get "$hr" "$2" --ignore-not-found --output go-template='{{`{{if .metadata.deletionTimestamp}}{{if .spec.suspend}}suspended{{else}}{{$held := false}}{{range .metadata.finalizers}}{{if eq . "finalizers.fluxcd.io"}}{{$held = true}}{{end}}{{end}}{{if and (not $held) (ne (printf "%v" .status.observedGeneration) (printf "%v" .metadata.generation))}}unhandled{{end}}{{end}}{{end}}`}}' || echo unknown
|
||||
kubectl --namespace "$1" get "$hr" "$2" --ignore-not-found --output go-template='{{`{{if .metadata.deletionTimestamp}}{{if .spec.suspend}}suspended{{else}}{{$held := false}}{{range .metadata.finalizers}}{{if eq . "finalizers.fluxcd.io"}}{{$held = true}}{{end}}{{end}}{{if and (not $held) (ne (printf "%v" .status.observedGeneration) (printf "%v" .metadata.generation))}}unhandled{{end}}{{end}}{{end}}`}}'
|
||||
}
|
||||
|
||||
# report_release <ref> <state>: remove the fence and name what that
|
||||
@@ -371,7 +372,7 @@ spec:
|
||||
echo "released $fence from $1 without an uninstall: helm-controller had not handled it" ;;
|
||||
suspended)
|
||||
unhandled=$((unhandled + 1))
|
||||
echo "released $fence from $1 without an uninstall: another manager holds its suspension" ;;
|
||||
echo "released $fence from $1 without an uninstall: it is still suspended, not by flux-plunger" ;;
|
||||
unknown)
|
||||
echo "released $fence from $1; whether helm-controller had handled it could not be read" ;;
|
||||
*)
|
||||
@@ -408,11 +409,15 @@ spec:
|
||||
echo "NOT released $ref: flux-plunger's suspension could not be lifted" >&2
|
||||
continue ;;
|
||||
esac
|
||||
state=$(read_state "$hns" "$name")
|
||||
if [ "$state" = unhandled ]; then
|
||||
pending="$pending $ref"
|
||||
continue
|
||||
fi
|
||||
# A release whose state cannot be read waits like one helm-controller
|
||||
# has not handled: unfencing it on a failed read could delete it
|
||||
# before its uninstall.
|
||||
state=$(retry read_state "$hns" "$name") || state=unknown
|
||||
case "$state" in
|
||||
unhandled|unknown)
|
||||
pending="$pending $ref"
|
||||
continue ;;
|
||||
esac
|
||||
report_release "$ref" "$state"
|
||||
done
|
||||
# The last fences go well before the window closes, so their
|
||||
@@ -422,19 +427,21 @@ spec:
|
||||
sleep 2
|
||||
still=
|
||||
for ref in $pending; do
|
||||
state=$(read_state "${ref%%/*}" "${ref#*/}")
|
||||
if [ "$state" = unhandled ]; then
|
||||
still="$still $ref"
|
||||
else
|
||||
begin_release
|
||||
report_release "$ref" "$state"
|
||||
fi
|
||||
state=$(read_state "${ref%%/*}" "${ref#*/}") || state=unknown
|
||||
case "$state" in
|
||||
unhandled|unknown)
|
||||
still="$still $ref" ;;
|
||||
*)
|
||||
begin_release
|
||||
report_release "$ref" "$state" ;;
|
||||
esac
|
||||
done
|
||||
pending=$still
|
||||
done
|
||||
for ref in $pending; do
|
||||
begin_release
|
||||
report_release "$ref" unhandled
|
||||
state=$(read_state "${ref%%/*}" "${ref#*/}") || state=unknown
|
||||
report_release "$ref" "$state"
|
||||
done
|
||||
stop_at=$retry_until
|
||||
|
||||
|
||||
Reference in new issue
Block a user