Files
Timofei Larkin a54d5ca4a5 fix(kubernetes-nodes): unpin absence via --ignore-not-found
Same two holes as the migration's read classification, in the unpin
hook. Capturing stderr into $pol (2>&1) contaminated the success path:
kubectl warns on successful reads, so an unannotated object read as
"pinned" and drew a spurious annotate call -- one per named object.
And the *"not found"* pattern classified a transient conversion-webhook
failure as absence, letting the Job report success while the
keep-annotated objects leaked past the uninstall.

--ignore-not-found makes the whole classification unnecessary here:
absence folds into the same empty-output no-op branch as "present but
unannotated", which is exactly the semantics unpin() wants, and any
non-zero exit is a real failure that set -e turns into a failed Job.
The case/esac disappears; stderr stays on the Job log where it is
visible instead of parsed. This also avoids tempfile juggling under the
container's readOnlyRootFilesystem.

The bats suite gains a previously missing failure-path case and
regression tests for both holes, red against the previous commit: a
failed KMT listing must fail the Job (FAKE_LIST_FAIL), a read failure
whose message contains "not found" must fail the Job, and a
success-path stderr warning must not fake a pinned state
(FAKE_WARN_STDERR). Absence is now modeled the way real kubectl
behaves under --ignore-not-found: success with empty output.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2026-07-25 02:07:43 +03:00

44 lines
2.2 KiB
Bash
Executable File

#!/bin/sh
# Fake kubectl for hack/kubernetes-nodes-unpin-hook.bats. Mocks the apiserver
# boundary of the pre-delete unpin hook's embedded script. Env:
# FAKE_MODE named-object get behavior:
# absent success, empty output (the hook passes
# --ignore-not-found, so absence is exit 0)
# error-named transient server error exit 1 (list still ok)
# error-named-notfoundish
# exit 1 with a conversion-webhook message whose
# text contains "not found" -- still a FAILURE
# pinned jsonpath read returns "keep"
# unpinned jsonpath read returns empty, exit 0
# FAKE_LIST_FAIL when set, the KMT list query fails with a transient error
# FAKE_WARN_STDERR
# when set, successful reads print a deprecation warning to
# stderr first, the way real kubectl does
# FAKE_KMT_NAMES newline-separated names the KMT list query returns
# FAKE_CMDLOG path to append annotate calls to
log() { [ -n "${FAKE_CMDLOG:-}" ] && echo "$*" >> "$FAKE_CMDLOG"; return 0; }
warn() { [ -n "${FAKE_WARN_STDERR:-}" ] && echo "Warning: cluster.x-k8s.io/v1beta1 MachineDeployment is deprecated" >&2; return 0; }
args="$*"
mode="${FAKE_MODE:-absent}"
case "$args" in
*" annotate "*)
log "annotate $args"; exit 0 ;;
*"get kubevirtmachinetemplate.infrastructure.cluster.x-k8s.io -o"*)
# list query (no /name)
if [ -n "${FAKE_LIST_FAIL:-}" ]; then
echo "Error from server: etcdserver: request timed out" >&2; exit 1
fi
warn
[ -n "${FAKE_KMT_NAMES:-}" ] && printf '%s\n' "$FAKE_KMT_NAMES"
exit 0 ;;
esac
# named-object get
case "$mode" in
pinned) warn; printf 'keep'; exit 0 ;;
unpinned) warn; exit 0 ;;
error-named) echo "Error from server: etcdserver: request timed out" >&2; exit 1 ;;
error-named-notfoundish)
echo 'Error from server: conversion webhook for cluster.x-k8s.io/v1beta1, Kind=MachineDeployment failed: service "capi-webhook-service" not found' >&2; exit 1 ;;
*) warn; exit 0 ;; # absent: --ignore-not-found -> success, empty
esac