Skip to content

Commit 5a69783

Browse files
committed
e2e test
Signed-off-by: omar <omar.hammami@solo.io>
1 parent ea75066 commit 5a69783

4 files changed

Lines changed: 247 additions & 2 deletions

File tree

test/e2e/emitters/kgateway/e2e_test.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,17 @@ func TestIngress2GatewayE2E(t *testing.T) {
203203
host = hr
204204
}
205205

206-
// Curl via Gateway.
207-
requireHTTP200Eventually(t, ctx, host, fmt.Sprintf("http://%s:80/", gwAddr), 1*time.Minute)
206+
// Special handling for SSL redirect test case
207+
if name == "ssl_redirect" {
208+
// Test HTTP redirect (308) to HTTPS
209+
requireHTTPRedirectEventually(t, ctx, host, fmt.Sprintf("http://%s:80/", gwAddr), 1*time.Minute)
210+
211+
// Test HTTPS connectivity (200) with insecure flag
212+
requireHTTPS200Eventually(t, ctx, host, fmt.Sprintf("https://%s:443/", gwAddr), 1*time.Minute)
213+
} else {
214+
// Standard HTTP test for other cases
215+
requireHTTP200Eventually(t, ctx, host, fmt.Sprintf("http://%s:80/", gwAddr), 1*time.Minute)
216+
}
208217
})
209218
}
210219
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: ssl-redirect-tls
5+
namespace: default
6+
type: kubernetes.io/tls
7+
data:
8+
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVS1E2anF1NXk3d1hNLytWWGRheGI0cG1HN0Frd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RWhNQjhHQTFVRUF3d1ljM05zTFhKbFpHbHlaV04wTG14dlkyRnNaR1YyTG0xbE1CNFhEVEkxTVRJeApOakU0TXpjd09Gb1hEVEkyTVRJeE5qRTRNemN3T0Zvd0l6RWhNQjhHQTFVRUF3d1ljM05zTFhKbFpHbHlaV04wCkxteHZZMkZzWkdWMkxtMWxNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQW5TZ2MKeFFqSzBySFZYT3Y1TDgxeDFza0RycTI0K29IcXRhQ3JDbC8rZm5tajdIbzI1M3JZMCtjUkZPTWpZV1I4SFF2eApHMUR3NDEvSjRKNUNwbDlJbnl1WXBBS0ZLUk94MXhSLzkxamQ5WmFxQnhGeVAzWTRPSEZ2R0dlVncrWi93R3JECnhTSmJ2WXVHazhiWEYwU3YvRmdkdDAxZWdOYUNNUG04RytIQUJ2dEJYWVlwaFZHSnZBWmdjMkwrR1ZDSk50dVIKeWJjVjVJVUNHR2pOa2dlbU5vNjlNeHVSY1FMOGJ5WGxrbmZQaytTYU1WM2pUZE1UWmFyTHRVN1J5cGFRVHJjKwpYckNQMVZWOFlkQXNSNEhKRWFmUUVhMEV6NTZLK0hQT0pRVXovaUVMTmk4R29EaDFEcnFLWWFRRFptWFdKNzVsCmtFZEJWV1o3dEpoZ25xbnE1UUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVd3M2RFlzblNCTlhXeTFxNmErU1AKVGZzTitDOHdId1lEVlIwakJCZ3dGb0FVd3M2RFlzblNCTlhXeTFxNmErU1BUZnNOK0M4d0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFIY2g2VjFZZHp2a0pLak1JMG9hUXE4ZDFrZFBQCll0WkhKU0RsQlNQaDdEeUxST0hEcFh6L2hqaHNYSHR6bTJUU3ZBOUhzYU1hcjdKVGtkdTBsTUJqanlpaWxJbnUKanZzVGVRNnpVMUNVR3Q2L05FNFdQVm9EdHVld28zcFhoQjBhbEdJMTF1bnBWLzBuYjFLMmVrUDN5NDRuazZaZQp3ekZGemRzdnlNVXV4ckpmdGxyOEIxb1ZheTJRQjY4dWp3U21Wdzd1S3FjMklOWG1hSFdIMzVtK0pTZHJlc01oCnV2TDhVK2c0YmYrcjY2bmhqWjZOcjJEZVlqVElqOTBtOG1yb08yaWNTeDMwU1krQk0yOHNqN014YWNheFoxb0UKMGN1aEoyWkFqUHNMYTJBdFd5ZUprQmhwUEJhZHpPbTAvN01uUHdQcXFCbWRTL0dUWURGUHRtTE5NQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
9+
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktZd2dnU2lBZ0VBQW9JQkFRQ2RLQnpGQ01yU3NkVmMKNi9rdnpYSFd5UU91cmJqNmdlcTFvS3NLWC81K2VhUHNlamJuZXRqVDV4RVU0eU5oWkh3ZEMvRWJVUERqWDhuZwpua0ttWDBpZks1aWtBb1VwRTdIWEZILzNXTjMxbHFvSEVYSS9kamc0Y1c4WVo1WEQ1bi9BYXNQRklsdTlpNGFUCnh0Y1hSSy84V0IyM1RWNkExb0l3K2J3YjRjQUcrMEZkaGltRlVZbThCbUJ6WXY0WlVJazIyNUhKdHhYa2hRSVkKYU0yU0I2WTJqcjB6RzVGeEF2eHZKZVdTZDgrVDVKb3hYZU5OMHhObHFzdTFUdEhLbHBCT3R6NWVzSS9WVlh4aAowQ3hIZ2NrUnA5QVJyUVRQbm9yNGM4NGxCVFArSVFzMkx3YWdPSFVPdW9waHBBTm1aZFludm1XUVIwRlZabnUwCm1HQ2VxZXJsQWdNQkFBRUNnZ0VBRWYxYmZicUo3U0JxT2g3UkFZWFUrQzgrZXFwc09GRFMyME92RUpyNGVPWFgKdStRcGtNbmhORENKSmdqeUxkM2d6enBrdVNuRXlXSlhxNXl5S0hWVHpOU2l2bk9EWnJaWWJ6UnJpWmJrb1k1RApuRmVFNm5yUDZMWWpiM204RFJhL25Qa2J1OTg0L1RPUmppeit4aTBZc0J3dUdxT0NvK0RxT2t6OEdYejRERlJjCm8rRHN0ZXZoQWxMdGYvaVVHaHVJdTl3b3Mvb0o1R1Z3WVNRL2J5MVd4aHBMenl6WmV4MjJDcHovazErZ0NOOEIKNFpWZHFvT3NiQzlrMzdsZ2JBZEhHK3MybjQ5aU52S01NM1VDWGhLV0tTYXNUalRUMWJ0U1VoZkU5Y0JEV2t5LwpEK0w0OWwvRVQrUEZXQ3EwaUVTRDY3R0xBWElDbkJER1ZQT0NHWnV6YVFLQmdRRFR6Z3BkUXNSZmlPcklET0JqCk42ZXJOL1NxeTB1aUw3V0t3ZGwxU1ZOc25kcEJrQVlMMGRTdUZ4aForRDVPYTVHZDhvQTJQNkJIUkE5UlUweUMKME4zQVAwZkVFZkhXN1BmcnM2QW91ZGdJOW9za1kvQ3B2eVRTTTBSVmRBUFZQRUw1dVJWcTExZXUvTmFaZURQWQpZVm1OUUlBY1dtNHN6OFVMQTY5czBmcFR4d0tCZ1FDOTh1KzdYNnhrRVVJNjNvNTZUYldaMWc4VTJpZU1taVhwCkZxaHd6Z2o5d3Z1SVdXdm04ZjVzb3B6dHpKNklUb0g3bkhYVDB4NG5oVk1aT2lTelk2ZGUzUTZkV21UN3RpMUcKT0o5VlJ6SzNsWFA2bkxRSEt2d1RGVHpoWnFRYTVpd1pIR1FKNUVuOVMydklHZllNTk5xNjN3RG9JSmFKT1dzSQpXVGRWZmtaejh3S0JnR0MzUnhoSzhxekZNcUJpOE51ZGdGeTQwbXBqSm9oS3pOVXRxNFRaRk5VV1I2R0VpSjVCCkZLTGlQT1pvYXRzWVY0Z09RZW1EcUVhaWwzUUZXM3lvcjNtbjY3ZG1razRZS3lWZ0FwUldPSVh2UHA1QlhKWEcKaUtQTGNUcXNIVXZ6bG9tOXNELzNVVHpBaTZYTXM2L3MxQjJpQkYzdUZUMDFLcjdhMGZJWTkvdmxBb0dBQ2ZicQptSnZHUVdHZVpkUXpDVHQyWVdHWFhQS3N6SFZ4czY2YW00QlRmR3gwSVl6L1doZ3J6cXNoTEdCbG1LVDFzS3RlCml3UXlPc1NGdlhjTllkUENmZmwrd01aek1iazIyczR4blpta2tYam5vcWdCMGJaeGp0YTRZT0t2alRHeDhvZEkKd0RRWHBaQUZVWFA3TWx5N2RMNHFJQU5Gb21FK3VpdGorYm9zRy8wQ2dZQWZUR1QwN09EVm9pSTR0TlhrYXUyWApzaTRaWTR5SVR5enppN3UxanZlaEdLbFVucDBQMGNFVWJlQ0NyNnc4M3BYdmpQUVd3ckZMMyt3VUtyNE1UeWhuCkRBLzNPcHR0MFVaR29XT0ZvTFNmN3ExQlBuK1hCWURSZ2F3aHRZWW5WQ09xbzgzaHVaekE4VzUvb3c5bzVERXYKbmpyRlhvQlVZcnpXM0xaVnV4NWhsZz09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
10+
---
11+
apiVersion: networking.k8s.io/v1
12+
kind: Ingress
13+
metadata:
14+
annotations:
15+
ingress2gateway.kubernetes.io/implementation: kgateway
16+
nginx.ingress.kubernetes.io/ssl-redirect: "true"
17+
name: ssl-redirect-localhost
18+
namespace: default
19+
spec:
20+
ingressClassName: nginx
21+
tls:
22+
- hosts:
23+
- ssl-redirect.localdev.me
24+
secretName: ssl-redirect-tls
25+
rules:
26+
- host: ssl-redirect.localdev.me
27+
http:
28+
paths:
29+
- backend:
30+
service:
31+
name: echo-backend
32+
port:
33+
number: 8080
34+
path: /
35+
pathType: Prefix
36+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
apiVersion: gateway.networking.k8s.io/v1
2+
kind: Gateway
3+
metadata:
4+
annotations:
5+
gateway.networking.k8s.io/generator: ingress2gateway-dev
6+
name: nginx
7+
namespace: default
8+
spec:
9+
gatewayClassName: kgateway
10+
listeners:
11+
- hostname: ssl-redirect.localdev.me
12+
name: ssl-redirect-localdev-me-http
13+
port: 80
14+
protocol: HTTP
15+
- hostname: ssl-redirect.localdev.me
16+
name: ssl-redirect-localdev-me-https
17+
port: 443
18+
protocol: HTTPS
19+
tls:
20+
certificateRefs:
21+
- group: null
22+
kind: null
23+
name: ssl-redirect-tls
24+
status: {}
25+
---
26+
apiVersion: gateway.networking.k8s.io/v1
27+
kind: HTTPRoute
28+
metadata:
29+
annotations:
30+
gateway.networking.k8s.io/generator: ingress2gateway-dev
31+
name: ssl-redirect-localhost-ssl-redirect-localdev-me-https
32+
namespace: default
33+
spec:
34+
hostnames:
35+
- ssl-redirect.localdev.me
36+
parentRefs:
37+
- name: nginx
38+
sectionName: ssl-redirect-localdev-me-https
39+
rules:
40+
- backendRefs:
41+
- name: echo-backend
42+
port: 8080
43+
matches:
44+
- path:
45+
type: PathPrefix
46+
value: /
47+
status:
48+
parents: []
49+
---
50+
apiVersion: gateway.networking.k8s.io/v1
51+
kind: HTTPRoute
52+
metadata:
53+
annotations:
54+
gateway.networking.k8s.io/generator: ingress2gateway-dev
55+
name: ssl-redirect-localhost-ssl-redirect-localdev-me-http-redirect
56+
namespace: default
57+
spec:
58+
hostnames:
59+
- ssl-redirect.localdev.me
60+
parentRefs:
61+
- name: nginx
62+
sectionName: ssl-redirect-localdev-me-http
63+
rules:
64+
- filters:
65+
- requestRedirect:
66+
scheme: https
67+
statusCode: 308
68+
type: RequestRedirect
69+
matches:
70+
- path:
71+
type: PathPrefix
72+
value: /
73+
status:
74+
parents: []

