#!/usr/bin/env bash # T21: EDS warming + atomic swap on scale 1->3 (hitless, no sidecar restart) set -euo pipefail NS=istio-vt-t21 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cleanup() { echo "[cleanup] deleting namespace ${NS}" kubectl delete namespace "${NS}" --wait=false --ignore-not-found } trap cleanup EXIT echo "[1/7] create namespace ${NS} (istio-injection=enabled)" kubectl create namespace "${NS}" kubectl label namespace "${NS}" istio-injection=enabled echo "[2/7] apply manifest" kubectl apply -f "${SCRIPT_DIR}/manifest.yaml" echo "[3/7] wait for client Ready and echo rollout" kubectl -n "${NS}" wait --for=condition=Ready pod/client --timeout=90s kubectl -n "${NS}" rollout status deploy/echo --timeout=90s CLIENT_START=$(kubectl -n "${NS}" get pod client -o jsonpath='{.status.startTime}') echo "[4/7] CLIENT_START=${CLIENT_START}" echo "[5/7] sanity curl" kubectl -n "${NS}" exec client -c curl -- curl -s -o /dev/null -w "sanity_http_code=%{http_code}\n" --max-time 3 \ "http://echo.${NS}.svc.homelab.local/" echo "[6/7] start warm curl loop (local redirect, runs for duration of exec) then scale to 3" # NOTE: must be launched as a true background OS process (nohup/disown or the # harness's own background-job facility). A naive "( cmd & )" inside a shell # that itself exits immediately after the tool call returns WILL be killed # before it can run - confirmed empirically in this test run. kubectl -n "${NS}" exec client -c curl -- sh -c \ 'for i in $(seq 1 100); do curl -s -o /dev/null -w "%{http_code}\n" --max-time 2 http://echo.'"${NS}"'.svc.homelab.local/; sleep 0.1; done' \ > "${SCRIPT_DIR}/warm_curl.log" 2>"${SCRIPT_DIR}/warm_curl.err" & WARM_PID=$! sleep 1 kubectl -n "${NS}" scale deploy/echo --replicas=3 echo "[7/7] poll /clusters for our namespace's endpoint count during transition (10x, 1s)" for i in $(seq 1 10); do cnt=$(kubectl -n "${NS}" exec client -c istio-proxy -- curl -s localhost:15000/clusters 2>/dev/null \ | grep "outbound|80||echo.${NS}.svc.cluster.local" \ | grep -oE '10\.[0-9]+\.[0-9]+\.[0-9]+' | sort -u | wc -l) echo "poll $i: our_ns_endpoint_count=${cnt}" sleep 1 done kubectl -n "${NS}" rollout status deploy/echo --timeout=60s wait "${WARM_PID}" || true echo "=== warm_curl.log result (expect ONLY 200s) ===" sort "${SCRIPT_DIR}/warm_curl.log" | uniq -c echo "=== client startTime after scale (compare to ${CLIENT_START}, must be unchanged) ===" kubectl -n "${NS}" get pod client -o jsonpath='{.status.startTime}' echo echo "=== final endpoint count (istioctl) ===" # NOTE: Istio's internal cluster name suffix is svc.cluster.local even though # this cluster's actual DNS domain is homelab.local (see harness-notes.md) - # use the cluster.local-suffixed name here, not the svc.homelab.local FQDN. istioctl proxy-config endpoint "client.${NS}" --cluster "outbound|80||echo.${NS}.svc.cluster.local"