Skip to content

Commit 57e5a5c

Browse files
Merge pull request #29806 from melvinjoseph86/gatewaycontroller_enh-new
NE-2031: Enhancing the gateway controller test cases with two more function
2 parents 918f338 + 868d90c commit 57e5a5c

32 files changed

+1736
-100
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ require (
3434
github.com/onsi/ginkgo/v2 v2.21.0
3535
github.com/onsi/gomega v1.35.1
3636
github.com/opencontainers/go-digest v1.0.0
37-
github.com/openshift/api v0.0.0-20250425163235-9b80d67473bc
37+
github.com/openshift/api v0.0.0-20250513132935-9052dea86694
3838
github.com/openshift/apiserver-library-go v0.0.0-20250127121756-dc9a973f14ce
3939
github.com/openshift/build-machinery-go v0.0.0-20250102153059-e85a1a7ecb5c
40-
github.com/openshift/client-go v0.0.0-20250425165505-5f55ff6979a1
40+
github.com/openshift/client-go v0.0.0-20250513150353-9ea84fa6431b
4141
github.com/openshift/cluster-network-operator v0.0.0-20240708200319-1cd8678b38fb
4242
github.com/openshift/library-go v0.0.0-20250129210218-fe56c2cf5d70
4343
github.com/ovn-org/ovn-kubernetes/go-controller v0.0.0-20250118001652-a8b9c3c31417

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -603,14 +603,14 @@ github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jD
603603
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
604604
github.com/openshift-eng/openshift-tests-extension v0.0.0-20250220212757-b9c4d98a0c45 h1:hXpbYtP3iTh8oy/RKwKkcMziwchY3fIk95ciczf7cOA=
605605
github.com/openshift-eng/openshift-tests-extension v0.0.0-20250220212757-b9c4d98a0c45/go.mod h1:6gkP5f2HL0meusT0Aim8icAspcD1cG055xxBZ9yC68M=
606-
github.com/openshift/api v0.0.0-20250425163235-9b80d67473bc h1:BGKjHtYzBweOSu1UwTnNqtPbJZ4VzOTqVFlUDpP+6U8=
607-
github.com/openshift/api v0.0.0-20250425163235-9b80d67473bc/go.mod h1:yk60tHAmHhtVpJQo3TwVYq2zpuP70iJIFDCmeKMIzPw=
606+
github.com/openshift/api v0.0.0-20250513132935-9052dea86694 h1:kPnk1+m89LJHexYsTP+MVM9OgJLxcpUR3vRdMQNF66s=
607+
github.com/openshift/api v0.0.0-20250513132935-9052dea86694/go.mod h1:yk60tHAmHhtVpJQo3TwVYq2zpuP70iJIFDCmeKMIzPw=
608608
github.com/openshift/apiserver-library-go v0.0.0-20250127121756-dc9a973f14ce h1:w0Up6YV1APcn20v/1h5IfuToz96o2pVqZyjzbw0yotU=
609609
github.com/openshift/apiserver-library-go v0.0.0-20250127121756-dc9a973f14ce/go.mod h1:kkSwH4osgejnRIyHfsfkv0V0xfmgH4Yk/VDObaJukHU=
610610
github.com/openshift/build-machinery-go v0.0.0-20250102153059-e85a1a7ecb5c h1:6XcszPFZpan4qll5XbdLll7n1So3IsPn28aw2j1obMo=
611611
github.com/openshift/build-machinery-go v0.0.0-20250102153059-e85a1a7ecb5c/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE=
612-
github.com/openshift/client-go v0.0.0-20250425165505-5f55ff6979a1 h1:2HPG58V07TrrSGBviNPd0PY42vYHPPCIEwj/pb9nUlY=
613-
github.com/openshift/client-go v0.0.0-20250425165505-5f55ff6979a1/go.mod h1:kH5mjMfcHCF0tEnxwvNJTLMnlbrEt3Ua+vMVGvBOK5w=
612+
github.com/openshift/client-go v0.0.0-20250513150353-9ea84fa6431b h1:ffS7iyeXP5PdiiVVssk2GCP6PqrVR4LdCdrKRcI+ogM=
613+
github.com/openshift/client-go v0.0.0-20250513150353-9ea84fa6431b/go.mod h1:ic71V3s+SXPaFMRd80eZhNvjEWom7s61GXE2wmYCfOI=
614614
github.com/openshift/cluster-network-operator v0.0.0-20240708200319-1cd8678b38fb h1:Dr0dbSQTAU9UaoAvimGjR+fsvwx2twJ5KR0s/jyAz88=
615615
github.com/openshift/cluster-network-operator v0.0.0-20240708200319-1cd8678b38fb/go.mod h1:LnhqxbWhAnhPwilJ4yX1/ly7wCMCYJKkaiSJQSh+Wjg=
616616
github.com/openshift/kubernetes v1.30.1-0.20250509031521-18f7f4cbd6ea h1:HcjGXaqrP+cnCMw6Ppqvqt1ryxCzTDJI2Hh+N9Ujxls=

test/extended/router/gatewayapicontroller.go

Lines changed: 156 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ package router
22

33
import (
44
"context"
5+
"crypto/tls"
56
"fmt"
7+
"net"
8+
"net/http"
69
"strings"
710
"time"
811

912
g "github.com/onsi/ginkgo/v2"
1013
o "github.com/onsi/gomega"
1114

1215
configv1 "github.com/openshift/api/config/v1"
16+
operatoringressv1 "github.com/openshift/api/operatoringress/v1"
17+
1318
exutil "github.com/openshift/origin/test/extended/util"
1419
corev1 "k8s.io/api/core/v1"
1520
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -32,6 +37,13 @@ var (
3237
}
3338
)
3439

40+
const (
41+
// Max time duration for the DNS resolution
42+
dnsResolutionTimeout = 10 * time.Minute
43+
// Max time duration for the Load balancer address
44+
loadBalancerReadyTimeout = 10 * time.Minute
45+
)
46+
3547
var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feature:Router][apigroup:gateway.networking.k8s.io]", g.Ordered, g.Serial, func() {
3648
defer g.GinkgoRecover()
3749
var (
@@ -40,6 +52,7 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
4052
err error
4153
gateways []string
4254
)
55+
4356
const (
4457
// The expected OSSM subscription name.
4558
expectedSubscriptionName = "servicemeshoperator3"
@@ -145,15 +158,15 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
145158

146159
g.By("Confirm that Istio CR is created and in healthy state")
147160
waitForIstioHealthy(oc)
148-
149161
})
162+
150163
g.It("Ensure default gatewayclass is accepted", func() {
151164

152165
g.By("Check if default GatewayClass is accepted after OLM resources are successful")
153166
errCheck := checkGatewayClass(oc, gatewayClassName)
154167
o.Expect(errCheck).NotTo(o.HaveOccurred(), "GatewayClass %q was not installed and accepted", gatewayClassName)
155-
156168
})
169+
157170
g.It("Ensure custom gatewayclass can be accepted", func() {
158171
customGatewayClassName := "custom-gatewayclass"
159172

@@ -182,7 +195,6 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
182195
})
183196

184197
g.It("Ensure LB, service, and dnsRecord are created for a Gateway object", func() {
185-
var lbAddress string
186198
g.By("Ensure default GatewayClass is accepted")
187199
errCheck := checkGatewayClass(oc, gatewayClassName)
188200
o.Expect(errCheck).NotTo(o.HaveOccurred(), "GatewayClass %q was not installed and accepted", gatewayClassName)
@@ -199,30 +211,10 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
199211
o.Expect(gwerr).NotTo(o.HaveOccurred(), "failed to create Gateway")
200212

201213
g.By("Verify the gateway's LoadBalancer service and DNSRecords")
202-
// check gateway LB service, note that External-IP might be hostname (AWS) or IP (Azure/GCP)
203-
lbService, err := oc.AdminKubeClient().CoreV1().Services("openshift-ingress").Get(context.Background(), gw+"-openshift-default", metav1.GetOptions{})
204-
o.Expect(err).NotTo(o.HaveOccurred())
205-
if lbService.Status.LoadBalancer.Ingress[0].Hostname != "" {
206-
lbAddress = lbService.Status.LoadBalancer.Ingress[0].Hostname
207-
} else {
208-
lbAddress = lbService.Status.LoadBalancer.Ingress[0].IP
209-
}
210-
e2e.Logf("The load balancer External-IP is: %v", lbAddress)
214+
assertGatewayLoadbalancerReady(oc, gw, gw+"-openshift-default")
211215

212-
gwlist, haerr := oc.AdminGatewayApiClient().GatewayV1().Gateways("openshift-ingress").Get(context.Background(), gw, metav1.GetOptions{})
213-
e2e.Logf("The gateway hostname address is %v ", gwlist.Status.Addresses[0].Value)
214-
o.Expect(haerr).NotTo(o.HaveOccurred())
215-
o.Expect(lbAddress).To(o.Equal(gwlist.Status.Addresses[0].Value))
216-
217-
// get the dnsrecord name
218-
dnsRecordName, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", "openshift-ingress", "dnsrecord", "-l", "gateway.networking.k8s.io/gateway-name="+gw, "-o=jsonpath={.items[0].metadata.name}").Output()
219-
o.Expect(err).NotTo(o.HaveOccurred())
220-
e2e.Logf("The gateway API dnsrecord name is: %v", dnsRecordName)
221-
// check status of published dnsrecord of the gateway, all zones should be True (not contains False)
222-
dnsRecordStatus, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", "openshift-ingress", "dnsrecord", dnsRecordName, `-o=jsonpath={.status.zones[*].conditions[0].status}`).Output()
223-
o.Expect(err).NotTo(o.HaveOccurred())
224-
e2e.Logf("The dnsrecords status of all zones: %v", dnsRecordStatus)
225-
o.Expect(dnsRecordStatus).NotTo(o.ContainSubstring("False"))
216+
// check the dns record is created and status of the published dnsrecord of all zones are True
217+
assertDNSRecordStatus(oc, gw)
226218
})
227219

