Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ require (
github.com/opencontainers/go-digest v1.0.0
github.com/openshift-eng/openshift-tests-extension v0.0.0-20260707142426-572a3e9deb7a
github.com/openshift-kni/commatrix v0.0.5-0.20251111204857-e5a931eff73f
github.com/openshift/api v0.0.0-20260715165912-72066cc9718b
github.com/openshift/api v0.0.0-20260810132456-8f52beb625b5
github.com/openshift/apiserver-library-go v0.0.0-20260715200723-42e5e402ca43
github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee
github.com/openshift/client-go v0.0.0-20260715172546-dac61734e0ec
github.com/openshift/client-go v0.0.0-20260810202730-ddca5e0b7146
Comment on lines +68 to +71

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify vendored openshift/api and client-go expose the management-mode API surface.
set -uo pipefail

grep -nE 'github.com/openshift/(api|client-go) ' go.mod
grep -nE '^# github.com/openshift/(api|client-go) ' vendor/modules.txt || echo "vendor/modules.txt entries missing"
grep -nE 'github.com/openshift/(api|client-go) ' go.sum | head -20

fd -t d 'v1alpha1' vendor/github.com/openshift/api/operator | head
rg -n 'GatewayAPIManagementMode(Managed|Unmanaged)?\b' vendor/github.com/openshift/api/operator -g '*.go' | head -20
rg -n 'func .*OperatorV1alpha1\(\)|Ingresses\(\)' vendor/github.com/openshift/client-go/operator -g '*.go' | head -20

Repository: openshift/origin

Length of output: 3019


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

go_mod = Path("go.mod").read_text()
modules = Path("vendor/modules.txt").read_text()
go_sum = Path("go.sum").read_text()

names = ["github.com/openshift/api", "github.com/openshift/client-go"]
for name in names:
    mod_match = re.search(rf"(?m)^\s*{re.escape(name)}\s+(\S+)", go_mod)
    vendor_match = re.search(rf"(?m)^# {re.escape(name)}\s+(\S+)", modules)
    sum_versions = sorted(set(re.findall(rf"^{re.escape(name)}\s+(\S+)(?:/go\.mod)?\s+", go_sum, re.M)))
    print(name)
    print("  go.mod:", mod_match.group(1) if mod_match else "MISSING")
    print("  vendor/modules.txt:", vendor_match.group(1) if vendor_match else "MISSING")
    print("  go.sum versions:", ", ".join(sum_versions) if sum_versions else "MISSING")
    if not mod_match or not vendor_match or mod_match.group(1) != vendor_match.group(1):
        print("  VERSION_MISMATCH")

print("\nExact client call-chain references:")
for path in Path(".").rglob("*.go"):
    if any(part in {"vendor", ".git"} for part in path.parts):
        continue
    text = path.read_text(errors="replace")
    for i, line in enumerate(text.splitlines(), 1):
        if "AdminOperatorClient().OperatorV1alpha1().Ingresses()" in line:
            print(f"{path}:{i}:{line.strip()}")
PY

printf '\nGo module vendor diagnostics:\n'
go version
go mod vendor -h 2>&1 | sed -n '1,80p'

printf '\nSelected vendored module metadata:\n'
go list -mod=vendor -m github.com/openshift/api github.com/openshift/client-go

Repository: openshift/origin

Length of output: 1331


🏁 Script executed:

#!/bin/bash
set -u

printf '%s\n' 'Read-only go.mod/go.sum tidy diff:'
go mod tidy -diff 2>&1
tidy_status=$?
printf 'go mod tidy -diff exit status: %s\n' "$tidy_status"

printf '\nVendor-mode module query:\n'
go list -mod=vendor -m all 2>&1 | rg 'github.com/openshift/(api|client-go)|go:|cannot|error' | head -40
printf 'go list exit status: %s\n' "${PIPESTATUS[0]}"

Repository: openshift/origin

Length of output: 9410


Run go mod tidy && go mod vendor. go.sum contains four obsolete checksums for the previous OpenShift API and client-go versions. The vendored packages provide the requested API symbols.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@go.mod` around lines 68 - 71, Run go mod tidy followed by go mod vendor to
refresh dependency metadata and vendored sources for the OpenShift modules in
go.mod, removing obsolete go.sum checksums for prior API and client-go versions
while retaining the versions that provide the required symbols.

Source: Coding guidelines

github.com/openshift/library-go v0.0.0-20260716164659-7926d144f96a
github.com/ovn-org/ovn-kubernetes/go-controller v0.0.0-20250118001652-a8b9c3c31417
github.com/pborman/uuid v1.2.0
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -893,12 +893,16 @@ github.com/openshift-kni/commatrix v0.0.5-0.20251111204857-e5a931eff73f h1:E72Zo
github.com/openshift-kni/commatrix v0.0.5-0.20251111204857-e5a931eff73f/go.mod h1:cDVdp0eda7EHE6tLuSeo4IqPWdAX/KJK+ogBirIGtsI=
github.com/openshift/api v0.0.0-20260715165912-72066cc9718b h1:gN3SihCYEwoIksD+f24wHhwiEgvaV0RxNjgmkDvBBeg=
github.com/openshift/api v0.0.0-20260715165912-72066cc9718b/go.mod h1:k6qH5QOVa5GDln2VVm8Jz4NV3Z7R2SATHFLwGS6Wh3M=
github.com/openshift/api v0.0.0-20260810132456-8f52beb625b5 h1:/UAIG4kF4dXdamTuN9rL5kSjbQZ9wDES9O2q/wS8Bsk=
github.com/openshift/api v0.0.0-20260810132456-8f52beb625b5/go.mod h1:k6qH5QOVa5GDln2VVm8Jz4NV3Z7R2SATHFLwGS6Wh3M=
github.com/openshift/apiserver-library-go v0.0.0-20260715200723-42e5e402ca43 h1:V9hWaBi9cnohNk1F0Ph6wpI0otMWqMHleJ3oj5603Bc=
github.com/openshift/apiserver-library-go v0.0.0-20260715200723-42e5e402ca43/go.mod h1:ZuzfEq1ccZpHNx05xEUKlm2TcMHt2iXVutb79kAuTfM=
github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee h1:+Sp5GGnjHDhT/a/nQ1xdp43UscBMr7G5wxsYotyhzJ4=
github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE=
github.com/openshift/client-go v0.0.0-20260715172546-dac61734e0ec h1:UDjX+mot5IVLpcChyBqLXG1oSB29s4UkqFmgNb0Xsqc=
github.com/openshift/client-go v0.0.0-20260715172546-dac61734e0ec/go.mod h1:iMHec0APKVjOH8GfL/RxddX8DuiuSvPlRe+s7KDqlyA=
github.com/openshift/client-go v0.0.0-20260810202730-ddca5e0b7146 h1:fX/gaOPiS2vrYGSAFSeqqoWjj32H+NbMmB5RDjwhCnU=
github.com/openshift/client-go v0.0.0-20260810202730-ddca5e0b7146/go.mod h1:bhbP5y308NNbImrgArOc9611HIDAMcNWdCCO4tq5ob4=
github.com/openshift/kubernetes v1.30.1-0.20260719124805-0f7d1a1b66af h1:jdn7sNKevBHElUk/fgJNiM0QGJmKysgp+CdJWfRMTu4=
github.com/openshift/kubernetes v1.30.1-0.20260719124805-0f7d1a1b66af/go.mod h1:bJ5h4TVP09ipVze5drR/0a1DQRP9Bbk+PKEU5/6Hvy4=
github.com/openshift/kubernetes/staging/src/k8s.io/api v0.0.0-20260719124805-0f7d1a1b66af h1:twJGGZwzx1L4hiSP1Pdo6JdAWGhXaal0CL0XVaZABCg=
Expand Down
1 change: 1 addition & 0 deletions test/e2e/upgrade/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func AllTests() []upgrades.Test {
&prometheus.MetricsAvailableAfterUpgradeTest{},
&dns.UpgradeTest{},
&router.GatewayAPIUpgradeTest{},
&router.GatewayAPIManagementModeUpgradeTest{},
}
}

Expand Down
850 changes: 850 additions & 0 deletions test/extended/router/gatewayapi_management_mode.go

Large diffs are not rendered by default.

320 changes: 320 additions & 0 deletions test/extended/router/gatewayapi_management_mode_upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
package router

import (
"context"
"fmt"
"strings"
"time"

g "github.com/onsi/ginkgo/v2"
o "github.com/onsi/gomega"

operatorv1alpha1 "github.com/openshift/api/operator/v1alpha1"

exutil "github.com/openshift/origin/test/extended/util"

apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
e2e "k8s.io/kubernetes/test/e2e/framework"
"k8s.io/kubernetes/test/e2e/upgrades"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
)

// GatewayAPIManagementModeUpgradeTest verifies that Gateway API management mode
// transitions work correctly during upgrades and resources remain functional
type GatewayAPIManagementModeUpgradeTest struct {
oc *exutil.CLI
namespace string
gatewayClassName string
gatewayName string
routeName string
hostname string
startMode operatorv1alpha1.GatewayAPIManagementMode
loadBalancerSupported bool
managedDNS bool
}

func (t *GatewayAPIManagementModeUpgradeTest) Name() string {
return "gateway-api-management-mode-upgrade"
}

func (t *GatewayAPIManagementModeUpgradeTest) DisplayName() string {
return "[sig-network-edge][OCPFeatureGate:GatewayAPIManagementMode][Feature:Router][apigroup:operator.openshift.io] Verify Gateway API management mode transitions during upgrade"
}

// Skip checks if this upgrade test should be skipped
func (t *GatewayAPIManagementModeUpgradeTest) Skip(_ upgrades.UpgradeContext) bool {
oc := exutil.NewCLIForMonitorTest("gateway-api-mgmt-mode-upgrade-skip").AsAdmin()

// Check if feature gate is enabled
if !exutil.IsTechPreviewNoUpgrade(context.Background(), oc.AdminConfigClient()) {
e2e.Logf("Skipping: GatewayAPIManagementMode feature is not in TechPreviewNoUpgrade")
return true
}

skip, reason, err := shouldSkipGatewayAPITests(oc, true) // NoOLM is default/GA
if err != nil {
e2e.Logf("Failed to check Gateway API skip conditions: %v", err)
return true
}
if skip {
e2e.Logf("Skipping test: %s", reason)
return true
}

return false
}
Comment on lines +46 to +66

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect the TechPreviewNoUpgrade helper and look for other upgrade tests that gate on it.
rg -nP -C 8 'func IsTechPreviewNoUpgrade\b' test/extended/util

# Find upgrade tests (Skip(upgrades.UpgradeContext)) that also check TechPreviewNoUpgrade.
rg -nP -C 5 'IsTechPreviewNoUpgrade' --type=go -g '!vendor/**'

Repository: openshift/origin

Length of output: 2763


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- TechPreviewNoUpgrade helper ---'
sed -n '190,225p' test/extended/util/compat_otp/clusters.go
printf '%s\n' '--- config-client TechPreviewNoUpgrade helper ---'
sed -n '2188,2225p' test/extended/util/framework.go
printf '%s\n' '--- upgrade test interface and runner ---'
rg -n -C 8 'type Test interface|func .*Skip\(|Skip\(.*UpgradeContext|Test\(\s*ctx|UpgradeContext' test/e2e test/extended --type=go -g '!vendor/**' | head -n 240
printf '%s\n' '--- relevant test lines ---'
sed -n '135,175p' test/extended/router/gatewayapi_management_mode_upgrade.go
printf '%s\n' '--- all TechPreviewNoUpgrade references outside helper definitions ---'
rg -n -C 6 'IsTechPreviewNoUpgrade' --type=go -g '!vendor/**'

Repository: openshift/origin

Length of output: 21941


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- upgrade test contract and skip invocation ---'
rg -n -C 12 'type UpgradeTest|Skip\(.*UpgradeContext|\.Skip\(|checkUpgradeability|IsNoUpgradeFeatureSet|TechPreviewNoUpgrade|CustomNoUpgrade' test/e2e/upgrade test/extended/router --type=go
printf '%s\n' '--- upgradeability implementation ---'
sed -n '230,330p' test/e2e/upgrade/upgrade.go
printf '%s\n' '--- complete imports and Skip implementation ---'
sed -n '1,90p' test/extended/router/gatewayapi_management_mode_upgrade.go
printf '%s\n' '--- all source references, including non-Go files ---'
rg -n -C 4 'IsTechPreviewNoUpgrade|TechPreviewNoUpgrade|IsNoUpgradeFeatureSet' . -g '!vendor/**' -g '!node_modules/**'

Repository: openshift/origin

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- management-mode upgrade test ---'
sed -n '1,210p' test/extended/router/gatewayapi_management_mode_upgrade.go
printf '%s\n' '--- upgrade test registration ---'
rg -n -C 8 'GatewayAPIManagementModeUpgradeTest|gateway-api-mgmt-mode-upgrade' test/e2e/upgrade/upgrade.go test/extended/router/gatewayapi_management_mode_upgrade.go
printf '%s\n' '--- upgrade framework interface and skip path ---'
rg -n -C 8 'type Test interface|func .*Run|Skip\(.*UpgradeContext|test\.Skip|Setup\(ctx|Teardown\(ctx' vendor test 2>/dev/null | rg 'upgrades|UpgradeContext|Skip\(.*UpgradeContext|test/e2e/upgrade' | head -n 160
printf '%s\n' '--- feature-set upgrade restrictions ---'
rg -n -C 8 'TechPreviewNoUpgrade|CustomNoUpgrade|non-upgradeable|not.*upgrade|upgrade.*feature.?set' . -g '*.go' -g '*.md' -g '*.yaml' -g '*.yml' -g '!vendor/**' | head -n 240

Repository: openshift/origin

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- complete upgradeability check ---'
sed -n '234,273p' test/e2e/upgrade/upgrade.go
printf '%s\n' '--- disruption skip and execution order ---'
sed -n '165,235p' test/extended/util/disruption/disruption.go
printf '%s\n' '--- upgrade feature-set references in vendored APIs and repository code ---'
rg -n -C 5 'TechPreviewNoUpgrade|ClusterNotUpgradeable|Upgradeable.*false|not upgradeable|not upgrade' vendor/k8s.io vendor/github.com/openshift . -g '*.go' -g '*.md' 2>/dev/null | head -n 260

Repository: openshift/origin

Length of output: 27021


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- CI jobs that combine upgrade tests and feature sets ---'
rg -n -C 5 'FEATURE_SET|TechPreviewNoUpgrade|techpreview|upgrade' ci-operator test -g '*.yaml' -g '*.yml' -g '*.json' -g '*.go' -g '*.md' 2>/dev/null | rg -C 3 'FEATURE_SET|TechPreviewNoUpgrade|techpreview|upgrade' | head -n 300
printf '%s\n' '--- upgrade suite documentation and job references ---'
rg -n -C 5 'Suite:upgrade|cluster-upgrade|openshift-tests.*upgrade|upgrade.*suite|upgrade.*job' . -g '*.md' -g '*.yaml' -g '*.yml' -g '*.json' -g '*.go' -g '!vendor/**' | head -n 240

Repository: openshift/origin

Length of output: 40588


Do not run this test in the real upgrade suite on TechPreviewNoUpgrade clusters. TechPreviewNoUpgrade sets the cluster as non-upgradeable, so the upgrade cannot complete. Upgradeable clusters skip this test, while TechPreviewNoUpgrade clusters enter Setup and can block at <-done. Move this scenario to a non-upgrade test suite or use a feature configuration supported during upgrades.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/extended/router/gatewayapi_management_mode_upgrade.go` around lines 47 -
73, Update GatewayAPIManagementModeUpgradeTest.Skip so this scenario is excluded
from real upgrade runs on TechPreviewNoUpgrade clusters; do not allow those
clusters to proceed into Setup. Move the scenario to a non-upgrade suite or gate
it on a feature configuration that supports upgrades, while preserving the
existing skip checks for other environments.


// Setup creates Gateway resources and records initial management mode
func (t *GatewayAPIManagementModeUpgradeTest) Setup(ctx context.Context, f *e2e.Framework) {
g.By("Setting up Gateway API management mode upgrade test")

t.oc = exutil.NewCLIWithFramework(f).AsAdmin()
t.namespace = f.Namespace.Name

// Get platform capabilities
t.loadBalancerSupported, t.managedDNS = getPlatformCapabilities(t.oc)

g.By("Recording initial management mode before upgrade")
ingress, err := getIngressCR(ctx, t.oc)
o.Expect(err).NotTo(o.HaveOccurred())

t.startMode = ingress.Spec.GatewayAPI.ManagementMode
if t.startMode == "" {
t.startMode = operatorv1alpha1.GatewayAPIManagementModeManaged
}
e2e.Logf("Starting with management mode: %s", t.startMode)

// Ensure we're in Managed mode for test setup
if t.startMode != operatorv1alpha1.GatewayAPIManagementModeManaged {
g.By("Transitioning to Managed mode for setup")
err = setManagementMode(ctx, t.oc, operatorv1alpha1.GatewayAPIManagementModeManaged)
o.Expect(err).NotTo(o.HaveOccurred())
err = waitForManagementModeTransition(ctx, t.oc, operatorv1alpha1.GatewayAPIManagementModeManaged, 5*time.Minute)
o.Expect(err).NotTo(o.HaveOccurred())
}
Comment on lines +78 to +95

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Restore the recorded start mode instead of forcing Managed.

Setup records t.startMode, but no later code reads it. Test restores currentMode (the post-upgrade mode), and Teardown always sets Managed. If the cluster began in Unmanaged mode, the test leaves the cluster in Managed mode after cleanup. This changes cluster state for subsequent tests in the same run.

Use t.startMode as the final target in Teardown.

♻️ Proposed change in Teardown
-	g.By("Ensuring Managed mode for cleanup")
-	err := setManagementMode(ctx, t.oc, operatorv1alpha1.GatewayAPIManagementModeManaged)
+	g.By("Restoring the original management mode for cleanup")
+	restoreMode := t.startMode
+	if restoreMode == "" {
+		restoreMode = operatorv1alpha1.GatewayAPIManagementModeManaged
+	}
+	err := setManagementMode(ctx, t.oc, restoreMode)
 	if err != nil {
-		e2e.Logf("Failed to set Managed mode during cleanup: %v", err)
+		e2e.Logf("Failed to restore management mode %s during cleanup: %v", restoreMode, err)
 	} else {
-		_ = waitForManagementModeTransition(ctx, t.oc, operatorv1alpha1.GatewayAPIManagementModeManaged, 5*time.Minute)
+		if waitErr := waitForManagementModeTransition(ctx, t.oc, restoreMode, 5*time.Minute); waitErr != nil {
+			e2e.Logf("Management mode did not settle on %s during cleanup: %v", restoreMode, waitErr)
+		}
 	}

Note: deleting resources requires Managed mode in some flows. If that is the case, keep Managed for the delete steps and restore t.startMode at the end of Teardown.

Also applies to: 216-226

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/extended/router/gatewayapi_management_mode_upgrade.go` around lines 89 -
106, Update Teardown to restore the recorded initial mode from t.startMode
rather than the post-upgrade current mode, preserving the original cluster
state. Keep Managed mode during any resource-deletion steps that require it,
then transition to t.startMode as the final cleanup action and wait for that
transition to complete.


g.By("Creating GatewayClass")
t.gatewayClassName = "upgrade-test-mgmt-mode"
gatewayClass := buildGatewayClass(t.gatewayClassName, "openshift.io/gateway-controller/v1")
_, err = t.oc.AdminGatewayApiClient().GatewayV1().GatewayClasses().Create(ctx, gatewayClass, metav1.CreateOptions{})
if err != nil && !apierrors.IsAlreadyExists(err) {
e2e.Failf("Failed to create GatewayClass: %v", err)
}

err = checkGatewayClassCondition(t.oc, t.gatewayClassName, string(gatewayv1.GatewayClassConditionStatusAccepted), metav1.ConditionTrue)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Creating Gateway")
defaultIngressDomain, err := getDefaultIngressClusterDomainName(t.oc, 1*time.Minute)
o.Expect(err).NotTo(o.HaveOccurred())
customDomain := strings.Replace(defaultIngressDomain, "apps.", "gw-upgrade-mgmt.", 1)

t.gatewayName = "upgrade-mgmt-mode-gateway"
t.hostname = "test-upgrade-mgmt." + customDomain
Comment on lines +109 to +114

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Confirm the custom domain replacement always applies.

strings.Replace(defaultIngressDomain, "apps.", "gw-upgrade-mgmt.", 1) is a no-op when the default ingress domain does not contain the literal apps.. In that case customDomain equals the default ingress domain, and the Gateway listener claims the same wildcard domain that the default IngressController serves. That can produce confusing routing failures instead of a clear test error.

Assert that the replacement changed the value, or derive the custom domain by prefixing the cluster base domain.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/extended/router/gatewayapi_management_mode_upgrade.go` around lines 120
- 125, Update the custom-domain setup near getDefaultIngressClusterDomainName
and the customDomain assignment to verify that replacing "apps." actually
changes defaultIngressDomain before using it; fail the test clearly when the
expected segment is absent, while preserving the existing gateway hostname
construction.


_, err = createAndCheckGateway(t.oc, t.gatewayName, t.gatewayClassName, customDomain, t.loadBalancerSupported)
o.Expect(err).NotTo(o.HaveOccurred())

if t.loadBalancerSupported {
g.By("Verifying LoadBalancer service is ready")
assertGatewayLoadbalancerReady(t.oc, t.gatewayName, t.gatewayName+"-openshift-default")
}

if t.managedDNS {
g.By("Verifying DNS controller creates DNSRecord")
assertDNSRecordStatus(t.oc, t.gatewayName)
}

g.By("Creating HTTPRoute with backend")
t.routeName = "test-upgrade-mgmt-route"
backendName := "echo-backend-" + t.gatewayName
createHttpRoute(t.oc, t.gatewayName, t.routeName, t.hostname, backendName)

g.By("Waiting for HTTPRoute to be accepted")
_, err = assertHttpRouteSuccessful(t.oc, t.gatewayName, t.routeName)
o.Expect(err).NotTo(o.HaveOccurred())

if t.loadBalancerSupported && t.managedDNS {
g.By("Verifying HTTP connectivity before upgrade")
assertHttpRouteConnection(t.hostname)
e2e.Logf("HTTPRoute connectivity verified before upgrade")
}

e2e.Logf("Setup complete: Gateway and HTTPRoute created in %s mode", t.startMode)
}

// Test validates resources after upgrade and tests mode transitions
func (t *GatewayAPIManagementModeUpgradeTest) Test(ctx context.Context, f *e2e.Framework, done <-chan struct{}, _ upgrades.UpgradeType) {
g.By("Validating Gateway API management mode functionality after upgrade")

// Block until upgrade completes
g.By("Waiting for upgrade to complete")
<-done

g.By("Verifying Gateway still exists and is programmed")
_, err := checkGatewayStatus(t.oc, t.gatewayName, ingressNamespace, t.loadBalancerSupported)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Verifying HTTPRoute still exists and is accepted")
_, err = assertHttpRouteSuccessful(t.oc, t.gatewayName, t.routeName)
o.Expect(err).NotTo(o.HaveOccurred())

if t.loadBalancerSupported && t.managedDNS {
g.By("Verifying HTTP connectivity after upgrade")
assertHttpRouteConnection(t.hostname)
}

g.By("Checking current management mode after upgrade")
ingress, err := getIngressCR(ctx, t.oc)
o.Expect(err).NotTo(o.HaveOccurred())

currentMode := ingress.Spec.GatewayAPI.ManagementMode
if currentMode == "" {
currentMode = operatorv1alpha1.GatewayAPIManagementModeManaged
}
e2e.Logf("Current management mode after upgrade: %s", currentMode)

// Test mode transitions in both directions
g.By("Testing mode transitions after upgrade")

// Transition 1: Current mode → Opposite mode
var targetMode1 operatorv1alpha1.GatewayAPIManagementMode
if currentMode == operatorv1alpha1.GatewayAPIManagementModeManaged {
targetMode1 = operatorv1alpha1.GatewayAPIManagementModeUnmanaged
} else {
targetMode1 = operatorv1alpha1.GatewayAPIManagementModeManaged
}

g.By(fmt.Sprintf("Transitioning from %s to %s", currentMode, targetMode1))
err = setManagementMode(ctx, t.oc, targetMode1)
o.Expect(err).NotTo(o.HaveOccurred())

err = waitForManagementModeTransition(ctx, t.oc, targetMode1, 5*time.Minute)
o.Expect(err).NotTo(o.HaveOccurred())

t.validateModeState(ctx, targetMode1)

g.By("Verifying Gateway and HTTPRoute remain functional after first transition")
_, err = checkGatewayStatus(t.oc, t.gatewayName, ingressNamespace, t.loadBalancerSupported)
o.Expect(err).NotTo(o.HaveOccurred())

_, err = assertHttpRouteSuccessful(t.oc, t.gatewayName, t.routeName)
o.Expect(err).NotTo(o.HaveOccurred())

// Transition 2: Opposite mode → Original mode
targetMode2 := currentMode

g.By(fmt.Sprintf("Transitioning from %s back to %s", targetMode1, targetMode2))
err = setManagementMode(ctx, t.oc, targetMode2)
o.Expect(err).NotTo(o.HaveOccurred())

err = waitForManagementModeTransition(ctx, t.oc, targetMode2, 5*time.Minute)
o.Expect(err).NotTo(o.HaveOccurred())

t.validateModeState(ctx, targetMode2)

g.By("Verifying Gateway and HTTPRoute remain functional after second transition")
_, err = checkGatewayStatus(t.oc, t.gatewayName, ingressNamespace, t.loadBalancerSupported)
o.Expect(err).NotTo(o.HaveOccurred())

_, err = assertHttpRouteSuccessful(t.oc, t.gatewayName, t.routeName)
o.Expect(err).NotTo(o.HaveOccurred())

if t.loadBalancerSupported && t.managedDNS {
g.By("Verifying HTTP connectivity still works after both transitions")
assertHttpRouteConnection(t.hostname)
}

// Verify DNS and controller reconciliation if in Managed mode
if targetMode2 == operatorv1alpha1.GatewayAPIManagementModeManaged {
g.By("Verifying controllers are actively reconciling in Managed mode")

if t.managedDNS {
g.By("Verifying DNS controller is reconciling DNSRecords")
assertDNSRecordStatus(t.oc, t.gatewayName)
}

g.By("Verifying gateway-status controller is updating Gateway status")
_, err = checkGatewayStatus(t.oc, t.gatewayName, ingressNamespace, t.loadBalancerSupported)
o.Expect(err).NotTo(o.HaveOccurred())
}

e2e.Logf("Successfully tested management mode transitions after upgrade: %s → %s → %s",
currentMode, targetMode1, targetMode2)
}

// validateModeState verifies the cluster state matches the expected management mode
func (t *GatewayAPIManagementModeUpgradeTest) validateModeState(ctx context.Context, expectedMode operatorv1alpha1.GatewayAPIManagementMode) {
if expectedMode == operatorv1alpha1.GatewayAPIManagementModeManaged {
g.By("Validating Managed mode state")

err := checkIngressCondition(ctx, t.oc, "GatewayAPICRDsManaged", metav1.ConditionTrue, "")
o.Expect(err).NotTo(o.HaveOccurred())

err = assertVAPExists(ctx, t.oc, gwapiCRDVAPName)
o.Expect(err).NotTo(o.HaveOccurred(), "VAP should exist in Managed mode")

err = checkIstiodRunning(t.oc, 5*time.Minute)
o.Expect(err).NotTo(o.HaveOccurred(), "Istiod should be running in Managed mode")

e2e.Logf("Validated Managed mode state: VAP and Istiod are present")
} else {
g.By("Validating Unmanaged mode state")

err := checkIngressCondition(ctx, t.oc, "GatewayAPICRDsManaged", metav1.ConditionFalse, "Unmanaged")
o.Expect(err).NotTo(o.HaveOccurred())

err = assertVAPDeleted(ctx, t.oc, gwapiCRDVAPName)
o.Expect(err).NotTo(o.HaveOccurred(), "VAP should be deleted in Unmanaged mode")

waitForIstiodPodDeletion(t.oc)

e2e.Logf("Validated Unmanaged mode state: VAP and Istiod are removed")
}

// CRDs should always be present regardless of mode
g.By("Verifying Gateway API CRDs are still present")
assertGatewayAPICRDsInstalled(ctx, t.oc)
}

// Teardown cleans up Gateway API resources
func (t *GatewayAPIManagementModeUpgradeTest) Teardown(ctx context.Context, f *e2e.Framework) {
if t.oc == nil || t.gatewayName == "" {
e2e.Logf("Skipping cleanup because setup did not initialize resources")
return
}

g.By("Ensuring Managed mode for cleanup")
err := setManagementMode(ctx, t.oc, operatorv1alpha1.GatewayAPIManagementModeManaged)
if err != nil {
e2e.Logf("Failed to set Managed mode during cleanup: %v", err)
} else {
_ = waitForManagementModeTransition(ctx, t.oc, operatorv1alpha1.GatewayAPIManagementModeManaged, 5*time.Minute)
}

g.By("Deleting HTTPRoute")
err = t.oc.AdminGatewayApiClient().GatewayV1().HTTPRoutes(t.namespace).Delete(ctx, t.routeName, metav1.DeleteOptions{})
if err != nil && !apierrors.IsNotFound(err) {
e2e.Logf("Failed to delete HTTPRoute: %v", err)
}

g.By("Deleting Gateway")
err = t.oc.AdminGatewayApiClient().GatewayV1().Gateways(ingressNamespace).Delete(ctx, t.gatewayName, metav1.DeleteOptions{})
if err != nil && !apierrors.IsNotFound(err) {
e2e.Logf("Failed to delete Gateway: %v", err)
}

g.By("Waiting for Gateway deployment to be deleted")
if err := waitForGatewayDeploymentDeletion(t.oc, t.gatewayName); err != nil {
e2e.Logf("Gateway deployment was not cleaned up: %v", err)
}

g.By("Deleting GatewayClass")
err = t.oc.AdminGatewayApiClient().GatewayV1().GatewayClasses().Delete(ctx, t.gatewayClassName, metav1.DeleteOptions{})
if err != nil && !apierrors.IsNotFound(err) {
e2e.Logf("Failed to delete GatewayClass: %v", err)
}

e2e.Logf("Gateway API management mode upgrade test cleanup complete")
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading