#!/bin/sh # Fake kubectl for the migration-56 unit test (hack/migration-56-adopt-values.bats). # Mocks only the cluster boundary. Env: # FAKE_HR_LIST path to the `get helmrelease -o json` list fixture # FAKE_CHILD_HR path to write the child HelmRelease that `apply -f -` receives # FAKE_CMDLOG path to append a call log (APPLY-HR / STAMP / annotate / label) # FAKE_GET_FAIL substring: any `get` whose args match it fails with a # transient server error (message overridable via # FAKE_GET_FAIL_MSG, e.g. a conversion-webhook failure whose # text happens to contain "not found"), so the migration's # absence-vs-failure classification is exercised # FAKE_WARN_STDERR when set, every successful get prints a deprecation # warning to stderr first, the way real kubectl does -- data # captured with 2>&1 would be contaminated by it # Absence honors --ignore-not-found the way real kubectl does: with the flag a # missing object is SUCCESS with empty output; without it, "Error from server # (NotFound)" on stderr and exit 1. log() { [ -n "${FAKE_CMDLOG:-}" ] && echo "$*" >> "$FAKE_CMDLOG"; return 0; } notfound() { echo "Error from server (NotFound): $1 not found" >&2; exit 1; } absent() { case "$args" in *--ignore-not-found*) exit 0 ;; esac notfound "$1" } verb=${1:-} shift 2>/dev/null || true args="$*" case "$verb" in get) if [ -n "${FAKE_GET_FAIL:-}" ]; then case "$args" in *"${FAKE_GET_FAIL}"*) echo "${FAKE_GET_FAIL_MSG:-Error from server: etcdserver: request timed out}" >&2; exit 1 ;; esac fi if [ -n "${FAKE_WARN_STDERR:-}" ]; then echo "Warning: cluster.x-k8s.io/v1beta1 MachineDeployment is deprecated" >&2 fi case "$args" in *helmrelease*--all-namespaces*) cat "$FAKE_HR_LIST"; exit 0 ;; *helmrelease*kubernetes-nodes-*) notfound "helmreleases.helm.toolkit.fluxcd.io" ;; # child HR absent -> create path esac # tokenize: $1=resource, $2=object-name (or a flag for list queries) # shellcheck disable=SC2086 set -- $args res=$1; name=$2 # Real kubectl rejects a name containing '/' client-side, before any API # call, deterministically -- reproduce that so the migration provably never # issues a read for a composed name that cannot exist. case "$name" in */*) echo "error: there is no need to specify a resource type as a separate argument when passing arguments in resource/name form" >&2 exit 1 ;; esac case "$res" in kubevirtmachinetemplate*) case "$name" in -*|"") # KMT range/list query [ -n "${FAKE_KMT_NAMES:-}" ] && printf '%s\n' "$FAKE_KMT_NAMES" exit 0 ;; esac ;; esac # Named-object get. When FAKE_OBJS is set (adoption-path tests), look the # object up there: a matching line " " means present. # Without FAKE_OBJS every named get is "absent" (value-mapping tests rely on # adopt_one short-circuiting on absent). if [ -n "${FAKE_OBJS:-}" ] && [ -f "$FAKE_OBJS" ]; then line=$(grep -E "^${res}[[:space:]]+${name}([[:space:]]|$)" "$FAKE_OBJS" || true) [ -z "$line" ] && absent "${res} \"${name}\"" # absent rel=$(printf '%s\n' "$line" | awk '{print $3}') case "$args" in *jsonpath*release-name*) printf '%s' "$rel"; exit 0 ;; *" -o json"*) printf '{"metadata":{"annotations":{"meta.helm.sh/release-name":"%s"}}}' "$rel"; exit 0 ;; *" -o name"*) printf '%s/%s\n' "$res" "$name"; exit 0 ;; *) exit 0 ;; # existence check esac fi absent "${res} \"${name}\"" ;; # default: absent apply) payload=$(cat) if printf '%s' "$payload" | grep -q 'HelmRelease'; then # Reject an RFC-1123-invalid metadata.name the way the real apiserver would, # so the migration's name-validity guard is exercised: without it the script # would reach this apply and exit non-zero, deadlocking the upgrade. hrname=$(printf '%s' "$payload" | jq -r '.metadata.name // ""' 2>/dev/null) if ! printf '%s' "$hrname" | grep -qE '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'; then echo "The HelmRelease \"$hrname\" is invalid: metadata.name: Invalid value: \"$hrname\": a lowercase RFC 1123 label is required" >&2 exit 1 fi printf '%s' "$payload" > "$FAKE_CHILD_HR"; log "APPLY-HR" elif printf '%s' "$payload" | grep -q 'ConfigMap'; then if printf '%s' "$payload" | grep -q 'cozystack-migration-56-unadopted'; then # FAKE_UNADOPTED_CM_FAIL exercises the best-effort path: a permanent # failure publishing the record must NOT abort the migration. if [ -n "${FAKE_UNADOPTED_CM_FAIL:-}" ]; then echo "Error from server: ConfigMap \"cozystack-migration-56-unadopted\" is invalid: exceeds object size limit" >&2 exit 1 fi log "APPLY-UNADOPTED-CM" else log "STAMP" fi else log "APPLY-OTHER" fi exit 0 ;; annotate) log "annotate $args"; exit 0 ;; label) log "label $args"; exit 0 ;; create) # Only `create configmap cozystack-migration-56-unadopted --from-file ... # --dry-run=client -o yaml` is used; emit a minimal ConfigMap manifest so the # piped `apply --filename -` receives it and logs APPLY-UNADOPTED-CM. case "$args" in *configmap*cozystack-migration-56-unadopted*) printf 'apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: cozystack-migration-56-unadopted\n' ;; esac exit 0 ;; *) exit 0 ;; esac