From 7b6974bc44ff8c0dc8864f85180e9e0b13295495 Mon Sep 17 00:00:00 2001 From: Mateusz Kowalski Date: Mon, 6 Jul 2026 15:17:01 +0200 Subject: [PATCH] test/router: retry http2 test when response comes from wrong router The http2 test deploys a dedicated router shard with HTTP/2 enabled and reaches it via the shard's wildcard DNS record (*..apps.). Until that record propagates, the route hostname still matches the cluster's *.apps wildcard (RFC 4592 wildcards match multiple labels), so requests are served by the default router, which also admits the test routes but has HTTP/2 disabled by default. Such a response carries the expected status code and body, so the poll loop accepts it, and the subsequent protocol assertion fails with HTTP/1.1 != HTTP/2.0 even though the shard would have served the request correctly moments later. This is visible as a flaky failure on 5.0 HyperShift AWS conformance (Component Readiness regression #42853, ~20% failure rate, consistently failing on the first test case with 'Expected HTTP/1.1 to equal HTTP/2.0'): fail [.../test/extended/router/http2.go:513]: {route:http2-custom-cert-edge frontendProto:HTTP/2.0 ...} Expected : HTTP/1.1 to equal : HTTP/2.0 Fix by treating a response with an unexpected negotiated protocol or body as retryable inside the poll loop. Also drop pooled keep-alive connections before retrying so the next attempt re-resolves DNS and opens a fresh TLS connection instead of being pinned to the wrong router by the transport's connection pool. Generated-by: AI Signed-off-by: Mateusz Kowalski --- test/extended/router/http2.go | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/test/extended/router/http2.go b/test/extended/router/http2.go index d9bfed9f40e0..4a91b9616cbd 100644 --- a/test/extended/router/http2.go +++ b/test/extended/router/http2.go @@ -488,6 +488,7 @@ var _ = g.Describe("[sig-network-edge][Conformance][Area:Networking][Feature:Rou for i, tc := range testCases { testConfig := fmt.Sprintf("%+v", tc) var resp *http.Response + var body []byte client := makeHTTPClient(tc.useHTTP2Transport, http2ClientTimeout) o.Expect(wait.Poll(time.Second, 5*time.Minute, func() (bool, error) { @@ -499,22 +500,43 @@ var _ = g.Describe("[sig-network-edge][Conformance][Area:Networking][Feature:Rou return false, nil // could be 503 if service not ready } if resp.StatusCode != tc.statusCode { - // Successful responses are checked and asserted - // in the o.Expect() checks below. resp.Body.Close() e2e.Logf("[test #%d/%d]: config: %s, expected status: %v, actual status: %v", i+1, len(testCases), testConfig, tc.statusCode, resp.StatusCode) return false, nil } + body, err = ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + e2e.Logf("[test #%d/%d]: config: %s, body read error: %v", i+1, len(testCases), testConfig, err) + return false, nil + } + // A response can transiently come from the wrong + // router: the shard's wildcard DNS record + // (*.) may not have propagated yet, in + // which case the request matches the cluster's + // *.apps wildcard and is served by the default + // router, which admits the route too but has HTTP/2 + // disabled by default. Such a response has the + // expected status code and body, but the wrong + // negotiated protocol. Retry until the response + // comes from the shard router, i.e., protocol and + // body both match the expectation. + if resp.Proto != tc.frontendProto || string(body) != tc.backendProto { + e2e.Logf("[test #%d/%d]: config: %s, expected frontend proto %q and backend proto %q, got %q and %q; response may come from a router without http/2 enabled, retrying", i+1, len(testCases), testConfig, tc.frontendProto, tc.backendProto, resp.Proto, string(body)) + // Drop pooled keep-alive connections so the + // next attempt re-resolves DNS and opens a + // fresh TLS connection instead of reusing the + // connection to the wrong router. + client.CloseIdleConnections() + return false, nil + } return true, nil })).NotTo(o.HaveOccurred()) o.Expect(resp).ToNot(o.BeNil(), testConfig) o.Expect(resp.StatusCode).To(o.Equal(tc.statusCode), testConfig) o.Expect(resp.Proto).To(o.Equal(tc.frontendProto), testConfig) - body, err := ioutil.ReadAll(resp.Body) - o.Expect(err).NotTo(o.HaveOccurred(), testConfig) o.Expect(string(body)).To(o.Equal(tc.backendProto), testConfig) - o.Expect(resp.Body.Close()).NotTo(o.HaveOccurred()) } }) })