#!/usr/bin/env bash # T50: "DNS refresh != liveness check" - verify that a STRICT_DNS-resolved (ServiceEntry # resolution:DNS) upstream to a dead port keeps being reported healthy under repeated # connection failures until DestinationRule.outlierDetection is added, at which point # ejection (FAILED_OUTLIER_CHECK) happens within seconds. # # NOTE: curl MUST be exec'd in the "curl" app container (uid=100), NOT "-c istio-proxy" # (uid=1337). Istio's default iptables interception excludes traffic originating from the # envoy/istio-proxy UID (to avoid the sidecar looping into itself); curl run inside the # istio-proxy container bypasses the mesh entirely and hits a silent kube-proxy/kernel-level # blackhole for a ClusterIP:port with no matching Service port, which never touches Envoy's # cluster stats at all. set -euo pipefail NS=istio-vt-t50 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 "--- create namespace ---" kubectl create namespace "${NS}" kubectl label namespace "${NS}" istio-injection=enabled echo "--- apply manifest (client pod, echo deploy/svc, mock-deadport ServiceEntry) ---" kubectl apply -f "${DIR}/manifest.yaml" echo "--- wait for workloads ready ---" kubectl -n "${NS}" wait --for=condition=Ready pod/client --timeout=90s kubectl -n "${NS}" rollout status deployment/echo --timeout=90s echo "" echo "=== PHASE 1: no outlierDetection - expect endpoint to stay healthy despite failures ===" for i in $(seq 1 10); do kubectl -n "${NS}" exec client -c curl -- curl -s -o /dev/null -w 'try %{http_code}\n' \ --max-time 3 http://mock.istio-verify-ext.svc.homelab.local:8199/ || true sleep 1 done echo "--- phase1 /clusters (expect health_flags::healthy) ---" kubectl -n "${NS}" exec client -c istio-proxy -- curl -s 'localhost:15000/clusters' \ | grep 'outbound|8199||mock.istio-verify-ext.svc.homelab.local' echo "" echo "=== apply DestinationRule with outlierDetection ===" kubectl apply -f - <<'YAML' apiVersion: networking.istio.io/v1 kind: DestinationRule metadata: name: mock-deadport-dr namespace: istio-vt-t50 spec: host: mock.istio-verify-ext.svc.homelab.local trafficPolicy: connectionPool: tcp: connectTimeout: 1s outlierDetection: splitExternalLocalOriginErrors: true consecutiveLocalOriginFailures: 3 interval: 5s baseEjectionTime: 15s maxEjectionPercent: 100 YAML sleep 3 # allow config propagation to the sidecar echo "" echo "=== PHASE 2: outlierDetection active - expect ejection within a handful of requests ===" for i in $(seq 1 6); do kubectl -n "${NS}" exec client -c curl -- curl -s -o /dev/null -w '%{http_code}\n' \ --max-time 3 http://mock.istio-verify-ext.svc.homelab.local:8199/ || true sleep 1 done echo "--- phase2 /clusters (expect health_flags::/failed_outlier_check) ---" kubectl -n "${NS}" exec client -c istio-proxy -- curl -s 'localhost:15000/clusters' \ | grep 'outbound|8199||mock.istio-verify-ext.svc.homelab.local' echo "" echo "=== done (namespace ${NS} will be deleted by cleanup trap) ==="