Ctrl K
ComunidadGuías…

hack/kubernetes-nodes-unpin-hook.bats

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bats
# -----------------------------------------------------------------------------
# Behavioral tests for the pre-delete unpin hook of the kubernetes-nodes chart
# (packages/apps/kubernetes-nodes/templates/pre-delete-unpin.yaml).
#
# helm-unittest (tests/pre_delete_unpin_test.yaml) pins the hook's YAML shape;
# this file tests the embedded shell script's BEHAVIOR: the rendered chart's Job
# args are extracted and executed against a fake kubectl, because the script's
# error classification cannot be asserted from the manifest. The contract under
# test: absence (--ignore-not-found success + empty output) and "present but
# unannotated" are the same no-op, ANY read or list failure fails the Job
# (set -e, retried by backoffLimit) whatever its message says -- treating a
# failure as absent would skip the unpin, report success, and leak the still
# keep-annotated worker objects past the uninstall -- and stderr warnings on
# successful reads never contaminate the annotation value.
#
# Requires helm (present in the unit-tests CI toolchain; the same job installs
# the helm-unittest plugin).
#
# For compatibility with the legacy cozytest.sh translator, tests avoid
# run/$status and file-local setup/teardown, and keep bare `}` lines out of test
# bodies. Assertions are direct shell tests.
# Run with: bats hack/kubernetes-nodes-unpin-hook.bats
# -----------------------------------------------------------------------------

load test_helper

FAKEBIN="$PWD/hack/testdata/kubernetes-nodes-unpin"
CHART="$PWD/packages/apps/kubernetes-nodes"

# prep renders the chart for pool md0 of cluster myk8s and extracts the unpin
# Job's script (the only `- |` block in the template) into $WORK/unpin.sh.
prep() {
  chmod +x "$FAKEBIN/kubectl"
  WORK=$(mktemp -d)
  export FAKE_CMDLOG="$WORK/cmdlog"
  : > "$FAKE_CMDLOG"
  # -s selects the output but the whole chart still renders, so the values must
  # clear nodegroup.yaml's offline hurdles the same way the snapshot suite does:
  # instanceType="" + explicit resources (lookup returns nil under `helm
  # template`), and a _cluster block (injected from cozystack-values at runtime).
  helm template kubernetes-nodes-myk8s-md0 "$CHART" --namespace tenant-test \
    --set cluster=myk8s \
    --set instanceType= --set resources.cpu=4 --set resources.memory=8Gi \
    --set _cluster.cluster-domain=cozy.local \
    -s templates/pre-delete-unpin.yaml > "$WORK/rendered.yaml" 2>"$WORK/helm-stderr"
  sed -n '/- |/,/securityContext:/p' "$WORK/rendered.yaml" |
    sed '1d;$d' | sed 's/^              //' > "$WORK/unpin.sh"
  # Extraction sanity: a template refactor that moves the script must fail here,
  # loudly, not silently test an empty file.
  grep -q 'removing helm.sh/resource-policy' "$WORK/unpin.sh"
  export PATH="$FAKEBIN:$PATH"
}

@test "unpin hook: absent objects are a no-op and the script succeeds (native pool preserved)" {
  prep
  export FAKE_MODE=absent
  rc=0
  sh "$WORK/unpin.sh" >"$WORK/out" 2>&1 || rc=$?
  cat "$WORK/out"
  [ "$rc" -eq 0 ]
  if grep -q 'annotate' "$FAKE_CMDLOG"; then echo "FAIL: no annotate must be issued when the objects are absent"; false; fi
  rm -rf "$WORK"
}

@test "unpin hook: a transient error reading an object fails the Job, not silently skipping the unpin" {
  prep
  # With --ignore-not-found, absence is success+empty, so a non-zero get can
  # only be a real failure; reading it as "absent" would skip the unpin,
  # succeed, and the uninstall would leave the keep-annotated MachineDeployment
  # (and its worker VMs) running unmanaged in the tenant namespace. The KMT
  # list query succeeds in this mode, so a failure proves the NAMED-get path.
  export FAKE_MODE=error-named
  rc=0
  sh "$WORK/unpin.sh" >"$WORK/out" 2>&1 || rc=$?
  cat "$WORK/out"
  [ "$rc" -ne 0 ]
  grep -q 'request timed out' "$WORK/out"
  if grep -q 'annotate' "$FAKE_CMDLOG"; then echo "FAIL: no annotate must be issued after a read failure"; false; fi
  rm -rf "$WORK"
}

