#!/usr/bin/env bash # T03 - exportTo 없는(전역) 깨진 리소스 하나가 물리적으로 분리된 두 게이트웨이를 # 동시에 NACK시키는 xDS 트랜잭션성/스코핑 누수 - reproducible test script set -euo pipefail NS="istio-vt-t03" 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 "=== create namespace ${NS} (istio-injection=enabled) ===" kubectl create namespace "${NS}" kubectl label namespace "${NS}" istio-injection=enabled echo "=== apply manifest ===" kubectl apply -f "${SCRIPT_DIR}/manifest.yaml" echo "=== wait for workloads ===" kubectl -n "${NS}" wait --for=condition=Available deploy/egw-a deploy/egw-b deploy/multi-ip-backend --timeout=120s kubectl -n "${NS}" wait --for=condition=Available deploy/echo --timeout=120s kubectl -n "${NS}" wait --for=condition=Ready pod/client --timeout=120s kubectl -n "${NS}" get pods -o wide # NOTE: baseline traffic checks may need a retry - right after apply, xDS # (RDS/CDS for the new Gateway/VirtualService) can take several seconds to # propagate to egw-a/egw-b; a curl attempted too early returns exit 35 # (TLS connection reset, envoy access log "NC"/no-cluster) even though this # is not related to the leaky-nack-demo ServiceEntry under test. retry_curl() { local desc="$1"; shift local out for i in 1 2; do if out=$(kubectl -n "${NS}" exec client -c curl -- curl -sk -o /dev/null -w "${desc}=%{http_code}\n" "$@" 2>&1); then echo "$out"; return 0 fi echo "$out (attempt $i failed, retrying after 10s)" sleep 10 done echo "$out" } echo "=== baseline: client -> egw-a / egw-b ===" retry_curl via_egwA_baseline --connect-to mock.istio-verify-ext.svc.homelab.local:443:egw-a."${NS}".svc.homelab.local:8443 https://mock.istio-verify-ext.svc.homelab.local/ retry_curl via_egwB_baseline --connect-to mock.istio-verify-ext.svc.homelab.local:443:egw-b."${NS}".svc.homelab.local:8444 https://mock.istio-verify-ext.svc.homelab.local/ echo "=== baseline proxy-status ===" istioctl proxy-status | grep -E 'NAME|egw-a|egw-b' echo "=== confirm multi-ip-target endpoints (2 A records expected) ===" kubectl -n "${NS}" get endpoints multi-ip-target -o jsonpath='{.subsets[0].addresses[*].ip}{"\n"}' echo "=== sleep 15s (let leaky-nack-demo ServiceEntry propagate) ===" sleep 15 echo "=== proxy-status after leaky-nack-demo (CDS configStatus via json) ===" istioctl proxy-status -o json | python3 -c " import json,sys d = json.load(sys.stdin) for item in d['resources']: nid = item['node']['id'] if 'egw-a' in nid or 'egw-b' in nid: print('--- node:', nid) for cfg in item['genericXdsConfigs']: print(' ', cfg['typeUrl'].split('.')[-1], cfg.get('configStatus'), cfg.get('lastUpdated')) " echo "=== cluster type for multi-ip-target on egw-a ===" istioctl proxy-config cluster deploy/egw-a."${NS}" --fqdn multi-ip-target."${NS}".svc.homelab.local -o json 2>/dev/null | jq '.[].type // "ABSENT"' echo "=== istiod logs grep for rejection/NACK evidence ===" kubectl -n istio-system logs deploy/istiod --since=3m | grep -iE 'multi-ip-target|logical_dns|rejected|nack' | tail -20 || true echo "=== traffic during alleged NACK window ===" retry_curl via_egwA_during_nack --connect-to mock.istio-verify-ext.svc.homelab.local:443:egw-a."${NS}".svc.homelab.local:8443 https://mock.istio-verify-ext.svc.homelab.local/ retry_curl via_egwB_during_nack --connect-to mock.istio-verify-ext.svc.homelab.local:443:egw-b."${NS}".svc.homelab.local:8444 https://mock.istio-verify-ext.svc.homelab.local/ echo "=== patch leaky-nack-demo exportTo=['.'] ===" kubectl -n "${NS}" patch serviceentry leaky-nack-demo --type merge -p '{"spec":{"exportTo":["."]}}' sleep 10 echo "=== proxy-status after scoping fix ===" istioctl proxy-status -o json | python3 -c " import json,sys d = json.load(sys.stdin) for item in d['resources']: nid = item['node']['id'] if 'egw-a' in nid or 'egw-b' in nid: print('--- node:', nid) for cfg in item['genericXdsConfigs']: print(' ', cfg['typeUrl'].split('.')[-1], cfg.get('configStatus'), cfg.get('lastUpdated')) " echo "=== done ==="