228220
g.It("Ensure HTTPRoute object is created", func() {
@@ -241,12 +233,18 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
241233
_, gwerr := createAndCheckGateway(oc, gw, gatewayClassName, customDomain)
242234
o.Expect(gwerr).NotTo(o.HaveOccurred(), "Failed to create Gateway")
243235

236+
// make sure the DNSRecord is ready to use.
237+
assertDNSRecordStatus(oc, gw)
238+
244239
g.By("Create the http route using the custom gateway")
245240
defaultRoutename := "test-hostname." + customDomain
246241
createHttpRoute(oc, gw, "test-httproute", defaultRoutename, "echo-pod-"+gw)
247242

248243
g.By("Checking the http route using the default gateway is accepted")
249244
assertHttpRouteSuccessful(oc, gw, "test-httproute")
245+
246+
g.By("Validating the http connectivity to the backend application")
247+
assertHttpRouteConnection(defaultRoutename)
250248
})
251249
})
252250

@@ -331,33 +329,30 @@ func createAndCheckGateway(oc *exutil.CLI, gwname, gwclassname, domain string) (
331329
}
332330

333331
func checkGatewayStatus(oc *exutil.CLI, gwname, ingressNameSpace string) (*gatewayapiv1.Gateway, error) {
334-
var err error
335-
gateway := &gatewayapiv1.Gateway{}
336-
337-
waitErr := wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 10*time.Minute, false, func(context context.Context) (bool, error) {
338-
gateway, err = oc.AdminGatewayApiClient().GatewayV1().Gateways(ingressNameSpace).Get(context, gwname, metav1.GetOptions{})
332+
programmedGateway := &gatewayapiv1.Gateway{}
333+
if err := wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 10*time.Minute, false, func(context context.Context) (bool, error) {
334+
gateway, err := oc.AdminGatewayApiClient().GatewayV1().Gateways(ingressNameSpace).Get(context, gwname, metav1.GetOptions{})
339335
if err != nil {
340-
e2e.Logf("Failed to get gateway object, retrying...")
336+
e2e.Logf("Failed to get gateway %q: %v, retrying...", gwname, err)
341337
return false, nil
342338
}
343339
// Checking the gateway controller status
344340
for _, condition := range gateway.Status.Conditions {
345341
if condition.Type == string(gatewayapiv1.GatewayConditionProgrammed) {
346342
if condition.Status == metav1.ConditionTrue {
347-
e2e.Logf("The gateway controller is up and running")
343+
e2e.Logf("The gateway controller for gateway %q is programmed", gwname)
344+
programmedGateway = gateway
348345
return true, nil
349346
}
350347
}
351348
}
352-
e2e.Logf("Found gateway %q but the controller is still not programmed, retrying...", gateway.Name)
349+
e2e.Logf("Found gateway %q but the controller is still not programmed, retrying...", gwname)
353350
return false, nil
354-
})
355-
356-
if waitErr != nil {
357-
return nil, fmt.Errorf("Timed out waiting for gateway %q to become programmed: %w", gateway.Name, waitErr)
351+
}); err != nil {
352+
return nil, fmt.Errorf("timed out waiting for gateway %q to become programmed: %w", gwname, err)
358353
}
359-
e2e.Logf("Gateway %q successfully programmed!", gateway.Name)
360-
return gateway, nil
354+
e2e.Logf("Gateway %q successfully programmed!", gwname)
355+
return programmedGateway, nil
361356
}
362357