@test "unpin hook: a read failure whose message contains 'not found' still fails the Job" {
  prep
  # A conversion-webhook outage fails a get with '... service
  # "capi-webhook-service" not found'. That is a failure, not absence; a
  # text-match on "not found" would classify it as absent and leak the pinned
  # objects. Only --ignore-not-found's success+empty means absent.
  export FAKE_MODE=error-named-notfoundish
  rc=0
  sh "$WORK/unpin.sh" >"$WORK/out" 2>&1 || rc=$?
  cat "$WORK/out"
  [ "$rc" -ne 0 ]
  if grep -q 'annotate' "$FAKE_CMDLOG"; then echo "FAIL: no annotate must be issued after a read failure"; false; fi
  rm -rf "$WORK"
}

@test "unpin hook: a failed KMT listing fails the Job, not reading as zero templates" {
  prep
  # The named objects read clean (unpinned) so a failure proves the LIST path:
  # a listing failure masked as empty would skip every KMT unpin, succeed, and
  # leak the keep-annotated templates past the uninstall.
  export FAKE_MODE=unpinned FAKE_LIST_FAIL=1
  rc=0
  sh "$WORK/unpin.sh" >"$WORK/out" 2>&1 || rc=$?
  cat "$WORK/out"
  [ "$rc" -ne 0 ]
  if grep -q 'annotate' "$FAKE_CMDLOG"; then echo "FAIL: no annotate must be issued after a failed KMT listing"; false; fi
  unset FAKE_LIST_FAIL
  rm -rf "$WORK"
}

@test "unpin hook: a kubectl warning on stderr does not fake a pinned state" {
  prep
  # kubectl warns on SUCCESSFUL reads (API deprecation -- deterministic for a
  # deprecated GVK -- and client-side throttling). If the annotation read
  # captured stderr into its value, an unannotated object would read as
  # "pinned" and get a spurious annotate call.
  export FAKE_MODE=unpinned FAKE_WARN_STDERR=1
  rc=0
  sh "$WORK/unpin.sh" >"$WORK/out" 2>&1 || rc=$?
  cat "$WORK/out"
  [ "$rc" -eq 0 ]
  if grep -q 'annotate' "$FAKE_CMDLOG"; then echo "FAIL: a warning on stderr must not be read as a pinned object"; false; fi
  unset FAKE_WARN_STDERR
  rm -rf "$WORK"
}

@test "unpin hook: keep-annotated objects are unpinned, hash-anchored to this pool's own KMTs" {
  prep
  export FAKE_MODE=pinned
  FAKE_KMT_NAMES=$(printf '%s\n%s' kubernetes-myk8s-md0-abc123 kubernetes-myk8s-md0-large-def456)
  export FAKE_KMT_NAMES
  rc=0
  sh "$WORK/unpin.sh" >"$WORK/out" 2>&1 || rc=$?
  cat "$WORK/out"
  [ "$rc" -eq 0 ]
  # All three named objects and the pool's own KMT get the annotation removed...
  grep -q 'annotate .*machinedeployment.cluster.x-k8s.io/kubernetes-myk8s-md0 helm.sh/resource-policy-' "$FAKE_CMDLOG"
  grep -q 'annotate .*machinehealthcheck.cluster.x-k8s.io/kubernetes-myk8s-md0 helm.sh/resource-policy-' "$FAKE_CMDLOG"
  grep -q 'annotate .*workloadmonitor.cozystack.io/kubernetes-myk8s-md0 helm.sh/resource-policy-' "$FAKE_CMDLOG"
  grep -q 'annotate .*kubevirtmachinetemplate.infrastructure.cluster.x-k8s.io/kubernetes-myk8s-md0-abc123 helm.sh/resource-policy-' "$FAKE_CMDLOG"
  # ...but the sibling pool md0-large's KMT is left alone (6-hex anchor).
  if grep -q 'md0-large-def456' "$FAKE_CMDLOG"; then echo "FAIL: the sibling pool's KubevirtMachineTemplate must not be unpinned"; false; fi
  rm -rf "$WORK"
}

@test "unpin hook: an existing object without the keep annotation is left untouched" {
  prep
  export FAKE_MODE=unpinned
  rc=0
  sh "$WORK/unpin.sh" >"$WORK/out" 2>&1 || rc=$?
  cat "$WORK/out"
  [ "$rc" -eq 0 ]
  if grep -q 'annotate' "$FAKE_CMDLOG"; then echo "FAIL: an object without the keep annotation must not be unpinned"; false; fi
  rm -rf "$WORK"
}