Skip to content

Commit e020ad5

Browse files
committed
fix(ci): expose gateway kind runtime ports
1 parent b3b9649 commit e020ad5

6 files changed

Lines changed: 225 additions & 23 deletions

File tree

.github/workflows/conformance.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ jobs:
3939
run: scripts/ci/install-kind-tools.sh
4040

4141
- name: Create kind cluster
42-
run: |
43-
kind create cluster --name "$CLUSTER_NAME" --wait 5m
44-
kubectl wait --for=condition=ready node --all --timeout=2m
42+
run: scripts/ci/create-kind-cluster.sh
4543

4644
- name: Install Gateway API CRDs
4745
run: scripts/ci/install-gateway-api-crds.sh

.github/workflows/e2e.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ jobs:
3333
run: scripts/ci/install-kind-tools.sh
3434

3535
- name: Create kind cluster
36-
run: |
37-
kind create cluster --name "$CLUSTER_NAME" --wait 5m
38-
kubectl wait --for=condition=ready node --all --timeout=2m
36+
run: scripts/ci/create-kind-cluster.sh
3937

4038
- name: Install Gateway API CRDs
4139
run: scripts/ci/install-gateway-api-crds.sh

scripts/ci/ci_assets_test.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package ci
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
"testing"
9+
10+
"gopkg.in/yaml.v3"
11+
)
12+
13+
type kindClusterConfig struct {
14+
Kind string `yaml:"kind"`
15+
APIVersion string `yaml:"apiVersion"`
16+
Nodes []kindNode `yaml:"nodes"`
17+
}
18+
19+
type kindNode struct {
20+
Role string `yaml:"role"`
21+
ExtraPortMappings []kindPortMapping `yaml:"extraPortMappings"`
22+
}
23+
24+
type kindPortMapping struct {
25+
ContainerPort int `yaml:"containerPort"`
26+
HostPort int `yaml:"hostPort"`
27+
Protocol string `yaml:"protocol"`
28+
}
29+
30+
func TestKindCIConfigExposesConformancePorts(t *testing.T) {
31+
data := readFile(t, "kind-ci-config.yaml")
32+
33+
var config kindClusterConfig
34+
if err := yaml.Unmarshal(data, &config); err != nil {
35+
t.Fatalf("parse kind-ci-config.yaml: %v", err)
36+
}
37+
38+
if config.Kind != "Cluster" {
39+
t.Fatalf("kind = %q, want Cluster", config.Kind)
40+
}
41+
if config.APIVersion != "kind.x-k8s.io/v1alpha4" {
42+
t.Fatalf("apiVersion = %q, want kind.x-k8s.io/v1alpha4", config.APIVersion)
43+
}
44+
45+
var controlPlane *kindNode
46+
controlPlaneCount := 0
47+
for idx := range config.Nodes {
48+
if config.Nodes[idx].Role == "control-plane" {
49+
controlPlaneCount++
50+
controlPlane = &config.Nodes[idx]
51+
}
52+
}
53+
if controlPlane == nil {
54+
t.Fatalf("kind-ci-config.yaml has no control-plane node")
55+
}
56+
if controlPlaneCount != 1 {
57+
t.Fatalf("kind-ci-config.yaml has %d control-plane nodes, want 1", controlPlaneCount)
58+
}
59+
60+
mappings := make(map[string]kindPortMapping, len(controlPlane.ExtraPortMappings))
61+
for _, mapping := range controlPlane.ExtraPortMappings {
62+
protocol := strings.ToUpper(mapping.Protocol)
63+
key := portKey(mapping.HostPort, protocol)
64+
if _, exists := mappings[key]; exists {
65+
t.Fatalf("duplicate host mapping for %s", key)
66+
}
67+
mapping.Protocol = protocol
68+
mappings[key] = mapping
69+
}
70+
71+
required := []kindPortMapping{
72+
{HostPort: 80, ContainerPort: 30080, Protocol: "TCP"},
73+
{HostPort: 443, ContainerPort: 30443, Protocol: "TCP"},
74+
{HostPort: 8080, ContainerPort: 32080, Protocol: "TCP"},
75+
{HostPort: 8090, ContainerPort: 32090, Protocol: "TCP"},
76+
{HostPort: 8443, ContainerPort: 32443, Protocol: "TCP"},
77+
{HostPort: 8883, ContainerPort: 31883, Protocol: "TCP"},
78+
{HostPort: 5300, ContainerPort: 31300, Protocol: "UDP"},
79+
}
80+
81+
for _, want := range required {
82+
got, ok := mappings[portKey(want.HostPort, want.Protocol)]
83+
if !ok {
84+
t.Fatalf("missing host port mapping %d/%s", want.HostPort, want.Protocol)
85+
}
86+
if got.ContainerPort != want.ContainerPort {
87+
t.Fatalf(
88+
"host port mapping %d/%s containerPort = %d, want %d",
89+
want.HostPort,
90+
want.Protocol,
91+
got.ContainerPort,
92+
want.ContainerPort,
93+
)
94+
}
95+
}
96+
}
97+
98+
func TestWorkflowsUseSharedKindClusterHelper(t *testing.T) {
99+
for _, workflow := range []string{
100+
repoPath(".github", "workflows", "e2e.yml"),
101+
repoPath(".github", "workflows", "conformance.yml"),
102+
} {
103+
t.Run(filepath.Base(workflow), func(t *testing.T) {
104+
contents := string(readFile(t, workflow))
105+
if !strings.Contains(contents, "scripts/ci/create-kind-cluster.sh") {
106+
t.Fatalf("%s does not call scripts/ci/create-kind-cluster.sh", workflow)
107+
}
108+
if strings.Contains(contents, `kind create cluster --name "$CLUSTER_NAME" --wait 5m`) {
109+
t.Fatalf("%s still creates a plain kind cluster without --config", workflow)
110+
}
111+
})
112+
}
113+
}
114+
115+
func TestSmokeScriptForwardsToProgrammedGatewayListener(t *testing.T) {
116+
contents := string(readFile(t, repoPath("test", "e2e", "smoke", "run.sh")))
117+
118+
for _, want := range []string{
119+
`LOCAL_HTTP_PORT="${LOCAL_HTTP_PORT:-10080}"`,
120+
`GATEWAY_HTTP_PORT="${GATEWAY_HTTP_PORT:-80}"`,
121+
`"${LOCAL_HTTP_PORT}:${GATEWAY_HTTP_PORT}"`,
122+
`request_deadline=`,
123+
} {
124+
if !strings.Contains(contents, want) {
125+
t.Fatalf("smoke script missing %q", want)
126+
}
127+
}
128+
129+
if strings.Contains(contents, "10080:10080") {
130+
t.Fatalf("smoke script still forwards stale dataplane port 10080 to 10080")
131+
}
132+
}
133+
134+
func portKey(port int, protocol string) string {
135+
return fmt.Sprintf("%s/%d", protocol, port)
136+
}
137+
138+
func readFile(t *testing.T, path string) []byte {
139+
t.Helper()
140+
141+
data, err := os.ReadFile(path)
142+
if err != nil {
143+
t.Fatalf("read %s: %v", path, err)
144+
}
145+
return data
146+
}
147+
148+
func repoPath(elem ...string) string {
149+
return filepath.Join(append([]string{"..", ".."}, elem...)...)
150+
}

