@@ -6,6 +6,7 @@ package e2e
66import (
77 "context"
88 "fmt"
9+ "math/rand/v2"
910 "strings"
1011 "testing"
1112 "time"
@@ -16,6 +17,10 @@ import (
1617 operatorclient "github.com/openshift/cluster-ingress-operator/pkg/operator/client"
1718 operatorcontroller "github.com/openshift/cluster-ingress-operator/pkg/operator/controller"
1819 util "github.com/openshift/cluster-ingress-operator/pkg/util"
20+ "github.com/stretchr/testify/assert"
21+ "github.com/stretchr/testify/require"
22+ condutils "k8s.io/apimachinery/pkg/api/meta"
23+ "k8s.io/apimachinery/pkg/fields"
1924
2025 corev1 "k8s.io/api/core/v1"
2126 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -93,21 +98,22 @@ func TestGatewayAPI(t *testing.T) {
9398 // TODO: Uninstall OSSM after test is completed.
9499 })
95100
96- t .Run ("testGatewayAPIResources" , testGatewayAPIResources )
101+ // t.Run("testGatewayAPIResources", testGatewayAPIResources)
97102 if gatewayAPIControllerEnabled {
98- t .Run ("testGatewayAPIObjects" , testGatewayAPIObjects )
103+ /* t.Run("testGatewayAPIObjects", testGatewayAPIObjects)
99104 t.Run("testGatewayAPIManualDeployment", testGatewayAPIManualDeployment)
100105 t.Run("testGatewayAPIIstioInstallation", testGatewayAPIIstioInstallation)
101106 t.Run("testGatewayAPIDNS", testGatewayAPIDNS)
102107 t.Run("testGatewayAPIDNSListenerUpdate", testGatewayAPIDNSListenerUpdate)
103- t .Run ("testGatewayAPIDNSListenerWithNoHostname" , testGatewayAPIDNSListenerWithNoHostname )
108+ t.Run("testGatewayAPIDNSListenerWithNoHostname", testGatewayAPIDNSListenerWithNoHostname)*/
109+ t .Run ("testGatewayOpenshiftConditions" , testGatewayOpenshiftConditions )
104110
105111 } else {
106112 t .Log ("Gateway API Controller not enabled, skipping controller tests" )
107113 }
108- t .Run ("testGatewayAPIResourcesProtection" , testGatewayAPIResourcesProtection )
114+ /* t.Run("testGatewayAPIResourcesProtection", testGatewayAPIResourcesProtection)
109115 t.Run("testGatewayAPIRBAC", testGatewayAPIRBAC)
110- t .Run ("testOperatorDegradedCondition" , testOperatorDegradedCondition )
116+ t.Run("testOperatorDegradedCondition", testOperatorDegradedCondition)*/
111117}
112118
113119// testGatewayAPIResources tests that Gateway API Custom Resource Definitions are available.
@@ -587,7 +593,323 @@ func testGatewayAPIDNS(t *testing.T) {
587593 }
588594 })
589595 }
596+ }
597+
598+ // This e2e test will verify the following scenarios:
599+ // 1 - Creating a gateway with the right base domain but outside of `openshift-ingress`
600+ // namespace will not generate a DNSRecord nor add conditions to the gateway
601+ // 2 - Creating a Gateway on `openshift-ingress` namespace using the wrong base
602+ // domain should add DNS conditions that there are no managed zones for this case
603+ // 3 - Creating a Gateway with the right base domain on `openshift-ingress` will
604+ // add the conditions on the gateway reflecting the right status of LoadBalancer and DNSRecord
605+ // 4 - Bumping some label on the Gateway should trigger a reconciliation that will
606+ // bump the generation of conditions
607+ // 5 - Adding a label on DNSRecord and/or Service will trigger a reconciliation
608+ // that should be verified by a newly recorded event
609+ func testGatewayOpenshiftConditions (t * testing.T ) {
610+ domain := "gwcondtest." + dnsConfig .Spec .BaseDomain
611+
612+ gatewayClass , err := createGatewayClass (t , operatorcontroller .OpenShiftDefaultGatewayClassName , operatorcontroller .OpenShiftGatewayClassControllerName )
613+ require .NoError (t , err , "failed to create gatewayclass" )
614+
615+ t .Run ("creating a new gateway outside of 'openshift-ingress' namespace should not get openshift conditions" , func (t * testing.T ) {
616+ rnd := rand .IntN (1000 )
617+ name := fmt .Sprintf ("gw-test-%d" , rnd )
618+ testDomain := fmt .Sprintf ("some-%d.%s" , rnd , domain )
619+ gateway , err := createGateway (gatewayClass , name , "default" , testDomain )
620+ require .NoError (t , err , "failed to create gateway" , "name" , name )
621+ t .Cleanup (func () {
622+ require .NoError (t , client .IgnoreNotFound (kclient .Delete (context .TODO (), gateway )), "failed to clean test gateway" , "name" , name )
623+ })
624+
625+ gateway , err = assertGatewaySuccessful (t , "default" , name )
626+ require .NoError (t , err , "failed waiting gateway to be ready" )
627+ // Give some time to guarantee our controller will watch the change but ignore it
628+ time .Sleep (time .Second )
629+
630+ // Get gateway a 2nd time ti check the conditions
631+ gateway , err = assertGatewaySuccessful (t , "default" , name )
632+ require .NoError (t , err , "failed waiting gateway to have conditions" )
633+ require .Nil (t , condutils .FindStatusCondition (gateway .Status .Conditions , "DNSManaged" ), "condition should not be present" )
634+ require .Nil (t , condutils .FindStatusCondition (gateway .Status .Conditions , "DNSReady" ), "condition should not be present" )
635+ require .Nil (t , condutils .FindStatusCondition (gateway .Status .Conditions , "LoadBalancerManaged" ), "condition should not be present" )
636+ require .Nil (t , condutils .FindStatusCondition (gateway .Status .Conditions , "LoadBalancerReady" ), "condition should not be present" )
637+ })
638+
639+ t .Run ("creating a new gateway with the wrong base domain should add openshift conditions reflecting the failure" , func (t * testing.T ) {
640+ rnd := rand .IntN (1000 )
641+ name := fmt .Sprintf ("gw-test-%d" , rnd )
642+ testDomain := fmt .Sprintf ("some-%d.not.something.managed.tld" , rnd )
643+ gateway , err := createGateway (gatewayClass , name , operatorcontroller .DefaultOperandNamespace , testDomain )
644+ require .NoError (t , err , "failed to create gateway" , "name" , name )
645+ t .Cleanup (func () {
646+ require .NoError (t , client .IgnoreNotFound (kclient .Delete (context .TODO (), gateway )), "failed to clean test gateway" , "name" , name )
647+ })
648+
649+ gateway , err = assertGatewaySuccessful (t , operatorcontroller .DefaultOperandNamespace , name )
650+ require .NoError (t , err , "failed waiting gateway to be ready" )
651+
652+ assert .Eventuallyf (t , func () bool {
653+ gw := & gatewayapiv1.Gateway {}
654+ nsName := types.NamespacedName {Namespace : operatorcontroller .DefaultOperandNamespace , Name : name }
655+ if err := kclient .Get (context .Background (), nsName , gw ); err != nil {
656+ t .Logf ("Failed to get gateway %v: %v; retrying..." , nsName , err )
657+ return false
658+ }
659+
660+ if condutils .IsStatusConditionTrue (gw .Status .Conditions , "DNSManaged" ) &&
661+ condutils .IsStatusConditionPresentAndEqual (gw .Status .Conditions , "DNSReady" , metav1 .ConditionUnknown ) &&
662+ condutils .IsStatusConditionTrue (gw .Status .Conditions , "LoadBalancerManaged" ) &&
663+ condutils .IsStatusConditionTrue (gw .Status .Conditions , "LoadBalancerReady" ) {
664+
665+ return true
666+ }
667+ t .Logf ("conditions are not yet the expected: %v, retrying..." , gw .Status .Conditions )
668+ return false
669+ }, 30 * time .Second , 2 * time .Second , "error waiting for openshift conditions to be present on Gateway" )
670+ })
671+
672+ t .Run ("creating a new gateway with the right base domain" , func (t * testing.T ) {
673+ rnd := rand .IntN (1000 )
674+ name := fmt .Sprintf ("gw-test-%d" , rnd )
675+ testDomain := fmt .Sprintf ("some-%d.%s" , rnd , domain )
676+ gateway , err := createGateway (gatewayClass , name , operatorcontroller .DefaultOperandNamespace , testDomain )
677+ require .NoError (t , err , "failed to create gateway" , "name" , name )
678+ t .Cleanup (func () {
679+ require .NoError (t , client .IgnoreNotFound (kclient .Delete (context .TODO (), gateway )), "failed to clean test gateway" , "name" , name )
680+ })
681+
682+ gateway , err = assertGatewaySuccessful (t , operatorcontroller .DefaultOperandNamespace , name )
683+ require .NoError (t , err , "failed waiting gateway to be ready" )
684+
685+ err = assertExpectedDNSRecords (t , map [expectedDnsRecord ]bool {
686+ {dnsName : "*." + testDomain + "." , gatewayName : name }: true })
687+
688+ assert .NoError (t , err , "dnsrecord never got ready" )
689+
690+ t .Run ("should add openshift conditions" , func (t * testing.T ) {
691+ assert .Eventuallyf (t , func () bool {
692+ gw := & gatewayapiv1.Gateway {}
693+ nsName := types.NamespacedName {Namespace : operatorcontroller .DefaultOperandNamespace , Name : name }
694+ if err := kclient .Get (context .Background (), nsName , gw ); err != nil {
695+ t .Logf ("Failed to get gateway %v: %v; retrying..." , nsName , err )
696+ return false
697+ }
698+
699+ if condutils .IsStatusConditionTrue (gw .Status .Conditions , "DNSManaged" ) &&
700+ condutils .IsStatusConditionTrue (gw .Status .Conditions , "DNSReady" ) &&
701+ condutils .IsStatusConditionTrue (gw .Status .Conditions , "LoadBalancerManaged" ) &&
702+ condutils .IsStatusConditionTrue (gw .Status .Conditions , "LoadBalancerReady" ) {
703+
704+ return true
705+ }
706+ t .Logf ("conditions are not yet the expected: %v, retrying..." , gw .Status .Conditions )
707+ return false
708+ }, 30 * time .Second , 2 * time .Second , "error waiting for openshift conditions to be present on Gateway" )
709+
710+ // Check also for the existing event
711+ assert .Eventually (t , func () bool {
712+ events , err := getMatchingEventsFromGateway (t , kclient , gateway , "Normal" , "AddedConditions" )
713+ if err != nil {
714+ t .Logf ("error fetching the events from namespace: %s; retrying..." , err )
715+ return false
716+ }
717+ t .Logf ("events found: (%d): %+v" , len (events .Items ), events .Items )
718+ return len (events .Items ) > 0
719+ }, 30 * time .Second , 2 * time .Second , "error fetching matching resource to add conditions" )
720+ })
721+
722+ t .Run ("should bump openshift conditions when the gateway is changed" , func (t * testing.T ) {
723+ // Try to add a new infrastructure label, forcing the generation to bump
724+ originalGateway := & gatewayapiv1.Gateway {}
725+ assert .Eventually (t , func () bool {
726+ gw := & gatewayapiv1.Gateway {}
727+ nsName := types.NamespacedName {Namespace : operatorcontroller .DefaultOperandNamespace , Name : name }
728+ if err := kclient .Get (context .Background (), nsName , gw ); err != nil {
729+ t .Logf ("Failed to get gateway %v: %v; retrying..." , nsName , err )
730+ return false
731+ }
732+ originalGateway = gw .DeepCopy ()
733+ if gw .Spec .Infrastructure == nil {
734+ gw .Spec .Infrastructure = & gatewayapiv1.GatewayInfrastructure {}
735+ }
736+ if gw .Spec .Infrastructure .Labels == nil {
737+ gw .Spec .Infrastructure .Labels = make (map [gatewayapiv1.LabelKey ]gatewayapiv1.LabelValue )
738+ }
739+
740+ gw .Spec .Infrastructure .Labels ["something" ] = "somelabel"
741+
742+ if err := kclient .Patch (context .Background (), gw , client .MergeFrom (originalGateway )); err != nil {
743+ t .Logf ("failed to patch gateway %v: %v; retrying..." , nsName , err )
744+ return false
745+ }
746+ return true
747+ }, 30 * time .Second , 2 * time .Second , "timeout waiting to patch the gateway" )
748+
749+ gw := & gatewayapiv1.Gateway {}
750+ // Get the Gateway and check if conditions are there, and if their generation are different from the originalGateway value
751+ assert .Eventually (t , func () bool {
752+ nsName := types.NamespacedName {Namespace : operatorcontroller .DefaultOperandNamespace , Name : name }
753+ if err := kclient .Get (context .Background (), nsName , gw ); err != nil {
754+ t .Logf ("Failed to get gateway %v: %v; retrying..." , nsName , err )
755+ return false
756+ }
757+
758+ dnsManaged := condutils .FindStatusCondition (gw .Status .Conditions , "DNSManaged" )
759+ dnsReady := condutils .FindStatusCondition (gw .Status .Conditions , "DNSReady" )
760+ loadBalancerManaged := condutils .FindStatusCondition (gw .Status .Conditions , "LoadBalancerManaged" )
761+ loadBalancerReady := condutils .FindStatusCondition (gw .Status .Conditions , "LoadBalancerReady" )
762+
763+ // Check if all conditions are not null and have a different generation from the original one
764+ // before adding the label
765+ if (dnsManaged != nil && dnsManaged .ObservedGeneration != originalGateway .Generation ) &&
766+ (dnsReady != nil && dnsReady .ObservedGeneration != originalGateway .Generation ) &&
767+ (loadBalancerManaged != nil && loadBalancerManaged .ObservedGeneration != originalGateway .Generation ) &&
768+ (loadBalancerReady != nil && loadBalancerReady .ObservedGeneration != originalGateway .Generation ) {
769+ return true
770+ }
771+
772+ t .Logf ("conditions are not yet the expected: %v, retrying..." , gw .Status .Conditions )
773+ return false
774+ }, 30 * time .Second , 2 * time .Second , "error waiting for openshift conditions to be present on Gateway" )
775+ // We expect exactly 6 conditions. If we get more than it, Istio is adding
776+ // more conditions and we need to be aware that Gateway API status.conditons has a maxItems of 8
777+ assert .Len (t , gw .Status .Conditions , 6 )
778+ // We expect an event to happen, so try to get this event to guarantee it was properly added
779+ assert .Eventually (t , func () bool {
780+ events , err := getMatchingEventsFromGateway (t , kclient , gw , "Normal" , "AddedConditions" )
781+ if err != nil {
782+ t .Logf ("error fetching the events from namespace: %s; retrying..." , err )
783+ return false
784+ }
785+ t .Logf ("events found: (%d): %+v" , len (events .Items ), events .Items )
786+ return len (events .Items ) > 0
787+ }, 30 * time .Second , 5 * time .Second , "error fetching matching resource to add conditions" )
788+ })
789+
790+ // This test will delete the Gateway service. This should kick a new reconciliation
791+ // from Istio to recreate the services, and the condition "Programmed" should have
792+ // a different lastTransitionTime before the service being deleted.
793+ // But the condition "DNSManaged" and "LoadBalancerManaged"
794+ // should have the original timestamp, meaning they weren't changed
795+ t .Run ("should not replace openshift conditions when Istio reconciles the gateway" , func (t * testing.T ) {
796+ originalGateway := & gatewayapiv1.Gateway {}
797+ nsName := types.NamespacedName {Namespace : operatorcontroller .DefaultOperandNamespace , Name : name }
798+ require .Eventually (t , func () bool {
799+ if err := kclient .Get (context .Background (), nsName , originalGateway ); err != nil {
800+ t .Logf ("Failed to get gateway %v: %v; retrying..." , nsName , err )
801+ return false
802+ }
803+ return true
804+ }, 30 * time .Second , 2 * time .Second )
805+
806+ // These lastTransitionTime should not change
807+ // Also do a sanity check that they are true / ready
808+ originalDNSManagedCondition := condutils .FindStatusCondition (originalGateway .Status .Conditions , "DNSManaged" )
809+ require .NotNil (t , originalDNSManagedCondition )
810+ require .Equal (t , metav1 .ConditionTrue , originalDNSManagedCondition .Status )
811+ originalLoadBalancerManagedCondition := condutils .FindStatusCondition (originalGateway .Status .Conditions , "LoadBalancerManaged" )
812+ require .NotNil (t , originalLoadBalancerManagedCondition )
813+ require .Equal (t , metav1 .ConditionTrue , originalLoadBalancerManagedCondition .Status )
814+
815+ // These lastTransitionTime should change once the service is deleted and reprovisioned
816+ originalLoadBalancerReadyCondition := condutils .FindStatusCondition (originalGateway .Status .Conditions , "LoadBalancerReady" )
817+ require .NotNil (t , originalLoadBalancerReadyCondition )
818+ require .Equal (t , metav1 .ConditionTrue , originalLoadBalancerReadyCondition .Status )
819+ originalProgrammedCondition := condutils .FindStatusCondition (originalGateway .Status .Conditions , "Programmed" )
820+ require .NotNil (t , originalProgrammedCondition )
821+ require .Equal (t , metav1 .ConditionTrue , originalProgrammedCondition .Status )
822+
823+ t .Run ("deleting a service managed by Istio" , func (t * testing.T ) {
824+ ctx := context .Background ()
825+ svcList := & corev1.ServiceList {}
826+ assert .Eventually (t , func () bool {
827+ if err := kclient .List (ctx , svcList ,
828+ client .InNamespace (operatorcontroller .DefaultOperandNamespace ),
829+ client.MatchingLabels {operatorcontroller .GatewayNameLabelKey : originalGateway .GetName ()},
830+ ); err != nil {
831+ t .Logf ("Failed to list services attached to Gateway %s; retrying...: %s" , originalGateway .GetName (), err )
832+ return false
833+ }
834+ return true
835+ }, 30 * time .Second , 2 * time .Second )
836+
837+ require .Len (t , svcList .Items , 1 )
838+ svc := svcList .Items [0 ]
839+
840+ // Delete the service
841+ assert .Eventually (t , func () bool {
842+ if err := kclient .Delete (ctx , & svc ); client .IgnoreNotFound (err ) != nil {
843+ t .Logf ("Failed to delete service %s attached to Gateway %s; retrying...: %s" , svc .GetName (), originalGateway .GetName (), err )
844+ return false
845+ }
846+ return true
847+ }, 30 * time .Second , time .Second )
848+ })
849+
850+ currentGateway := & gatewayapiv1.Gateway {}
851+ var currentDNSManagedCondition , currentLoadBalancerManagedCondition * metav1.Condition
852+ t .Run ("lastTransitionTime should change for some conditions and not for others" , func (t * testing.T ) {
853+ assert .Eventually (t , func () bool {
854+
855+ nsName := types.NamespacedName {Namespace : operatorcontroller .DefaultOperandNamespace , Name : name }
856+
857+ if err := kclient .Get (context .Background (), nsName , currentGateway ); err != nil {
858+ t .Logf ("Failed to get current gateway %v: %v; retrying..." , nsName , err )
859+ return false
860+ }
861+
862+ currentDNSManagedCondition = condutils .FindStatusCondition (currentGateway .Status .Conditions , "DNSManaged" )
863+ currentLoadBalancerManagedCondition = condutils .FindStatusCondition (currentGateway .Status .Conditions , "LoadBalancerManaged" )
864+ currentLoadBalancerReadyCondition := condutils .FindStatusCondition (currentGateway .Status .Conditions , "LoadBalancerReady" )
865+ currentProgrammedCondition := condutils .FindStatusCondition (currentGateway .Status .Conditions , "Programmed" )
866+
867+ // Expect conditions to be ready
868+ if (currentDNSManagedCondition == nil || currentDNSManagedCondition .Status != metav1 .ConditionTrue ) ||
869+ (currentLoadBalancerManagedCondition == nil || currentLoadBalancerManagedCondition .Status != metav1 .ConditionTrue ) ||
870+ (currentLoadBalancerReadyCondition == nil || currentLoadBalancerReadyCondition .Status != metav1 .ConditionTrue ) ||
871+ (currentProgrammedCondition == nil || currentProgrammedCondition .Status != metav1 .ConditionTrue ) {
872+
873+ t .Logf ("conditions on gateway %s are not ready yet: %+v" , currentGateway .GetName (), currentGateway .Status .Conditions )
874+ return false
875+ }
876+
877+ // Expect LoadBalancerReady and Programmed condition to have a new transition time
878+ if ! currentLoadBalancerReadyCondition .LastTransitionTime .After (originalLoadBalancerReadyCondition .LastTransitionTime .Time ) ||
879+ ! currentProgrammedCondition .LastTransitionTime .After (originalProgrammedCondition .LastTransitionTime .Time ) {
880+ t .Logf ("conditions on gateway %s didn't changed yet: %+v" , currentGateway .GetName (), currentGateway .Status .Conditions )
881+ return false
882+ }
883+
884+ return true
885+ }, 3 * time .Minute , 3 * time .Second )
886+ })
887+ // After conditions are bumped, the original ones should not change
888+ assert .Equal (t , originalDNSManagedCondition .LastTransitionTime , currentDNSManagedCondition .LastTransitionTime , "the DNSManaged condition LastTransitionTime should not change" )
889+ assert .Equal (t , originalLoadBalancerManagedCondition .LastTransitionTime , currentLoadBalancerManagedCondition .LastTransitionTime , "the LoadBalancerManaged condition LastTransitionTime should not change" )
890+
891+ })
892+
893+ })
894+ }
895+
896+ func getMatchingEventsFromGateway (t * testing.T , kclient client.Reader , gw * gatewayapiv1.Gateway , eventType , reason string ) (* corev1.EventList , error ) {
897+ t .Helper ()
898+ operandEvents := & corev1.EventList {}
899+ fieldSelector := fields.Set {
900+ "involvedObject.kind" : "Gateway" ,
901+ "involvedObject.namespace" : gw .Namespace ,
902+ "involvedObject.name" : gw .Name ,
903+ "type" : eventType ,
904+ "reason" : reason ,
905+ }
906+
907+ t .Logf ("using field selector: %+v" , fieldSelector )
590908
909+ err := kclient .List (context .Background (), operandEvents ,
910+ client .InNamespace (operatorcontroller .DefaultOperandNamespace ),
911+ client.MatchingFieldsSelector {Selector : fields .SelectorFromSet (fieldSelector )})
912+ return operandEvents , err
591913}
592914
593915func testGatewayAPIDNSListenerUpdate (t * testing.T ) {
0 commit comments