@@ -11,6 +11,7 @@ import (
1111 "time"
1212
1313 "github.com/openshift/api/features"
14+ iov1 "github.com/openshift/api/operatoringress/v1"
1415 operatorclient "github.com/openshift/cluster-ingress-operator/pkg/operator/client"
1516 operatorcontroller "github.com/openshift/cluster-ingress-operator/pkg/operator/controller"
1617
@@ -101,6 +102,10 @@ func TestGatewayAPI(t *testing.T) {
101102 t .Run ("testGatewayAPIObjects" , testGatewayAPIObjects )
102103 t .Run ("testGatewayAPIManualDeployment" , testGatewayAPIManualDeployment )
103104 t .Run ("testGatewayAPIIstioInstallation" , testGatewayAPIIstioInstallation )
105+ t .Run ("testGatewayAPIDNS" , testGatewayAPIDNS )
106+ t .Run ("testGatewayAPIDNSListenerUpdate" , testGatewayAPIDNSListenerUpdate )
107+ t .Run ("testGatewayAPIDNSListenerWithNoHostname" , testGatewayAPIDNSListenerWithNoHostname )
108+
104109 } else {
105110 t .Log ("Gateway API Controller not enabled, skipping controller tests" )
106111 }
@@ -423,6 +428,279 @@ func testGatewayAPIRBAC(t *testing.T) {
423428 }
424429}
425430
431+ func testGatewayAPIDNS (t * testing.T ) {
432+ domain := "gws." + dnsConfig .Spec .BaseDomain
433+
434+ gatewayClass , err := createGatewayClass (operatorcontroller .OpenShiftDefaultGatewayClassName , operatorcontroller .OpenShiftGatewayClassControllerName )
435+ if err != nil {
436+ t .Fatalf ("failed to create gatewayclass: %v" , err )
437+ }
438+
439+ testCases := []struct {
440+ name string
441+ createGateways []testGateway
442+ expectedListenerConditions []metav1.Condition
443+ expectedDNSRecords map [expectedDnsRecord ]bool
444+ }{
445+ // TODO: In this case Gateway Listeners should be reported as conflicted. To be fixed in the future release.
446+ {
447+ name : "multipleGatewaysSameListenerHostname" ,
448+ createGateways : []testGateway {
449+ {gatewayName : "gw1" ,
450+ namespace : operatorcontroller .DefaultOperandNamespace ,
451+ listeners : []testListener {
452+ {
453+ name : "http" ,
454+ hostname : ptr .To ("abc." + domain ),
455+ },
456+ },
457+ },
458+ {gatewayName : "gw2" ,
459+ namespace : operatorcontroller .DefaultOperandNamespace ,
460+ listeners : []testListener {
461+ {
462+ name : "http" ,
463+ hostname : ptr .To ("abc." + domain )},
464+ },
465+ },
466+ },
467+ expectedListenerConditions : []metav1.Condition {
468+ {Type : "Accepted" , Status : metav1 .ConditionTrue },
469+ {Type : "Conflicted" , Status : metav1 .ConditionFalse },
470+ {Type : "Programmed" , Status : metav1 .ConditionTrue },
471+ {Type : "ResolvedRefs" , Status : metav1 .ConditionTrue },
472+ },
473+ expectedDNSRecords : map [expectedDnsRecord ]bool {
474+ {dnsName : "abc." + domain + "." , gatewayName : "gw1" }: true ,
475+ {dnsName : "abc." + domain + "." , gatewayName : "gw2" }: true ,
476+ },
477+ },
478+ {
479+ name : "gatewayListenersWithOverlappingHostname" ,
480+ createGateways : []testGateway {
481+ {gatewayName : "gw3" ,
482+ namespace : operatorcontroller .DefaultOperandNamespace ,
483+ listeners : []testListener {
484+ {
485+ name : "http" ,
486+ hostname : ptr .To ("qwe." + domain )},
487+ },
488+ },
489+ {gatewayName : "gw4" ,
490+ namespace : operatorcontroller .DefaultOperandNamespace ,
491+ listeners : []testListener {
492+ {
493+ name : "http" ,
494+ hostname : ptr .To ("*." + domain ),
495+ },
496+ },
497+ },
498+ },
499+ expectedListenerConditions : []metav1.Condition {
500+ {Type : "Accepted" , Status : metav1 .ConditionTrue },
501+ {Type : "Conflicted" , Status : metav1 .ConditionFalse },
502+ {Type : "Programmed" , Status : metav1 .ConditionTrue },
503+ {Type : "ResolvedRefs" , Status : metav1 .ConditionTrue },
504+ },
505+ expectedDNSRecords : map [expectedDnsRecord ]bool {
506+ {dnsName : "qwe." + domain + "." , gatewayName : "gw3" }: true ,
507+ {dnsName : "*." + domain + "." , gatewayName : "gw4" }: true ,
508+ },
509+ },
510+ }
511+
512+ for _ , tc := range testCases {
513+ t .Run (tc .name , func (t * testing.T ) {
514+ var gateways []* gatewayapiv1.Gateway
515+
516+ // Create gateways
517+ for _ , gateway := range tc .createGateways {
518+ createdGateway , err := createGatewayWithListeners (gatewayClass , gateway .gatewayName , gateway .namespace , gateway .listeners )
519+ gateways = append (gateways , createdGateway )
520+ if err != nil {
521+ t .Fatalf ("failed to create gateway %s: %v" , gateway .gatewayName , err )
522+ }
523+ }
524+
525+ t .Cleanup (func () {
526+ for _ , gateway := range gateways {
527+ if err := kclient .Delete (context .TODO (), gateway ); err != nil {
528+ if errors .IsNotFound (err ) {
529+ continue
530+ }
531+ t .Errorf ("Failed to delete gateway %q: %v" , gateway .Name , err )
532+ }
533+ }
534+ })
535+
536+ for _ , gateway := range gateways {
537+ gateway , err := assertGatewaySuccessful (t , operatorcontroller .DefaultOperandNamespace , gateway .Name )
538+ if err != nil {
539+ t .Errorf ("Failed to create %s gateway: %v" , gateway .Name , err )
540+ }
541+ }
542+
543+ if err := assertExpectedDNSRecords (t , tc .expectedDNSRecords ); err != nil {
544+ t .Fatalf ("dnsRecord expectations not met: %v" , err )
545+ }
546+
547+ t .Logf ("Check if gateways listener conditions match expected state." )
548+ for _ , gateway := range gateways {
549+ for _ , listener := range gateway .Spec .Listeners {
550+ if err := waitForGatewayListenerCondition (t , types.NamespacedName {Namespace : gateway .Namespace , Name : gateway .Name }, string (listener .Name ), tc .expectedListenerConditions ... ); err != nil {
551+ t .Fatalf ("did not get expected listener %s condition: %v" , listener .Name , err )
552+ } else {
553+ t .Logf ("gateway: %s listener %s conditions match expected state." , gateway .Name , listener .Name )
554+ }
555+ }
556+ }
557+ })
558+ }
559+
560+ }
561+
562+ func testGatewayAPIDNSListenerUpdate (t * testing.T ) {
563+ gatewayClass , err := createGatewayClass (operatorcontroller .OpenShiftDefaultGatewayClassName , operatorcontroller .OpenShiftGatewayClassControllerName )
564+ if err != nil {
565+ t .Fatalf ("failed to create gatewayclass: %v" , err )
566+ }
567+
568+ domain := "gws." + dnsConfig .Spec .BaseDomain
569+
570+ gateway , err := createGatewayWithListeners (gatewayClass , "test-gateway-update" , operatorcontroller .DefaultOperandNamespace , []testListener {
571+ {name : "http-listener1" , hostname : ptr .To ("foo." + domain )},
572+ {name : "http-listener2" , hostname : ptr .To ("bar." + domain )},
573+ })
574+
575+ t .Cleanup (func () {
576+ if err := kclient .Delete (context .TODO (), gateway ); err != nil {
577+ if errors .IsNotFound (err ) {
578+ return
579+ }
580+ t .Errorf ("failed to delete gateway %q: %v" , gateway .Name , err )
581+ }
582+ })
583+
584+ if err != nil {
585+ t .Fatalf ("failed to create gateway with multiple listeners: %v" , err )
586+ }
587+ t .Logf ("Created gateway %s with multiple hostnames" , gateway .Name )
588+
589+ gateway , err = assertGatewaySuccessful (t , operatorcontroller .DefaultOperandNamespace , "test-gateway-update" )
590+ if err != nil {
591+ t .Fatalf ("failed to accept/program gateway test-gateway-update: %v" , err )
592+ }
593+
594+ if err := assertExpectedDNSRecords (t , map [expectedDnsRecord ]bool {
595+ {dnsName : "foo." + domain + "." , gatewayName : "test-gateway-update" }: true ,
596+ }); err != nil {
597+ t .Fatalf ("DNSRecord %s expectations not met: %v" , "foo." + domain + "." , err )
598+ }
599+
600+ // Fetch the latest version of the Gateway resource.
601+ if err := kclient .Get (context .TODO (), types.NamespacedName {Namespace : operatorcontroller .DefaultOperandNamespace , Name : gateway .Name }, gateway ); err != nil {
602+ t .Fatalf ("failed to get gateway resource: %v" , err )
603+ }
604+
605+ // Modify one gateway listener hostname
606+ newHostname := gatewayapiv1 .Hostname ("baz." + domain )
607+ gateway .Spec .Listeners [0 ].Hostname = & newHostname
608+
609+ if err := kclient .Update (context .TODO (), gateway ); err != nil {
610+ t .Fatalf ("failed to update gateway %v: %v" , gateway .Name , err )
611+ }
612+ t .Logf ("Modified gateway %s's listener hostname from %s to: %s" , gateway .Name , "foo." + domain + "." , "baz." + domain + "." )
613+
614+ if err := assertExpectedDNSRecords (t , map [expectedDnsRecord ]bool {
615+ {dnsName : "foo." + domain + "." , gatewayName : "test-gateway-update" }: false ,
616+ {dnsName : "bar." + domain + "." , gatewayName : "test-gateway-update" }: true ,
617+ {dnsName : "baz." + domain + "." , gatewayName : "test-gateway-update" }: true ,
618+ }); err != nil {
619+ t .Fatalf ("DNSRecord %s expectations not met: %v" , "baz." + domain + "." , err )
620+ }
621+
622+ // Fetch the latest version of the Gateway resource.
623+ if err := kclient .Get (context .TODO (), types.NamespacedName {Namespace : operatorcontroller .DefaultOperandNamespace , Name : gateway .Name }, gateway ); err != nil {
624+ t .Fatalf ("failed to get gateway resource: %v" , err )
625+ }
626+
627+ // Delete one of the listeners
628+ gateway .Spec .Listeners = gateway .Spec .Listeners [0 : len (gateway .Spec .Listeners )- 1 ]
629+
630+ if err := kclient .Update (context .TODO (), gateway ); err != nil {
631+ t .Fatalf ("failed to update gateway with removed listener: %v" , err )
632+ }
633+ t .Logf ("Deleted listener %s from gateway %s." , "http-listener2" , gateway .Name )
634+
635+ t .Logf ("Checking that the dnsRecord for %s gets removed after removing the listener." , "bar." + domain + "." )
636+
637+ if err := assertExpectedDNSRecords (t , map [expectedDnsRecord ]bool {
638+ {dnsName : "baz." + domain + "." , gatewayName : "test-gateway-update" }: true ,
639+ {dnsName : "bar." + domain + "." , gatewayName : "test-gateway-update" }: false ,
640+ }); err != nil {
641+ t .Fatalf ("expected bar.%s. to be deleted, but it was not" , domain )
642+ }
643+
644+ if err := kclient .Delete (context .TODO (), gateway ); err != nil {
645+ t .Errorf ("failed to delete gateway %q: %v" , gateway .Name , err )
646+ }
647+
648+ t .Logf ("Checking the remaining DNSRecord %s gets deleted after gateway deletion." , "baz." + domain + "." )
649+ if err := assertExpectedDNSRecords (t , map [expectedDnsRecord ]bool {
650+ {dnsName : "baz." + domain + "." , gatewayName : "test-gateway-update" }: false ,
651+ }); err != nil {
652+ t .Fatalf ("expected baz.%s. to be deleted, but it was not" , domain )
653+ }
654+ t .Logf ("Confirmed DNSRecord removed after gateway deletion." )
655+ }
656+
657+ func testGatewayAPIDNSListenerWithNoHostname (t * testing.T ) {
658+ gatewayClass , err := createGatewayClass (operatorcontroller .OpenShiftDefaultGatewayClassName , operatorcontroller .OpenShiftGatewayClassControllerName )
659+ if err != nil {
660+ t .Fatalf ("failed to create gatewayclass: %v" , err )
661+ }
662+
663+ gateway , err := createGatewayWithListeners (gatewayClass , "test-nohost-gateway" , operatorcontroller .DefaultOperandNamespace , []testListener {
664+ {name : "http-listener-no-host" , hostname : nil },
665+ })
666+
667+ t .Cleanup (func () {
668+ if err := kclient .Delete (context .TODO (), gateway ); err != nil {
669+ t .Errorf ("failed to delete gateway %q: %v" , gateway .Name , err )
670+ }
671+ })
672+
673+ if err != nil {
674+ t .Fatalf ("failed to create gateway with a listener with no hostname: %v" , err )
675+ }
676+
677+ gateway , err = assertGatewaySuccessful (t , operatorcontroller .DefaultOperandNamespace , "test-nohost-gateway" )
678+ if err != nil {
679+ t .Fatalf ("failed to accept/program gateway test-nohost-gateway: %v" , err )
680+ }
681+
682+ t .Logf ("Created gateway %s with a listener with no hostname" , gateway .Name )
683+
684+ err = wait .PollUntilContextTimeout (context .Background (), 1 * time .Second , 1 * time .Minute , false , func (context context.Context ) (bool , error ) {
685+ dnsRecords := & iov1.DNSRecordList {}
686+ // List all DNSRecords from the default operand namespace.
687+ if err := kclient .List (context , dnsRecords , client .InNamespace (operatorcontroller .DefaultOperandNamespace )); err != nil {
688+ t .Logf ("failed to list DNSRecords: %v; retrying..." , err )
689+ return false , nil
690+ }
691+
692+ for _ , record := range dnsRecords .Items {
693+ if record .Labels ["gateway.networking.k8s.io/gateway-name" ] == gateway .Name {
694+ t .Fatalf ("dnsrecord found while not expected: %v" , err )
695+ }
696+ }
697+ t .Logf ("No DNSRecord for gateway listener with no hostname, continuing polling..." )
698+ return false , nil
699+ })
700+ t .Logf ("Confirmed no DNSRecord created for gateway listener with no hostname." )
701+
702+ }
703+
426704// ensureCRDs tests that the Gateway API custom resource definitions exist.
427705func ensureCRDs (t * testing.T ) {
428706 t .Helper ()
0 commit comments