test/e2e/emitters/kgateway/wait.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,58 @@ func curlHTTPCodeFromClient(ctx context.Context, host, url string) (code string,
149149
return strings.TrimSpace(out), out, nil
150150
}
151151

152+
// curlHTTPRedirectFromClient executes curl and returns the HTTP status code and Location header.
153+
// It does NOT follow redirects (no -L flag) so we can see the redirect response.
154+
func curlHTTPRedirectFromClient(ctx context.Context, host, url string) (code string, location string, out string, err error) {
155+
// Use -i to include headers, -s for silent, but keep stderr for errors
156+
// Extract status code and Location header
157+
script := `set -o pipefail; curl -sSi --connect-timeout 2 --max-time 5 -H "Host: $1" "$2" 2>&1 || echo "000"`
158+
out, err = kubectl(ctx, "-n", "default", "exec", "deploy/curl", "--",
159+
"sh", "-c", script, "_", host, url,
160+
)
161+
if err != nil {
162+
return "000", "", out, err
163+
}
164+
165+
// Parse status code from HTTP/1.1 308 Permanent Redirect
166+
lines := strings.Split(out, "\n")
167+
code = "000"
168+
location = ""
169+
for _, line := range lines {
170+
lineLower := strings.ToLower(line)
171+
if strings.HasPrefix(line, "HTTP/") {
172+
// Extract status code from "HTTP/1.1 308 Permanent Redirect"
173+
parts := strings.Fields(line)
174+
if len(parts) >= 2 {
175+
code = parts[1]
176+
}
177+
}
178+
if strings.HasPrefix(lineLower, "location:") {
179+
// Extract Location header value (case-insensitive)
180+
// Handle both "Location: https://..." and "location: https://..."
181+
idx := strings.Index(lineLower, "location:")
182+
if idx != -1 {
183+
location = strings.TrimSpace(line[idx+9:])
184+
}
185+
}
186+
}
187+
188+
return code, location, out, nil
189+
}
190+
191+
// curlHTTPS200FromClient executes curl with -k (insecure) flag for HTTPS requests.
192+
func curlHTTPS200FromClient(ctx context.Context, host, url string) (code string, out string, err error) {
193+
// Use -k to skip certificate verification for self-signed certs
194+
script := `set -o pipefail; curl -k -sS -o /dev/null -w "%{http_code}" --connect-timeout 2 --max-time 5 -H "Host: $1" "$2" || echo 000`
195+
out, err = kubectl(ctx, "-n", "default", "exec", "deploy/curl", "--",
196+
"sh", "-c", script, "_", host, url,
197+
)
198+
if err != nil {
199+
return "000", out, err
200+
}
201+
return strings.TrimSpace(out), out, nil
202+
}
203+
152204
func debugCurlVerbose(t *testing.T, ctx context.Context, host, url string) error {
153205
t.Helper()
154206
script := `curl -v --connect-timeout 2 --max-time 5 -H "Host: $1" "$2" || true`
@@ -159,6 +211,80 @@ func debugCurlVerbose(t *testing.T, ctx context.Context, host, url string) error
159211
return err
160212
}
161213

