From a97b2ad5bdf12577ae5d45858a9d783eeb1b9e14 Mon Sep 17 00:00:00 2001 From: Andrey Lebedev Date: Wed, 25 Mar 2026 22:43:39 +0100 Subject: [PATCH 1/4] NE-2422: router/metrics: disable proxy protocol for dual-stack AWS clusters Dual-stack installations on AWS are forced to use NLB by the installer, which doesn't support proxy protocol. Skip proxy protocol when the infrastructure IPFamily is DualStackIPv4Primary or DualStackIPv6Primary. Co-Authored-By: Claude Opus 4.6 --- test/extended/router/metrics.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/extended/router/metrics.go b/test/extended/router/metrics.go index b25732bbd64f..2aeb9c66102a 100644 --- a/test/extended/router/metrics.go +++ b/test/extended/router/metrics.go @@ -52,10 +52,19 @@ var _ = g.Describe("[sig-network][Feature:Router]", func() { infra, err := oc.AdminConfigClient().ConfigV1().Infrastructures().Get(context.Background(), "cluster", metav1.GetOptions{}) o.Expect(err).NotTo(o.HaveOccurred()) platformType := infra.Status.Platform + dualStackIPFamily := false if infra.Status.PlatformStatus != nil { platformType = infra.Status.PlatformStatus.Type + if infra.Status.PlatformStatus.AWS != nil { + ipFamily := infra.Status.PlatformStatus.AWS.IPFamily + if ipFamily == configv1.DualStackIPv4Primary || ipFamily == configv1.DualStackIPv6Primary { + dualStackIPFamily = true + } + } } - proxyProtocol = platformType == configv1.AWSPlatformType + // Dual-stack installations on AWS are forced to use NLB type, which + // doesn't accept proxy protocol (yet). + proxyProtocol = (platformType == configv1.AWSPlatformType) && !dualStackIPFamily // This test needs to make assertions against a single router pod, so all access // to the router should happen through a single endpoint. From 90acef1d0c104c7f26be1ce81c4a76c857c2355c Mon Sep 17 00:00:00 2001 From: Andrey Lebedev Date: Wed, 25 Mar 2026 23:08:43 +0100 Subject: [PATCH 2/4] NE-2422: router/h2spec: add v6only bind to h2spec test haproxy for dual-stack The h2spec test deploys a standalone haproxy pod as a backend to verify HTTP/2 conformance. On dual-stack clusters, this test haproxy needs a separate IPv6 bind with v6only to listen on both address families. Add the extra bind line when the infrastructure IPFamily is one of the DualStack values. Co-Authored-By: Claude Opus 4.6 --- test/extended/router/h2spec.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/extended/router/h2spec.go b/test/extended/router/h2spec.go index d8af2b42654d..f7edf4abb641 100644 --- a/test/extended/router/h2spec.go +++ b/test/extended/router/h2spec.go @@ -24,6 +24,7 @@ import ( admissionapi "k8s.io/pod-security-admission/api" utilpointer "k8s.io/utils/pointer" + configv1 "github.com/openshift/api/config/v1" routev1 "github.com/openshift/api/route/v1" securityv1 "github.com/openshift/api/security/v1" routeclientset "github.com/openshift/client-go/route/clientset/versioned" @@ -71,6 +72,14 @@ var _ = g.Describe("[sig-network-edge][Conformance][Area:Networking][Feature:Rou g.Skip("Skip on platforms where the default router is not exposed by a load balancer service.") } + dualStackIPFamily := false + if infra.Status.PlatformStatus != nil && infra.Status.PlatformStatus.AWS != nil { + ipFamily := infra.Status.PlatformStatus.AWS.IPFamily + if ipFamily == configv1.DualStackIPv4Primary || ipFamily == configv1.DualStackIPv6Primary { + dualStackIPFamily = true + } + } + g.By("Getting the default domain") defaultDomain, err := getDefaultIngressClusterDomainName(oc, time.Minute) o.Expect(err).NotTo(o.HaveOccurred(), "failed to find default domain name") @@ -96,6 +105,13 @@ var _ = g.Describe("[sig-network-edge][Conformance][Area:Networking][Feature:Rou ) o.Expect(err).NotTo(o.HaveOccurred()) + // On dual-stack clusters, HAProxy needs a separate IPv6 bind + // with v6only to listen on both address families. + v6onlyBind := "" + if dualStackIPFamily { + v6onlyBind = "\n\tbind :::8443 ssl crt /tmp/bundle.pem alpn h2 v6only" + } + g.By("Creating h2spec test service config") ns := oc.KubeFramework().Namespace.Name configMap := &corev1.ConfigMap{ @@ -127,7 +143,7 @@ frontend fe_proxy_tls declare capture request len 40000 http-request capture req.body id 0 log global - bind *:8443 ssl crt /tmp/bundle.pem alpn h2 + bind *:8443 ssl crt /tmp/bundle.pem alpn h2` + v6onlyBind + ` default_backend haproxy-availability-ok backend haproxy-availability-ok errorfile 503 /etc/haproxy/errorfile From 76c4f47eac63b4e57cd2d0d1a164066fc2cea85e Mon Sep 17 00:00:00 2001 From: Andrey Lebedev Date: Wed, 25 Mar 2026 23:14:55 +0100 Subject: [PATCH 3/4] NE-2422: router/idle: use TCP6-LISTEN for socat on IPv6-primary clusters On IPv6-primary dual-stack clusters, the readiness probe connects via the pod's IPv6 address. The socat backend using TCP4-LISTEN only accepts IPv4 connections, causing the pod to never become ready and the test to time out. Use TCP6-LISTEN when the infrastructure IPFamily is DualStackIPv6Primary. Co-Authored-By: Claude Opus 4.6 --- test/extended/router/idle.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/extended/router/idle.go b/test/extended/router/idle.go index 7669075d9e7f..e89a402d10b6 100644 --- a/test/extended/router/idle.go +++ b/test/extended/router/idle.go @@ -64,6 +64,14 @@ var _ = g.Describe("[sig-network-edge][Conformance][Area:Networking][Feature:Rou g.Skip("https://bugzilla.redhat.com/show_bug.cgi?id=1933114") } + // On IPv6-primary clusters, socat must listen on IPv6 since + // the readiness probe connects via the pod's IPv6 address. + socatListen := "TCP4-LISTEN:8080" + if infra.Status.PlatformStatus.AWS != nil && + infra.Status.PlatformStatus.AWS.IPFamily == configv1.DualStackIPv6Primary { + socatListen = "TCP6-LISTEN:8080" + } + timeout := 15 * time.Minute g.By("creating test fixtures") @@ -149,7 +157,7 @@ var _ = g.Describe("[sig-network-edge][Conformance][Area:Networking][Feature:Rou }, Command: []string{ "/usr/bin/socat", - "TCP4-LISTEN:8080,reuseaddr,fork", + socatListen + ",reuseaddr,fork", `EXEC:'/bin/bash -c \"printf \\\"HTTP/1.0 200 OK\r\n\r\n\\\"; sed -e \\\"/^\r/q\\\"\"'`, }, Ports: []corev1.ContainerPort{ From 4fe448265b37fbd7cf35fa70e65b4af7e2a0b8c2 Mon Sep 17 00:00:00 2001 From: Andrey Lebedev Date: Thu, 26 Mar 2026 00:05:29 +0100 Subject: [PATCH 4/4] NE-2422: router/headers: use TCP6-LISTEN for socat on IPv6-primary clusters The headers test deploys a standalone http echo server pod using socat with TCP4-LISTEN. On IPv6-primary clusters, connections arrive over IPv6 so the pod never responds. Read the fixture, replace TCP4-LISTEN with TCP6-LISTEN when the infrastructure IPFamily is DualStackIPv6Primary, and feed the modified config via stdin. Co-Authored-By: Claude Opus 4.6 --- test/extended/router/headers.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/extended/router/headers.go b/test/extended/router/headers.go index e6c73ffc7a45..aaf255de495c 100644 --- a/test/extended/router/headers.go +++ b/test/extended/router/headers.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "net/http" + "os" "strings" "time" @@ -54,8 +55,12 @@ var _ = g.Describe("[sig-network][Feature:Router][apigroup:operator.openshift.io o.Expect(network).NotTo(o.BeNil()) platformType := infra.Status.Platform + dualStackIPv6Primary := false if infra.Status.PlatformStatus != nil { platformType = infra.Status.PlatformStatus.Type + if infra.Status.PlatformStatus.AWS != nil { + dualStackIPv6Primary = infra.Status.PlatformStatus.AWS.IPFamily == configv1.DualStackIPv6Primary + } } switch platformType { case configv1.AWSPlatformType, configv1.AzurePlatformType, configv1.GCPPlatformType: @@ -79,7 +84,15 @@ var _ = g.Describe("[sig-network][Feature:Router][apigroup:operator.openshift.io g.By(fmt.Sprintf("creating an http echo server from a config file %q", configPath)) - err := oc.Run("create").Args("-f", configPath).Execute() + configData, err := os.ReadFile(configPath) + o.Expect(err).NotTo(o.HaveOccurred()) + config := string(configData) + // On IPv6-primary clusters, socat must listen on IPv6. + if dualStackIPv6Primary { + g.By("with IPv6 listen") + config = strings.ReplaceAll(config, "TCP4-LISTEN", "TCP6-LISTEN") + } + err = oc.Run("create").Args("-f", "-").InputString(config).Execute() o.Expect(err).NotTo(o.HaveOccurred()) var clientIP string