#!/usr/bin/env bash # Unit tests for etc/init_cloudnative_pg.sh image policy behavior: # - no-op when desired image already deployed # - fail early on version mismatch without --upgrade (before namespace creation) # - on --upgrade + mismatch, trigger a rollout/restart hook set -euo pipefail SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) REPO_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd) SCRIPT_UNDER_TEST="$REPO_ROOT/etc/init_cloudnative_pg.sh" TMP_DIR=$(mktemp -d) trap 'rm -rf "$TMP_DIR"' EXIT BIN_DIR="$TMP_DIR/bin" mkdir -p "$BIN_DIR" MOCK_LOG="$TMP_DIR/mock_calls.log" cat <<'K_EOF' > "$BIN_DIR/kubectl" #!/usr/bin/env bash set -euo pipefail echo "Mocked kubectl called with $@" >> "${MOCK_LOG:-/tmp/mock_calls.log}" args="$*" if [[ "$args" == *"get --raw=/readyz"* || "$args" == *"get --raw='/readyz'"* || "$args" == *"get --raw=\"/readyz\""* ]]; then echo "ok" exit 0 fi if [[ "$args" == *"version --short"* ]]; then echo "Client Version: v0.0.0" echo "Server Version: v0.0.0" exit 0 fi # CNPG webhook wait: return an endpoint IP so the wait loop passes. if [[ "$args" == *"get endpoints"*"cnpg-webhook-service"* ]]; then echo "10.42.0.10" exit 0 fi if [[ "$args" == *"get endpoints barman-cloud"* ]]; then echo "10.42.0.11" exit 0 fi # Barman plugin service registration: return a service name for label-based lookups. if [[ "$args" == *"get svc"*"-l cnpg.io/pluginName=barman-cloud.cloudnative-pg.io"*"-o jsonpath="* ]]; then echo "barman-cloud" exit 0 fi if [[ "$args" == *"get svc barman-cloud"*"-o jsonpath="*".metadata.name"* ]]; then echo "barman-cloud" exit 0 fi if [[ "$args" == *"get svc barman-cloud"*"-o jsonpath="*"pluginClientSecret"* ]]; then echo "barman-cloud-client-tls" exit 0 fi if [[ "$args" == *"get svc barman-cloud"*"-o jsonpath="*"pluginServerSecret"* ]]; then echo "barman-cloud-server-tls" exit 0 fi if [[ "$args" == *"get svc barman-cloud"*"-o jsonpath="*"pluginPort"* ]]; then echo "443" exit 0 fi if [[ "$args" == *"get namespace"* ]]; then exit 0 fi if [[ "$args" == *"create namespace"* ]]; then exit 0 fi if [[ "$args" == *"get cluster"*"-o jsonpath="*".spec.imageName"* ]]; then echo "${MOCK_CLUSTER_IMAGE:-}" exit 0 fi if [[ "$args" == *"get cluster"* ]]; then exit 0 fi if [[ "$args" == *"patch cluster"* ]]; then exit 0 fi if [[ "$args" == *"cnpg version"* ]]; then exit 0 fi if [[ "$args" == *"cnpg restart"* ]]; then exit 0 fi if [[ "$args" == *"apply"* ]]; then exit 0 fi if [[ "$args" == *"get secret"* ]]; then exit 0 fi exit 0 K_EOF chmod +x "$BIN_DIR/kubectl" mock_ok_tool() { cat < "$BIN_DIR/$1" #!/usr/bin/env bash echo "Mocked $1 called with \$@" >> "${MOCK_LOG:-/tmp/mock_calls.log}" exit 0 M_EOF chmod +x "$BIN_DIR/$1" } # Prevent accidental real calls during tests. mock_ok_tool docker mock_ok_tool k3d mock_ok_tool curl mock_ok_tool jq mock_ok_tool skopeo export PATH="$BIN_DIR:$PATH" export MOCK_LOG CONF_DIR="$TMP_DIR/conf" mkdir -p "$CONF_DIR" SERVICE_DIR="$TMP_DIR/service" mkdir -p "$SERVICE_DIR/secrets" printf '%s' "dummy-private-key" > "$SERVICE_DIR/secrets/admin.key" printf '%s' "dummy-public-key" > "$SERVICE_DIR/secrets/admin.pub" write_cfg() { local prole_home_val="$1" cat < "$CONF_DIR/prole.cfg" [User] NAMESPACE = test-ns SERVICE_NAMESPACE = test-system PROLE_HOME = ${prole_home_val} PROLE_SERVICE = ${SERVICE_DIR} [Global] DEPLOYMENT_MODE = k3d [Docker Build] LOCAL_REGISTRY = localhost:5000 LOCAL_REGISTRY_INTERNAL = k3d-prole-registry:5000 C_EOF } reset_log() { : > "$MOCK_LOG" } assert_not_called() { local needle="$1" if grep -q "$needle" "$MOCK_LOG"; then echo "FAILURE: expected not to see '$needle' in mock log" sed -n '1,200p' "$MOCK_LOG" || true exit 1 fi } assert_called() { local needle="$1" if ! grep -q "$needle" "$MOCK_LOG"; then echo "FAILURE: expected to see '$needle' in mock log" sed -n '1,200p' "$MOCK_LOG" || true exit 1 fi } run_case() { local name="$1"; shift local expected_rc="$1"; shift reset_log local out="$TMP_DIR/${name}.out" local err="$TMP_DIR/${name}.err" set +e "$@" >"$out" 2>"$err" local rc=$? set -e if [[ "$rc" != "$expected_rc" ]]; then echo "FAILURE: $name expected RC=$expected_rc got RC=$rc" echo "--- stdout ---" sed -n '1,200p' "$out" || true echo "--- stderr ---" sed -n '1,200p' "$err" || true echo "--- mock log ---" sed -n '1,200p' "$MOCK_LOG" || true exit 1 fi } ############################################### # Case 1: no-op on same image (initialize) ############################################### export PROLE_CONF="$CONF_DIR" export PROLE_SERVICE="$SERVICE_DIR" write_cfg "$REPO_ROOT" export MOCK_CLUSTER_IMAGE="k3d-prole-registry:5000/knoe-db:18-140" run_case "noop_initialize" 0 \ bash "$SCRIPT_UNDER_TEST" --mode k3d --namespace test-ns initialize knoe-db 18-140 assert_not_called "create namespace" ############################################### # Case 2: mismatch without --upgrade fails early ############################################### export MOCK_CLUSTER_IMAGE="k3d-prole-registry:5000/knoe-db:18-138" run_case "mismatch_initialize" 1 \ bash "$SCRIPT_UNDER_TEST" --mode k3d --namespace test-ns initialize knoe-db 18-140 assert_not_called "create namespace" ############################################### # Case 3: mismatch with --upgrade triggers restart hook (deploy) ############################################### PROLE_HOME_UPGRADE="$TMP_DIR/prole_home" mkdir -p "$PROLE_HOME_UPGRADE/deploy/opentofu/k3s/manifests/prole" cat <<'Y_EOF' > "$PROLE_HOME_UPGRADE/deploy/opentofu/k3s/manifests/prole/knoe-db.yaml" apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: knoe-db spec: instances: 1 imageName: knoe-db:dummy Y_EOF cat <<'Y_EOF' > "$PROLE_HOME_UPGRADE/deploy/opentofu/k3s/manifests/prole/knoe-db-barman-objectstore.yaml" apiVersion: postgresql.cnpg.io/v1 kind: ObjectStore metadata: name: knoe-db spec: {} Y_EOF write_cfg "$PROLE_HOME_UPGRADE" export MOCK_CLUSTER_IMAGE="k3d-prole-registry:5000/knoe-db:18-138" run_case "upgrade_deploy" 0 \ bash "$SCRIPT_UNDER_TEST" --mode k3d --namespace test-ns --upgrade deploy 18-140 assert_called "cnpg restart" echo "SUCCESS"