363358
// buildGateway initializes the Gateway and returns its address.
@@ -377,6 +372,77 @@ func buildGateway(name, namespace, gcname, fromNs, domain string) *gatewayapiv1.
377372
}
378373
}
379374

375+
// assertGatewayLoadbalancerReady verifies that the given gateway has the service's load balancer address assigned.
376+
func assertGatewayLoadbalancerReady(oc *exutil.CLI, gwName, gwServiceName string) {
377+
// check gateway LB service, note that External-IP might be hostname (AWS) or IP (Azure/GCP)
378+
var lbAddress string
379+
err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, loadBalancerReadyTimeout, false, func(context context.Context) (bool, error) {
380+
lbService, err := oc.AdminKubeClient().CoreV1().Services("openshift-ingress").Get(context, gwServiceName, metav1.GetOptions{})
381+
if err != nil {
382+
e2e.Logf("Failed to get service %q: %v, retrying...", gwServiceName, err)
383+
return false, nil
384+
}
385+
if lbService.Status.LoadBalancer.Ingress[0].Hostname != "" {
386+
lbAddress = lbService.Status.LoadBalancer.Ingress[0].Hostname
387+
} else {
388+
lbAddress = lbService.Status.LoadBalancer.Ingress[0].IP
389+
}
390+
if lbAddress == "" {
391+
e2e.Logf("No load balancer address for service %q, retrying", gwServiceName)
392+
return false, nil
393+
}
394+
e2e.Logf("Got load balancer address for service %q: %v", gwServiceName, lbAddress)
395+
396+
gw, err := oc.AdminGatewayApiClient().GatewayV1().Gateways("openshift-ingress").Get(context, gwName, metav1.GetOptions{})
397+
if err != nil {
398+
e2e.Logf("Failed to get gateway %q, retrying...", gwName)
399+
return false, nil
400+
}
401+
for _, gwAddr := range gw.Status.Addresses {
402+
if gwAddr.Value == lbAddress {
403+
return true, nil
404+
}
405+
}
406+
407+
e2e.Logf("Gateway %q does not have service load balancer address, retrying...", gwName)
408+
return false, nil
409+
})
410+
o.Expect(err).NotTo(o.HaveOccurred(), "Timed out waiting for gateway %q to get load balancer address of service %q", gwName, gwServiceName)
411+
}
412+
413+
// assertDNSRecordStatus polls until the DNSRecord's status in the default operand namespace is True.
414+
func assertDNSRecordStatus(oc *exutil.CLI, gatewayName string) {
415+
// find the DNS Record and confirm its zone status is True
416+
err := wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 10*time.Minute, false, func(context context.Context) (bool, error) {
417+
gatewayDNSRecord := &operatoringressv1.DNSRecord{}
418+
gatewayDNSRecords, err := oc.AdminIngressClient().IngressV1().DNSRecords("openshift-ingress").List(context, metav1.ListOptions{})
419+
if err != nil {
420+
e2e.Logf("Failed to list DNS records for gateway %q: %v, retrying...", gatewayName, err)
421+
return false, nil
422+
}
423+
424+
// get the desired DNS records of the given gateway
425+
for _, record := range gatewayDNSRecords.Items {
426+
if record.Labels["gateway.networking.k8s.io/gateway-name"] == gatewayName {
427+
gatewayDNSRecord = &record
428+
break
429+
}
430+
}
431+
432+
// checking the gateway DNS record status
433+
for _, zone := range gatewayDNSRecord.Status.Zones {
434+
for _, condition := range zone.Conditions {
435+
if condition.Type == "Published" && condition.Status == "True" {
436+
return true, nil
437+
}
438+
}
439+
}
440+
e2e.Logf("DNS record %q is not ready, retrying...", gatewayDNSRecord.Name)
441+
return false, nil
442+
})
443+
o.Expect(err).NotTo(o.HaveOccurred(), "Timed out waiting for gateway %q DNSRecord to become ready", gatewayName)
444+
}
445+
380446
// createHttpRoute checks if the HTTPRoute can be created.
381447
// If it can't an error is returned.
382448
func createHttpRoute(oc *exutil.CLI, gwName, routeName, hostname, backendRefname string) (*gatewayapiv1.HTTPRoute, error) {
@@ -546,6 +612,56 @@ func assertHttpRouteSuccessful(oc *exutil.CLI, gwName, name string) (*gatewayapi
546612
return checkHttpRoute, nil
547613
}
548614

615+
// assertHttpRouteConnection checks if the http route of the given name replies successfully,
616+
// and returns an error if not
617+
func assertHttpRouteConnection(hostname string) {
618+
// Create the http client to check the response status code.
619+
client := &http.Client{
620+
Timeout: 10 * time.Second,
621+
Transport: &http.Transport{
622+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
623+
},
624+
}
625+
626+
err := wait.PollUntilContextTimeout(context.Background(), 20*time.Second, dnsResolutionTimeout, false, func(context context.Context) (bool, error) {
627+
_, err := net.LookupHost(hostname)
628+
if err != nil {
629+
e2e.Logf("[%v] Failed to resolve HTTP route's hostname %q: %v, retrying...", time.Now(), hostname, err)
630+
return false, nil
631+
}
632+
return true, nil
633+
})
634+
o.Expect(err).NotTo(o.HaveOccurred(), "Timed out waiting for HTTP route's hostname %q to be resolved: %v", hostname, err)
635+
636+
// Wait for http route to respond, and when it does, check for the status code.
637+
err = wait.PollUntilContextTimeout(context.Background(), 5*time.Second, 5*time.Minute, false, func(context context.Context) (bool, error) {
638+
statusCode, err := getHttpResponse(client, hostname)
639+
if err != nil {
640+
e2e.Logf("HTTP GET request to %q failed: %v, retrying...", hostname, err)
641+
return false, nil
642+
}
643+
if statusCode != http.StatusOK {
644+
e2e.Logf("Unexpected status code for HTTP GET request to %q: %v, retrying...", hostname, statusCode)
645+
return false, nil // retry on 503 as pod/service may not be ready
646+
}
647+
return true, nil
648+
})
649+
o.Expect(err).NotTo(o.HaveOccurred(), "Timed out waiting for successful HTTP GET response from %q: %v", hostname, err)
650+
}
651+
652+
func getHttpResponse(client *http.Client, hostname string) (int, error) {
653+
// Send the HTTP request.
654+
response, err := client.Get("http://" + hostname)
655+
if err != nil {
656+
return 0, err
657+
}
658+
659+
// Close response body.
660+
defer response.Body.Close()
661+
662+
return response.StatusCode, nil
663+
}
664+
549665
// Check for the existence of the okd-scos string in the version name to determine if it is OKD
550666
func isOKD(oc *exutil.CLI) (bool, error) {
551667
current, err := exutil.GetCurrentVersion(context.TODO(), oc.AdminConfig())

test/extended/util/client.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ import (
6868
mcv1client "github.com/openshift/client-go/machineconfiguration/clientset/versioned"
6969
oauthv1client "github.com/openshift/client-go/oauth/clientset/versioned"
7070
operatorv1client "github.com/openshift/client-go/operator/clientset/versioned"
71+
ingressv1client "github.com/openshift/client-go/operatoringress/clientset/versioned"
7172
projectv1client "github.com/openshift/client-go/project/clientset/versioned"
7273
quotav1client "github.com/openshift/client-go/quota/clientset/versioned"
7374
routev1client "github.com/openshift/client-go/route/clientset/versioned"
@@ -732,6 +733,10 @@ func (c *CLI) AdminImageClient() imagev1client.Interface {
732733
return imagev1client.NewForConfigOrDie(c.AdminConfig())
733734
}
734735

736+
func (c *CLI) AdminIngressClient() ingressv1client.Interface {
737+
return ingressv1client.NewForConfigOrDie(c.AdminConfig())
738+
}
739+
735740
func (c *CLI) AdminOAuthClient() oauthv1client.Interface {
736741
return oauthv1client.NewForConfigOrDie(c.AdminConfig())
737742
}

vendor/github.com/openshift/api/.golangci.yaml

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/openshift/api/README.md

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/openshift/api/config/v1/types_infrastructure.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)