Skip to content

Commit c89596b

Browse files
Merge pull request #31000 from gcs278/fix-gwapi-upgrade-ipv6-skip
OCPBUGS-83267: Use upgrades.Skippable for Gateway API upgrade test skip logic
2 parents d7ad0db + 8ef51c3 commit c89596b

2 files changed

Lines changed: 117 additions & 51 deletions

File tree

test/extended/router/gatewayapi_upgrade.go

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type GatewayAPIUpgradeTest struct {
3030
startedWithNoOLM bool // tracks if GatewayAPIWithoutOLM was enabled at start
3131
loadBalancerSupported bool
3232
managedDNS bool
33+
precheckErr error // error from Skip() to surface in Setup()
3334
}
3435

3536
func (t *GatewayAPIUpgradeTest) Name() string {
@@ -40,25 +41,48 @@ func (t *GatewayAPIUpgradeTest) DisplayName() string {
4041
return "[sig-network-edge][Feature:Router][apigroup:gateway.networking.k8s.io] Verify Gateway API functionality during upgrade"
4142
}
4243

44+
// Skip checks if this upgrade test should be skipped. This is called by the
45+
// disruption framework before Setup.
46+
func (t *GatewayAPIUpgradeTest) Skip(_ upgrades.UpgradeContext) bool {
47+
oc := exutil.NewCLIForMonitorTest("gateway-api-upgrade-skip").AsAdmin()
48+
49+
t.precheckErr = nil
50+
noOLM, err := isNoOLMFeatureGateEnabled(oc)
51+
if err != nil {
52+
t.precheckErr = fmt.Errorf("failed to check GatewayAPIWithoutOLM feature gate: %w", err)
53+
return false
54+
}
55+
56+
skip, reason, err := shouldSkipGatewayAPITests(oc, noOLM)
57+
if err != nil {
58+
t.precheckErr = fmt.Errorf("failed to check Gateway API skip conditions: %w", err)
59+
return false
60+
}
61+
if skip {
62+
g.By(fmt.Sprintf("skipping test: %s", reason))
63+
return true
64+
}
65+
66+
return false
67+
}
68+
4369
// Setup creates Gateway and HTTPRoute resources and tests connectivity
4470
func (t *GatewayAPIUpgradeTest) Setup(ctx context.Context, f *e2e.Framework) {
4571
g.By("Setting up Gateway API upgrade test")
72+
o.Expect(t.precheckErr).NotTo(o.HaveOccurred(), "Skip() precheck failed: could not determine if Gateway API upgrade test should run")
4673

4774
t.oc = exutil.NewCLIWithFramework(f).AsAdmin()
4875
t.namespace = f.Namespace.Name
4976
t.gatewayName = "upgrade-test-gateway"
5077
t.routeName = "test-httproute"
5178

52-
// Check platform support and get capabilities (LoadBalancer, DNS)
53-
t.loadBalancerSupported, t.managedDNS = checkPlatformSupportAndGetCapabilities(t.oc)
79+
// Get platform capabilities (skip checks already handled by Skip())
80+
t.loadBalancerSupported, t.managedDNS = getPlatformCapabilities(t.oc)
5481

5582
g.By("Checking if GatewayAPIWithoutOLM feature gate is enabled before upgrade")
56-
t.startedWithNoOLM = isNoOLMFeatureGateEnabled(t.oc)
57-
58-
// Skip on clusters missing OLM/Marketplace capabilities if starting with OLM mode
59-
if !t.startedWithNoOLM {
60-
exutil.SkipIfMissingCapabilities(t.oc, olmCapabilities...)
61-
}
83+
var noOLMErr error
84+
t.startedWithNoOLM, noOLMErr = isNoOLMFeatureGateEnabled(t.oc)
85+
o.Expect(noOLMErr).NotTo(o.HaveOccurred())
6286

6387
if t.startedWithNoOLM {
6488
e2e.Logf("Starting with GatewayAPIWithoutOLM enabled (NO-OLM mode)")
@@ -135,7 +159,8 @@ func (t *GatewayAPIUpgradeTest) Test(ctx context.Context, f *e2e.Framework, done
135159
o.Expect(err).NotTo(o.HaveOccurred(), "Gateway should remain programmed")
136160

137161
g.By("Checking if GatewayAPIWithoutOLM feature gate is enabled after upgrade")
138-
endsWithNoOLM := isNoOLMFeatureGateEnabled(t.oc)
162+
endsWithNoOLM, err := isNoOLMFeatureGateEnabled(t.oc)
163+
o.Expect(err).NotTo(o.HaveOccurred())
139164

140165
// Determine if migration happened: started with OLM, ended with NO-OLM
141166
migrationOccurred := !t.startedWithNoOLM && endsWithNoOLM

test/extended/router/gatewayapicontroller.go

Lines changed: 83 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -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

628669
func waitForIstioHealthy(oc *exutil.CLI, timeout time.Duration) {

0 commit comments

Comments
 (0)