214+
// requireHTTPRedirectEventually waits for an HTTP redirect response (308 status code)
215+
// and verifies the Location header contains https:// scheme.
216+
func requireHTTPRedirectEventually(t *testing.T, ctx context.Context, host, url string, timeout time.Duration) {
217+
t.Helper()
218+
219+
deadline := time.Now().Add(timeout)
220+
interval := 2 * time.Second
221+
222+
var lastCode string
223+
var lastLocation string
224+
var lastOut string
225+
var lastErr error
226+
227+
for attempt := 1; time.Now().Before(deadline); attempt++ {
228+
code, location, out, err := curlHTTPRedirectFromClient(ctx, host, url)
229+
lastCode, lastLocation, lastOut, lastErr = code, location, out, err
230+
231+
if err == nil && strings.TrimSpace(code) == "308" {
232+
// Verify Location header contains https://
233+
if strings.HasPrefix(strings.ToLower(location), "https://") {
234+
return
235+
}
236+
}
237+
238+
if attempt == 1 || attempt%10 == 0 {
239+
t.Logf("waiting for HTTP 308 redirect (attempt=%d host=%s url=%s): code=%q location=%q err=%v",
240+
attempt, host, url, strings.TrimSpace(code), location, err)
241+
}
242+
time.Sleep(interval)
243+
}
244+
245+
_ = debugCurlVerbose(t, ctx, host, url)
246+
247+
t.Fatalf("timed out waiting for HTTP 308 redirect (host=%s url=%s timeout=%s). lastCode=%q lastLocation=%q lastErr=%v lastOut=%s",
248+
host, url, timeout, strings.TrimSpace(lastCode), lastLocation, lastErr, lastOut)
249+
}
250+
251+
// requireHTTPS200Eventually waits for an HTTPS 200 response using insecure curl (-k flag).
252+
func requireHTTPS200Eventually(t *testing.T, ctx context.Context, host, url string, timeout time.Duration) {
253+
t.Helper()
254+
255+
deadline := time.Now().Add(timeout)
256+
interval := 2 * time.Second
257+
258+
var lastCode string
259+
var lastOut string
260+
var lastErr error
261+
262+
for attempt := 1; time.Now().Before(deadline); attempt++ {
263+
code, out, err := curlHTTPS200FromClient(ctx, host, url)
264+
lastCode, lastOut, lastErr = code, out, err
265+
266+
if err == nil && strings.TrimSpace(code) == "200" {
267+
return
268+
}
269+
270+
if attempt == 1 || attempt%10 == 0 {
271+
t.Logf("waiting for HTTPS 200 (attempt=%d host=%s url=%s): code=%q err=%v",
272+
attempt, host, url, strings.TrimSpace(code), err)
273+
}
274+
time.Sleep(interval)
275+
}
276+
277+
// Debug with verbose curl
278+
script := `curl -kv --connect-timeout 2 --max-time 5 -H "Host: $1" "$2" || true`
279+
out, _ := kubectl(ctx, "-n", "default", "exec", "deploy/curl", "--",
280+
"sh", "-c", script, "_", host, url,
281+
)
282+
t.Logf("debug curl -kv output:\n%s", out)
283+
284+
t.Fatalf("timed out waiting for HTTPS 200 (host=%s url=%s timeout=%s). lastCode=%q lastErr=%v lastOut=%s",
285+
host, url, timeout, strings.TrimSpace(lastCode), lastErr, lastOut)
286+
}
287+
162288
func waitForGatewayAddress(ctx context.Context, ns, gwName string, timeout time.Duration) (string, error) {
163289
deadline := time.Now().Add(timeout)
164290
for time.Now().Before(deadline) {

0 commit comments

Comments
 (0)