scripts/ci/create-kind-cluster.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
CLUSTER_NAME="${CLUSTER_NAME:?CLUSTER_NAME is required}"
5+
KIND_CONFIG="${KIND_CONFIG:-scripts/ci/kind-ci-config.yaml}"
6+
7+
if [[ ! -f "$KIND_CONFIG" ]]; then
8+
echo "kind config not found: $KIND_CONFIG" >&2
9+
exit 1
10+
fi
11+
12+
kind create cluster --name "$CLUSTER_NAME" --config "$KIND_CONFIG" --wait 5m
13+
kubectl wait --for=condition=ready node --all --timeout=2m

scripts/ci/kind-ci-config.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
kind: Cluster
2+
apiVersion: kind.x-k8s.io/v1alpha4
3+
networking:
4+
ipFamily: dual
5+
nodes:
6+
- role: control-plane
7+
extraPortMappings:
8+
- containerPort: 30080
9+
hostPort: 80
10+
protocol: TCP
11+
- containerPort: 30443
12+
hostPort: 443
13+
protocol: TCP
14+
- containerPort: 32080
15+
hostPort: 8080
16+
protocol: TCP
17+
- containerPort: 32090
18+
hostPort: 8090
19+
protocol: TCP
20+
- containerPort: 32443
21+
hostPort: 8443
22+
protocol: TCP
23+
- containerPort: 31883
24+
hostPort: 8883
25+
protocol: TCP
26+
- containerPort: 31300
27+
hostPort: 5300
28+
protocol: UDP

