@@ -115,6 +115,7 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
115115 infPoolCRD = "https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/main/config/crd/bases/inference.networking.k8s.io_inferencepools.yaml"
116116 managedDNS bool
117117 loadBalancerSupported bool
118+ noOLM bool
118119 )
119120
120121 const (
@@ -123,16 +124,16 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
123124 openshiftOperatorsNamespace = "openshift-operators"
124125 )
125126 g .BeforeEach (func () {
126- // Check platform support and get capabilities (LoadBalancer, DNS )
127- loadBalancerSupported , managedDNS = checkPlatformSupportAndGetCapabilities ( oc )
127+ noOLM , err = isNoOLMFeatureGateEnabled ( oc )
128+ o . Expect ( err ). NotTo ( o . HaveOccurred () )
128129
129- if ! isNoOLMFeatureGateEnabled (oc ) {
130- // GatewayAPIController without GatewayAPIWithoutOLM featuregate
131- // relies on OSSM OLM operator.
132- // Skipping on clusters which don't have capabilities required
133- // to install an OLM operator.
134- exutil .SkipIfMissingCapabilities (oc , olmCapabilities ... )
130+ // Check platform support, capabilities, and skip conditions
131+ skip , reason , err := shouldSkipGatewayAPITests (oc , noOLM )
132+ o .Expect (err ).NotTo (o .HaveOccurred ())
133+ if skip {
134+ g .Skip (reason )
135135 }
136+ loadBalancerSupported , managedDNS = getPlatformCapabilities (oc )
136137 // create the default gatewayClass
137138 gatewayClass := buildGatewayClass (gatewayClassName , gatewayClassControllerName )
138139 _ , err = oc .AdminGatewayApiClient ().GatewayV1 ().GatewayClasses ().Create (context .TODO (), gatewayClass , metav1.CreateOptions {})
@@ -157,7 +158,7 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
157158 if err := oc .AdminGatewayApiClient ().GatewayV1 ().GatewayClasses ().Delete (context .Background (), gatewayClassName , metav1.DeleteOptions {}); err != nil && ! apierrors .IsNotFound (err ) {
158159 e2e .Failf ("Failed to delete GatewayClass %q" , gatewayClassName )
159160 }
160- if isNoOLMFeatureGateEnabled ( oc ) {
161+ if noOLM {
161162 g .By ("Waiting for the istiod pod to be deleted" )
162163 waitForIstiodPodDeletion (oc )
163164 } else {
@@ -283,7 +284,7 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
283284 g .It ("Ensure OSSM and OLM related resources are created after creating GatewayClass" , func () {
284285 defer markTestDone (oc , ossmAndOLMResourcesCreated )
285286 // these will fail since no OLM Resources will be available
286- if isNoOLMFeatureGateEnabled ( oc ) {
287+ if noOLM {
287288 g .Skip ("Skip this test since it requires OLM resources" )
288289 }
289290
@@ -313,7 +314,7 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
313314 errCheck := checkGatewayClassCondition (oc , customGatewayClassName , string (gatewayapiv1 .GatewayClassConditionStatusAccepted ), metav1 .ConditionTrue )
314315 o .Expect (errCheck ).NotTo (o .HaveOccurred (), "GatewayClass %q was not installed and accepted" , gwc .Name )
315316
316- if isNoOLMFeatureGateEnabled ( oc ) {
317+ if noOLM {
317318 g .By ("Check the GatewayClass conditions" )
318319 errCheck = checkGatewayClassCondition (oc , customGatewayClassName , gatewayClassControllerInstalledConditionType , metav1 .ConditionTrue )
319320 o .Expect (errCheck ).NotTo (o .HaveOccurred (), "GatewayClass %q does not have the ControllerInstalled condition" , customGatewayClassName )
@@ -340,7 +341,7 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
340341 defaultCheck := checkGatewayClassCondition (oc , gatewayClassName , string (gatewayapiv1 .GatewayClassConditionStatusAccepted ), metav1 .ConditionTrue )
341342 o .Expect (defaultCheck ).NotTo (o .HaveOccurred ())
342343
343- if ! isNoOLMFeatureGateEnabled ( oc ) {
344+ if ! noOLM {
344345 g .By ("Confirm that ISTIO CR is created and in healthy state" )
345346 waitForIstioHealthy (oc , 20 * time .Minute )
346347 }
@@ -482,7 +483,7 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
482483 g .By (fmt .Sprintf ("Wait until the istiod deployment in %s namespace is automatically created successfully" , ingressNamespace ))
483484 pollWaitDeploymentCreated (oc , ingressNamespace , istiodDeployment , deployment .CreationTimestamp )
484485
485- if ! isNoOLMFeatureGateEnabled ( oc ) {
486+ if ! noOLM {
486487 // delete the istio and check if it is restored
487488 g .By (fmt .Sprintf ("Try to delete the istio %s" , istioName ))
488489 istioOriginalCreatedTimestamp , err := oc .AsAdmin ().WithoutNamespace ().Run ("get" ).Args ("-n" , ingressNamespace , "istio/" + istioName , `-o=jsonpath={.metadata.creationTimestamp}` ).Output ()
@@ -548,44 +549,80 @@ var _ = g.Describe("[sig-network-edge][OCPFeatureGate:GatewayAPIController][Feat
548549 })
549550})
550551
551- // checkPlatformSupportAndGetCapabilities verifies the platform is supported and returns
552- // platform capabilities for LoadBalancer services and managed DNS.
553- func checkPlatformSupportAndGetCapabilities (oc * exutil.CLI ) (loadBalancerSupported bool , managedDNS bool ) {
554- // Skip on OKD since OSSM is not available as a community operator
552+ // shouldSkipGatewayAPITests checks if Gateway API tests should be skipped on this cluster.
553+ // It returns (skip bool, reason string, err error). An error indicates a failure to
554+ // determine skip status and should be treated as a test failure, not a skip.
555+ // This function avoids calling g.Skip() or o.Expect() so it is safe to call from
556+ // upgrade test Skip() methods that run outside of Ginkgo leaf nodes.
557+ func shouldSkipGatewayAPITests (oc * exutil.CLI , noOLM bool ) (bool , string , error ) {
555558 // TODO: Determine if we can enable and start testing OKD with Sail Library
556559 isokd , err := isOKD (oc )
557- o .Expect (err ).NotTo (o .HaveOccurred (), "Failed to get clusterversion to determine if release is OKD" )
560+ if err != nil {
561+ return false , "" , fmt .Errorf ("failed to determine if release is OKD: %v" , err )
562+ }
558563 if isokd {
559- g . Skip ( "Skipping on OKD cluster as OSSM is not available as a community operator" )
564+ return true , "Skipping on OKD cluster as OSSM is not available as a community operator" , nil
560565 }
561566
562567 infra , err := oc .AdminConfigClient ().ConfigV1 ().Infrastructures ().Get (context .Background (), "cluster" , metav1.GetOptions {})
563- o .Expect (err ).NotTo (o .HaveOccurred ())
564- o .Expect (infra ).NotTo (o .BeNil ())
568+ if err != nil {
569+ return false , "" , fmt .Errorf ("failed to get infrastructure: %v" , err )
570+ }
565571
566- o .Expect (infra .Status .PlatformStatus ).NotTo (o .BeNil ())
572+ if infra .Status .PlatformStatus == nil {
573+ return false , "" , fmt .Errorf ("infrastructure PlatformStatus is nil" )
574+ }
567575 platformType := infra .Status .PlatformStatus .Type
568- o .Expect (platformType ).NotTo (o .BeEmpty ())
569576 switch platformType {
577+ case configv1 .AWSPlatformType ,
578+ configv1 .AzurePlatformType ,
579+ configv1 .GCPPlatformType ,
580+ configv1 .IBMCloudPlatformType ,
581+ configv1 .VSpherePlatformType ,
582+ configv1 .BareMetalPlatformType ,
583+ configv1 .EquinixMetalPlatformType :
584+ // supported
585+ default :
586+ return true , fmt .Sprintf ("Skipping on unsupported platform type %q" , platformType ), nil
587+ }
588+
589+ ipv6 , err := isIPv6OrDualStack (oc )
590+ if err != nil {
591+ return false , "" , fmt .Errorf ("failed to check IPv6/dual-stack: %v" , err )
592+ }
593+ if ipv6 {
594+ return true , "Skipping Gateway API tests on IPv6/dual-stack cluster" , nil
595+ }
596+
597+ if ! noOLM {
598+ enabled , err := exutil .AllCapabilitiesEnabled (oc , olmCapabilities ... )
599+ if err != nil {
600+ return false , "" , fmt .Errorf ("failed to check OLM capabilities: %v" , err )
601+ }
602+ if ! enabled {
603+ return true , "Skipping: OLM/Marketplace capabilities are not enabled and GatewayAPIWithoutOLM is not enabled" , nil
604+ }
605+ }
606+
607+ return false , "" , nil
608+ }
609+
610+ // getPlatformCapabilities returns platform capabilities for LoadBalancer services and managed DNS.
611+ func getPlatformCapabilities (oc * exutil.CLI ) (loadBalancerSupported bool , managedDNS bool ) {
612+ infra , err := oc .AdminConfigClient ().ConfigV1 ().Infrastructures ().Get (context .Background (), "cluster" , metav1.GetOptions {})
613+ o .Expect (err ).NotTo (o .HaveOccurred ())
614+ o .Expect (infra .Status .PlatformStatus ).NotTo (o .BeNil ())
615+
616+ switch infra .Status .PlatformStatus .Type {
570617 case configv1 .AWSPlatformType , configv1 .AzurePlatformType , configv1 .GCPPlatformType , configv1 .IBMCloudPlatformType :
571- // Cloud platforms with native LoadBalancer support
572618 loadBalancerSupported = true
573619 case configv1 .VSpherePlatformType , configv1 .BareMetalPlatformType , configv1 .EquinixMetalPlatformType :
574- // Platforms without native LoadBalancer support (may have MetalLB or similar)
575620 loadBalancerSupported = false
576- default :
577- g .Skip (fmt .Sprintf ("Skipping on unsupported platform type %q" , platformType ))
578621 }
579622
580- // Check if DNS is managed (has public or private zones configured)
581623 managedDNS = isDNSManaged (oc )
582624
583- // Skip Gateway API tests on IPv6 or dual-stack clusters (any platform)
584- if isIPv6OrDualStack (oc ) {
585- g .Skip ("Skipping Gateway API tests on IPv6/dual-stack cluster" )
586- }
587-
588- e2e .Logf ("Platform: %s, LoadBalancer supported: %t, DNS managed: %t" , platformType , loadBalancerSupported , managedDNS )
625+ e2e .Logf ("Platform: %s, LoadBalancer supported: %t, DNS managed: %t" , infra .Status .PlatformStatus .Type , loadBalancerSupported , managedDNS )
589626 return loadBalancerSupported , managedDNS
590627}
591628
@@ -599,30 +636,34 @@ func isDNSManaged(oc *exutil.CLI) bool {
599636
600637// isIPv6OrDualStack checks if the cluster is using IPv6 or dual-stack networking.
601638// Returns true if any ServiceNetwork CIDR is IPv6 (indicates IPv6-only or dual-stack).
602- func isIPv6OrDualStack (oc * exutil.CLI ) bool {
639+ func isIPv6OrDualStack (oc * exutil.CLI ) ( bool , error ) {
603640 networkConfig , err := oc .AdminOperatorClient ().OperatorV1 ().Networks ().Get (context .Background (), "cluster" , metav1.GetOptions {})
604- o .Expect (err ).NotTo (o .HaveOccurred (), "Failed to get network config" )
641+ if err != nil {
642+ return false , fmt .Errorf ("failed to get network config: %v" , err )
643+ }
605644
606645 for _ , cidr := range networkConfig .Spec .ServiceNetwork {
607646 if utilnet .IsIPv6CIDRString (cidr ) {
608- return true
647+ return true , nil
609648 }
610649 }
611- return false
650+ return false , nil
612651}
613652
614- func isNoOLMFeatureGateEnabled (oc * exutil.CLI ) bool {
653+ func isNoOLMFeatureGateEnabled (oc * exutil.CLI ) ( bool , error ) {
615654 fgs , err := oc .AdminConfigClient ().ConfigV1 ().FeatureGates ().Get (context .TODO (), "cluster" , metav1.GetOptions {})
616- o .Expect (err ).NotTo (o .HaveOccurred (), "Error getting cluster FeatureGates." )
655+ if err != nil {
656+ return false , fmt .Errorf ("failed to get cluster FeatureGates: %v" , err )
657+ }
617658 for _ , fg := range fgs .Status .FeatureGates {
618659 for _ , enabledFG := range fg .Enabled {
619660 if enabledFG .Name == "GatewayAPIWithoutOLM" {
620661 e2e .Logf ("GatewayAPIWithoutOLM featuregate is enabled" )
621- return true
662+ return true , nil
622663 }
623664 }
624665 }
625- return false
666+ return false , nil
626667}
627668
628669func waitForIstioHealthy (oc * exutil.CLI , timeout time.Duration ) {
0 commit comments