@@ -37,27 +37,15 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:AWSDualStackInstall][Featu
3737 ctx := context .Background ()
3838
3939 g .By ("Checking that the Infrastructure CR has a DualStack IPFamily" )
40- infra , err := oc .AdminConfigClient ().ConfigV1 ().Infrastructures ().Get (ctx , "cluster" , metav1.GetOptions {})
41- o .Expect (err ).NotTo (o .HaveOccurred (), "failed to get infrastructure CR" )
42-
43- if infra .Status .PlatformStatus == nil || infra .Status .PlatformStatus .Type != configv1 .AWSPlatformType {
44- g .Skip ("Test requires AWS platform" )
45- }
46- if infra .Status .PlatformStatus .AWS == nil {
47- g .Skip ("AWS platform status is not set" )
48- }
49- ipFamily := infra .Status .PlatformStatus .AWS .IPFamily
50- if ipFamily != configv1 .DualStackIPv4Primary && ipFamily != configv1 .DualStackIPv6Primary {
51- g .Skip (fmt .Sprintf ("Test requires DualStack IPFamily, got %q" , ipFamily ))
52- }
40+ requireAWSDualStack (ctx , oc )
5341
5442 g .By ("Getting the default ingress domain" )
5543 defaultDomain , err := getDefaultIngressClusterDomainName (oc , time .Minute )
5644 o .Expect (err ).NotTo (o .HaveOccurred (), "failed to find default domain name" )
5745
5846 ns := oc .KubeFramework ().Namespace .Name
5947 baseDomain := strings .TrimPrefix (defaultDomain , "apps." )
60- shardFQDN := "hosts ." + baseDomain
48+ shardFQDN := "nlb ." + baseDomain
6149
6250 // Deploy the shard first so DNS and LB can provision while we set up the backend.
6351 g .By ("Deploying a new router shard with NLB" )
@@ -87,137 +75,219 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:AWSDualStackInstall][Featu
8775 err = oc .AsAdmin ().Run ("label" ).Args ("namespace" , oc .Namespace (), "type=" + oc .Namespace ()).Execute ()
8876 o .Expect (err ).NotTo (o .HaveOccurred ())
8977
90- g .By ("Creating backend service" )
91- service := & corev1.Service {
92- ObjectMeta : metav1.ObjectMeta {
93- Name : "dualstack-backend" ,
94- Labels : map [string ]string {
95- "app" : "dualstack-backend" ,
96- },
97- },
98- Spec : corev1.ServiceSpec {
99- Selector : map [string ]string {
100- "app" : "dualstack-backend" ,
101- },
102- IPFamilyPolicy : func () * corev1.IPFamilyPolicy {
103- p := corev1 .IPFamilyPolicyPreferDualStack
104- return & p
105- }(),
106- Ports : []corev1.ServicePort {
107- {
108- Name : "http" ,
109- Port : 8080 ,
110- Protocol : corev1 .ProtocolTCP ,
111- TargetPort : intstr .FromInt (8080 ),
78+ g .By ("Creating backend service and pod" )
79+ createBackendServiceAndPod (ctx , oc , ns , "dualstack-backend" )
80+
81+ g .By ("Creating an edge-terminated route" )
82+ routeHost := "dualstack-test." + shardFQDN
83+ createEdgeRoute (ctx , oc , ns , "dualstack-route" , routeHost , "dualstack-backend" )
84+
85+ g .By ("Waiting for the route to be admitted" )
86+ waitForRouteAdmitted (ctx , oc , ns , "dualstack-route" , routeHost )
87+
88+ g .By ("Creating exec pod for curl tests" )
89+ execPod := exutil .CreateExecPodOrFail (oc .AdminKubeClient (), ns , "execpod" )
90+ defer func () {
91+ oc .AdminKubeClient ().CoreV1 ().Pods (ns ).Delete (ctx , execPod .Name , * metav1 .NewDeleteOptions (1 ))
92+ }()
93+
94+ g .By ("Verifying route is reachable over IPv4" )
95+ err = waitForRouteResponse (ns , execPod .Name , routeHost , "-4" , 10 * time .Minute )
96+ o .Expect (err ).NotTo (o .HaveOccurred (), "route not reachable over IPv4" )
97+
98+ g .By ("Verifying route is reachable over IPv6" )
99+ err = waitForRouteResponse (ns , execPod .Name , routeHost , "-6" , 10 * time .Minute )
100+ o .Expect (err ).NotTo (o .HaveOccurred (), "route not reachable over IPv6" )
101+ })
102+
103+ g .It ("should be reachable via IPv4 through a Classic LB ingress controller on a dual-stack cluster" , func () {
104+ ctx := context .Background ()
105+
106+ g .By ("Checking that the Infrastructure CR has a DualStack IPFamily" )
107+ requireAWSDualStack (ctx , oc )
108+
109+ g .By ("Getting the default ingress domain" )
110+ defaultDomain , err := getDefaultIngressClusterDomainName (oc , time .Minute )
111+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to find default domain name" )
112+
113+ ns := oc .KubeFramework ().Namespace .Name
114+ baseDomain := strings .TrimPrefix (defaultDomain , "apps." )
115+ shardFQDN := "clb." + baseDomain
116+
117+ // Deploy the shard first so DNS and LB can provision while we set up the backend.
118+ g .By ("Deploying a new router shard with Classic LB" )
119+ shardIngressCtrl , err := shard .DeployNewRouterShard (oc , 10 * time .Minute , shard.Config {
120+ Domain : shardFQDN ,
121+ Type : oc .Namespace (),
122+ LoadBalancer : & operatorv1.LoadBalancerStrategy {
123+ Scope : operatorv1 .ExternalLoadBalancer ,
124+ ProviderParameters : & operatorv1.ProviderLoadBalancerParameters {
125+ Type : operatorv1 .AWSLoadBalancerProvider ,
126+ AWS : & operatorv1.AWSLoadBalancerParameters {
127+ Type : operatorv1 .AWSClassicLoadBalancer ,
112128 },
113129 },
114130 },
115- }
116- _ , err = oc .AdminKubeClient ().CoreV1 ().Services (ns ).Create (ctx , service , metav1.CreateOptions {})
131+ })
132+ defer func () {
133+ if shardIngressCtrl != nil {
134+ if err := oc .AdminOperatorClient ().OperatorV1 ().IngressControllers (shardIngressCtrl .Namespace ).Delete (ctx , shardIngressCtrl .Name , metav1.DeleteOptions {}); err != nil {
135+ e2e .Logf ("deleting ingress controller failed: %v\n " , err )
136+ }
137+ }
138+ }()
139+ o .Expect (err ).NotTo (o .HaveOccurred (), "new router shard did not rollout" )
140+
141+ g .By ("Labelling the namespace for the shard" )
142+ err = oc .AsAdmin ().Run ("label" ).Args ("namespace" , oc .Namespace (), "type=" + oc .Namespace ()).Execute ()
117143 o .Expect (err ).NotTo (o .HaveOccurred ())
118144
119- g .By ("Creating backend pod" )
120- backendPod := & corev1.Pod {
121- ObjectMeta : metav1.ObjectMeta {
122- Name : "dualstack-backend" ,
123- Labels : map [string ]string {
124- "app" : "dualstack-backend" ,
145+ g .By ("Creating backend service and pod" )
146+ createBackendServiceAndPod (ctx , oc , ns , "classic-backend" )
147+
148+ g .By ("Creating an edge-terminated route" )
149+ routeHost := "classic-test." + shardFQDN
150+ createEdgeRoute (ctx , oc , ns , "classic-route" , routeHost , "classic-backend" )
151+
152+ g .By ("Waiting for the route to be admitted" )
153+ waitForRouteAdmitted (ctx , oc , ns , "classic-route" , routeHost )
154+
155+ g .By ("Creating exec pod for curl tests" )
156+ execPod := exutil .CreateExecPodOrFail (oc .AdminKubeClient (), ns , "execpod" )
157+ defer func () {
158+ oc .AdminKubeClient ().CoreV1 ().Pods (ns ).Delete (ctx , execPod .Name , * metav1 .NewDeleteOptions (1 ))
159+ }()
160+
161+ g .By ("Verifying route is reachable over IPv4" )
162+ err = waitForRouteResponse (ns , execPod .Name , routeHost , "-4" , 10 * time .Minute )
163+ o .Expect (err ).NotTo (o .HaveOccurred (), "route not reachable over IPv4" )
164+ })
165+ })
166+
167+ func requireAWSDualStack (ctx context.Context , oc * exutil.CLI ) {
168+ infra , err := oc .AdminConfigClient ().ConfigV1 ().Infrastructures ().Get (ctx , "cluster" , metav1.GetOptions {})
169+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to get infrastructure CR" )
170+
171+ if infra .Status .PlatformStatus == nil || infra .Status .PlatformStatus .Type != configv1 .AWSPlatformType {
172+ g .Skip ("Test requires AWS platform" )
173+ }
174+ if infra .Status .PlatformStatus .AWS == nil {
175+ g .Skip ("AWS platform status is not set" )
176+ }
177+ ipFamily := infra .Status .PlatformStatus .AWS .IPFamily
178+ if ipFamily != configv1 .DualStackIPv4Primary && ipFamily != configv1 .DualStackIPv6Primary {
179+ g .Skip (fmt .Sprintf ("Test requires DualStack IPFamily, got %q" , ipFamily ))
180+ }
181+ }
182+
183+ func createBackendServiceAndPod (ctx context.Context , oc * exutil.CLI , ns , name string ) {
184+ service := & corev1.Service {
185+ ObjectMeta : metav1.ObjectMeta {
186+ Name : name ,
187+ Labels : map [string ]string {"app" : name },
188+ },
189+ Spec : corev1.ServiceSpec {
190+ Selector : map [string ]string {"app" : name },
191+ IPFamilyPolicy : func () * corev1.IPFamilyPolicy {
192+ p := corev1 .IPFamilyPolicyPreferDualStack
193+ return & p
194+ }(),
195+ Ports : []corev1.ServicePort {
196+ {
197+ Name : "http" ,
198+ Port : 8080 ,
199+ Protocol : corev1 .ProtocolTCP ,
200+ TargetPort : intstr .FromInt (8080 ),
125201 },
126202 },
127- Spec : corev1.PodSpec {
128- TerminationGracePeriodSeconds : utilpointer .Int64 (1 ),
129- Containers : []corev1.Container {
130- {
131- Name : "server" ,
132- Image : image .ShellImage (),
133- ImagePullPolicy : corev1 .PullIfNotPresent ,
134- Command : []string {"/bin/bash" , "-c" , `while true; do
203+ },
204+ }
205+ _ , err := oc .AdminKubeClient ().CoreV1 ().Services (ns ).Create (ctx , service , metav1.CreateOptions {})
206+ o .Expect (err ).NotTo (o .HaveOccurred ())
207+
208+ pod := & corev1.Pod {
209+ ObjectMeta : metav1.ObjectMeta {
210+ Name : name ,
211+ Labels : map [string ]string {"app" : name },
212+ },
213+ Spec : corev1.PodSpec {
214+ TerminationGracePeriodSeconds : utilpointer .Int64 (1 ),
215+ Containers : []corev1.Container {
216+ {
217+ Name : "server" ,
218+ Image : image .ShellImage (),
219+ ImagePullPolicy : corev1 .PullIfNotPresent ,
220+ Command : []string {"/bin/bash" , "-c" , `while true; do
135221printf "HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: text/plain\r\n\r\nOK" | ncat -l 8080 --send-only || true
136222done` },
137- Ports : []corev1.ContainerPort {
138- {
139- ContainerPort : 8080 ,
140- Name : "http" ,
141- Protocol : corev1 .ProtocolTCP ,
142- },
223+ Ports : []corev1.ContainerPort {
224+ {
225+ ContainerPort : 8080 ,
226+ Name : "http" ,
227+ Protocol : corev1 .ProtocolTCP ,
143228 },
144229 },
145230 },
146231 },
147- }
148- _ , err = oc .AdminKubeClient ().CoreV1 ().Pods (ns ).Create (ctx , backendPod , metav1.CreateOptions {})
149- o .Expect (err ).NotTo (o .HaveOccurred ())
232+ },
233+ }
234+ _ , err = oc .AdminKubeClient ().CoreV1 ().Pods (ns ).Create (ctx , pod , metav1.CreateOptions {})
235+ o .Expect (err ).NotTo (o .HaveOccurred ())
150236
151- g . By ( "Waiting for backend pod to be running" )
152- e2e . ExpectNoError ( e2epod . WaitForPodRunningInNamespaceSlow ( ctx , oc . KubeClient (), "dualstack-backend" , ns ), "backend pod not running" )
237+ e2e . ExpectNoError ( e2epod . WaitForPodRunningInNamespaceSlow ( ctx , oc . KubeClient (), name , ns ), " backend pod not running" )
238+ }
153239
154- g .By ("Creating an edge-terminated route" )
155- routeType := oc .Namespace ()
156- route := routev1.Route {
157- ObjectMeta : metav1.ObjectMeta {
158- Name : "dualstack-route" ,
159- Labels : map [string ]string {
160- "type" : routeType ,
161- },
240+ func createEdgeRoute (ctx context.Context , oc * exutil.CLI , ns , name , host , serviceName string ) {
241+ route := routev1.Route {
242+ ObjectMeta : metav1.ObjectMeta {
243+ Name : name ,
244+ Labels : map [string ]string {
245+ "type" : oc .Namespace (),
162246 },
163- Spec : routev1.RouteSpec {
164- Host : "dualstack-test." + shardFQDN ,
165- Port : & routev1.RoutePort {
166- TargetPort : intstr .FromInt (8080 ),
167- },
168- TLS : & routev1.TLSConfig {
169- Termination : routev1 .TLSTerminationEdge ,
170- InsecureEdgeTerminationPolicy : routev1 .InsecureEdgeTerminationPolicyRedirect ,
171- },
172- To : routev1.RouteTargetReference {
173- Kind : "Service" ,
174- Name : "dualstack-backend" ,
175- Weight : utilpointer .Int32 (100 ),
176- },
177- WildcardPolicy : routev1 .WildcardPolicyNone ,
247+ },
248+ Spec : routev1.RouteSpec {
249+ Host : host ,
250+ Port : & routev1.RoutePort {
251+ TargetPort : intstr .FromInt (8080 ),
178252 },
179- }
180- _ , err = oc .RouteClient ().RouteV1 ().Routes (ns ).Create (ctx , & route , metav1.CreateOptions {})
181- o .Expect (err ).NotTo (o .HaveOccurred ())
253+ TLS : & routev1.TLSConfig {
254+ Termination : routev1 .TLSTerminationEdge ,
255+ InsecureEdgeTerminationPolicy : routev1 .InsecureEdgeTerminationPolicyRedirect ,
256+ },
257+ To : routev1.RouteTargetReference {
258+ Kind : "Service" ,
259+ Name : serviceName ,
260+ Weight : utilpointer .Int32 (100 ),
261+ },
262+ WildcardPolicy : routev1 .WildcardPolicyNone ,
263+ },
264+ }
265+ _ , err := oc .RouteClient ().RouteV1 ().Routes (ns ).Create (ctx , & route , metav1.CreateOptions {})
266+ o .Expect (err ).NotTo (o .HaveOccurred ())
267+ }
182268
183- g .By ("Waiting for the route to be admitted" )
184- routeHost := "dualstack-test." + shardFQDN
185- err = wait .PollImmediate (5 * time .Second , 5 * time .Minute , func () (bool , error ) {
186- r , err := oc .RouteClient ().RouteV1 ().Routes (ns ).Get (ctx , "dualstack-route" , metav1.GetOptions {})
187- if err != nil {
188- e2e .Logf ("failed to get route: %v, retrying..." , err )
189- return false , nil
190- }
191- for _ , ingress := range r .Status .Ingress {
192- if ingress .Host == routeHost {
193- for _ , condition := range ingress .Conditions {
194- if condition .Type == routev1 .RouteAdmitted && condition .Status == corev1 .ConditionTrue {
195- return true , nil
196- }
269+ func waitForRouteAdmitted (ctx context.Context , oc * exutil.CLI , ns , name , host string ) {
270+ err := wait .PollImmediate (5 * time .Second , 5 * time .Minute , func () (bool , error ) {
271+ r , err := oc .RouteClient ().RouteV1 ().Routes (ns ).Get (ctx , name , metav1.GetOptions {})
272+ if err != nil {
273+ e2e .Logf ("failed to get route: %v, retrying..." , err )
274+ return false , nil
275+ }
276+ for _ , ingress := range r .Status .Ingress {
277+ if ingress .Host == host {
278+ for _ , condition := range ingress .Conditions {
279+ if condition .Type == routev1 .RouteAdmitted && condition .Status == corev1 .ConditionTrue {
280+ return true , nil
197281 }
198282 }
199283 }
200- return false , nil
201- })
202- o .Expect (err ).NotTo (o .HaveOccurred (), "route was not admitted" )
203-
204- g .By ("Creating exec pod for curl tests" )
205- execPod := exutil .CreateExecPodOrFail (oc .AdminKubeClient (), ns , "execpod" )
206- defer func () {
207- oc .AdminKubeClient ().CoreV1 ().Pods (ns ).Delete (ctx , execPod .Name , * metav1 .NewDeleteOptions (1 ))
208- }()
209-
210- g .By ("Verifying route is reachable over IPv4" )
211- err = waitForDualStackRouteResponse (ns , execPod .Name , routeHost , "-4" , 10 * time .Minute )
212- o .Expect (err ).NotTo (o .HaveOccurred (), "route not reachable over IPv4" )
213-
214- g .By ("Verifying route is reachable over IPv6" )
215- err = waitForDualStackRouteResponse (ns , execPod .Name , routeHost , "-6" , 10 * time .Minute )
216- o .Expect (err ).NotTo (o .HaveOccurred (), "route not reachable over IPv6" )
284+ }
285+ return false , nil
217286 })
218- })
287+ o .Expect (err ).NotTo (o .HaveOccurred (), "route was not admitted" )
288+ }
219289
220- func waitForDualStackRouteResponse (ns , execPodName , host , ipFlag string , timeout time.Duration ) error {
290+ func waitForRouteResponse (ns , execPodName , host , ipFlag string , timeout time.Duration ) error {
221291 curlCmd := fmt .Sprintf ("curl %s -k -v -m 10 --connect-timeout 5 -o /dev/null https://%s 2>&1" , ipFlag , host )
222292 var lastOutput string
223293 err := wait .PollImmediate (5 * time .Second , timeout , func () (bool , error ) {
0 commit comments