test/e2e/smoke/run.sh

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ CONTROL_PLANE_NS="nantian-gw"
1111
TEST_NS="nantian-e2e"
1212
DATA_PLANE_SVC="nantian-dataplane"
1313
ECHO_PORT=8080
14+
LOCAL_HTTP_PORT="${LOCAL_HTTP_PORT:-10080}"
15+
GATEWAY_HTTP_PORT="${GATEWAY_HTTP_PORT:-80}"
1416
TIMEOUT="${TIMEOUT:-180}"
1517
CLEANUP="${1:-}"
1618
FAILED=false
@@ -33,6 +35,12 @@ cleanup_cluster() {
3335
kind delete cluster --name "$CLUSTER_NAME" 2>/dev/null || true
3436
}
3537

38+
stop_port_forward() {
39+
local pid="$1"
40+
kill "$pid" 2>/dev/null || true
41+
wait "$pid" 2>/dev/null || true
42+
}
43+
3644
# ── Step 1: ensure kind cluster ──
3745
ensure_cluster() {
3846
if kind get clusters 2>/dev/null | grep -q "^$CLUSTER_NAME$"; then
@@ -193,27 +201,34 @@ send_request() {
193201
return 1
194202
fi
195203

196-
echo "=== Sending test request (port-forward $dataplane_pod:10080) ==="
204+
echo "=== Sending test request (port-forward $dataplane_pod ${LOCAL_HTTP_PORT}:${GATEWAY_HTTP_PORT}) ==="
197205

198206
# Start port-forward in background
199-
kubectl port-forward -n "$CONTROL_PLANE_NS" "pod/$dataplane_pod" 10080:10080 &>/dev/null &
207+
kubectl port-forward -n "$CONTROL_PLANE_NS" "pod/$dataplane_pod" "${LOCAL_HTTP_PORT}:${GATEWAY_HTTP_PORT}" &>/dev/null &
200208
PF_PID=$!
201-
sleep 2
202-
203-
# Send request and capture response
204-
local response
205-
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:10080/echo 2>/dev/null || echo "000")
206-
local status=$?
207209

208-
kill $PF_PID 2>/dev/null || true
209-
210-
if [[ "$response" == "200" ]]; then
211-
green " PASS: GET /echo → HTTP $response"
212-
return 0
213-
else
214-
fail "GET /echo → HTTP $response (expected 200)"
215-
return 1
216-
fi
210+
local request_deadline=$((SECONDS + TIMEOUT))
211+
local response="000"
212+
while (( SECONDS < request_deadline )); do
213+
if ! kill -0 "$PF_PID" 2>/dev/null; then
214+
stop_port_forward "$PF_PID"
215+
fail "port-forward to $dataplane_pod exited before request succeeded"
216+
return 1
217+
fi
218+
219+
response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${LOCAL_HTTP_PORT}/echo" 2>/dev/null || echo "000")
220+
if [[ "$response" == "200" ]]; then
221+
stop_port_forward "$PF_PID"
222+
green " PASS: GET /echo -> HTTP $response"
223+
return 0
224+
fi
225+
226+
sleep 2
227+
done
228+
229+
stop_port_forward "$PF_PID"
230+
fail "GET /echo -> HTTP $response (expected 200 within ${TIMEOUT}s)"
231+
return 1
217232
}
218233

219234
# ── Main ──

0 commit comments

Comments
 (0)