Ctrl K
ComunidadGuías…

hack/run-migrations.bats

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

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bats
# -----------------------------------------------------------------------------
# Unit tests for packages/core/platform/images/migrations/run-migrations.sh.
#
# The migration hook Job retries a failed pod with the environment rendered for
# the first one, so CURRENT_VERSION is what the cluster was on before any
# migration of this upgrade ran. Every migration that finished has stamped the
# cozystack-version ConfigMap since, and a retried pod must start from that
# stamp, or it runs finished migrations a second time.
#
# The runner executes in the migrations image's alpine base (busybox ash, the
# interpreter it ships with) against stand-in migrations and a fake kubectl
# that keeps the stamped version in a file, so one pod's stamps are what the
# next pod reads. The harness shape is hack/migration-58-http-cache-freeze.bats's.
#
# Run with: bats hack/run-migrations.bats from the repo root.
# -----------------------------------------------------------------------------

load test_helper

FAKEBIN="$PWD/hack/testdata/run-migrations"
RUNNER="$PWD/packages/core/platform/images/migrations/run-migrations.sh"
LIB="$PWD/packages/core/platform/images/migrations/migrations/lib"
ALPINE=$(sed -n 's/^FROM \(alpine:[^ ]*\).*$/\1/p' \
  "$PWD/packages/core/platform/images/migrations/Dockerfile" | head -1)
WORKROOT="${TMPDIR:-/tmp}/cozy-run-migrations-$$"

cozy_cleanup() {
  rm -rf "$WORKROOT"
  return 0
}

# prep <stamped version, or "none" for no ConfigMap>
prep() {
  docker info >/dev/null 2>&1 || {
    echo "docker is required: these tests run run-migrations.sh inside $ALPINE," >&2
    echo "the migrations image's base." >&2
    return 1
  }
  mkdir -p "$WORKROOT"
  WORK=$(mktemp -d "$WORKROOT/XXXXXX")
  mkdir -p "$WORK/migrations"
  cp -R "$LIB" "$WORK/migrations/lib"
  for n in 57 58 59; do
    cp "$FAKEBIN/migration" "$WORK/migrations/$n"
  done
  : > "$WORK/ran"
  [ "$1" = none ] || printf '%s' "$1" > "$WORK/state"
  chmod +x "$FAKEBIN/kubectl"
  unset FAKE_FAIL_AT FAKE_READ_FAIL FAKE_EXIST_FAIL || true
  return 0
}

# run_pod <CURRENT_VERSION> <TARGET_VERSION> -- one pod of the hook Job.
run_pod() {
  _run_pod_rc=0
  docker run --rm --network none \
    --user "$(id -u):$(id -g)" \
    -v "$RUNNER:/usr/bin/run-migrations.sh:ro" \
    -v "$WORK/migrations:/migrations" \
    -v "$FAKEBIN:/fakebin:ro" \
    -v "$WORK:/work" \
    -e PATH=/fakebin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
    -e FAKE_STATE=/work/state \
    -e FAKE_FAIL_AT="${FAKE_FAIL_AT-}" \
    -e FAKE_READ_FAIL="${FAKE_READ_FAIL-}" \
    -e FAKE_EXIST_FAIL="${FAKE_EXIST_FAIL-}" \
    -e NAMESPACE=cozy-system \
    -e CURRENT_VERSION="$1" \
    -e TARGET_VERSION="$2" \
    "$ALPINE" /bin/sh /usr/bin/run-migrations.sh || _run_pod_rc=$?
  return "$_run_pod_rc"
}

ran() {
  tr '\n' ' ' < "$WORK/ran" | sed 's/ $//'
  return 0
}

@test "a retried pod starts from the version the failed pod stamped" {
  prep 57
  export FAKE_FAIL_AT=59
  rc=0
  run_pod 57 60 || rc=$?
  [ "$rc" -ne 0 ]
  [ "$(cat "$WORK/state")" = 59 ]

  unset FAKE_FAIL_AT
  rc=0
  run_pod 57 60 || rc=$?
  echo "ran: $(ran)"
  [ "$rc" -eq 0 ]
  [ "$(ran)" = "57 58 59 59" ]
  [ "$(cat "$WORK/state")" = 60 ]
  rm -rf "$WORK"
}

@test "a pod that finds the target already stamped runs nothing" {
  prep 60
  rc=0
  run_pod 57 60 || rc=$?
  echo "ran: $(ran)"
  [ "$rc" -eq 0 ]
  [ -z "$(ran)" ]
  rm -rf "$WORK"
}

@test "a ConfigMap with no version falls back to the rendered one" {
  prep ""
  rc=0
  run_pod 58 60 || rc=$?
  echo "ran: $(ran)"
  [ "$rc" -eq 0 ]
  [ "$(ran)" = "58 59" ]
  rm -rf "$WORK"
}

# A non-integer start fails the -ge test inside an if and makes seq fail
# inside the for list, and neither trips set -e: without the fallback such a
# stamp gives a green Job that ran nothing and moved no stamp.
@test "a ConfigMap with a non-integer version falls back to the rendered one" {
  prep v58
  rc=0
  run_pod 58 60 || rc=$?
  echo "ran: $(ran)"
  [ "$rc" -eq 0 ]
  [ "$(ran)" = "58 59" ]
  rm -rf "$WORK"
}

# The same unguarded -ge and empty seq turn an all-digit stamp past the shell's
# integer range into a green Job that ran nothing. Falling back is no answer:
# the chart's int renders such a stamp as 0, which would replay every migration.
@test "a stamped version outside the shell's integer range stops before any migration" {
  prep 999999999999999999999999999999
  rc=0
  run_pod 0 60 || rc=$?
  echo "ran: $(ran)"
  [ "$rc" -ne 0 ]
  [ -z "$(ran)" ]
  rm -rf "$WORK"
}

@test "a failed read of the stamped version stops before any migration" {
  prep 58
  export FAKE_READ_FAIL="Error from server (Timeout): the server was unable to return a response in the time allotted"
  rc=0
  run_pod 57 60 || rc=$?
  echo "ran: $(ran)"
  [ "$rc" -ne 0 ]
  [ -z "$(ran)" ]
  [ "$(cat "$WORK/state")" = 58 ]
  rm -rf "$WORK"
}

# Only NotFound means a fresh cluster. Reading any other failure of the
# existence check as absence stamps the target over a cluster whose
# migrations never ran, and the Job ends green.
@test "a failed existence check stops before stamping or running anything" {
  prep 57
  export FAKE_EXIST_FAIL="Unable to connect to the server: net/http: TLS handshake timeout"
  rc=0
  run_pod 57 60 || rc=$?
  echo "ran: $(ran)"
  [ "$rc" -ne 0 ]
  [ -z "$(ran)" ]
  [ "$(cat "$WORK/state")" = 57 ]
  rm -rf "$WORK"
}

@test "with no ConfigMap it stamps the target and runs nothing" {
  prep none
  rc=0
  run_pod 57 60 || rc=$?
  echo "ran: $(ran)"
  [ "$rc" -eq 0 ]
  [ -z "$(ran)" ]
  [ "$(cat "$WORK/state")" = 60 ]
  rm -rf "$WORK"
}