Skip to content

Recover a GatewayClass wedged in Terminating - #5292

Open
electricjesus wants to merge 1 commit into
tigera:masterfrom
electricjesus:seth/gatewayclass-unwedge
Open

Recover a GatewayClass wedged in Terminating#5292
electricjesus wants to merge 1 commit into
tigera:masterfrom
electricjesus:seth/gatewayclass-unwedge

Conversation

@electricjesus

@electricjesus electricjesus commented Sep 1, 2026

Copy link
Copy Markdown
Member

Description

Deleting the GatewayAPI CR marks the GatewayClass for deletion, since we set a controller ownerRef on it. Envoy Gateway's gateway-exists-finalizer blocks that delete while any Gateway still uses the class, so it never finishes and nothing repairs it.

Envoy Gateway keeps managing a terminating class but skips its parametersRef, because it only resolves the ref when DeletionTimestamp is nil (internal/provider/kubernetes/controller.go:366, EG v1.8.3). Gateway deployments fall back to the upstream docker.io/envoyproxy/envoy image with no imagePullSecrets, and the class still reports Accepted=True. Re-creating the CR restores the EnvoyProxy but can't clear a DeletionTimestamp, so the cluster stays wrong until someone clears the finalizer by hand.

The gateway pod carries on serving and tigerastatus stays green, so this shows up later as a pod that won't start after a restart or drain.

This watches GatewayClass, clears the finalizers on a terminating one, and reports the repair on tigerastatus. The next reconcile re-creates the class and Envoy Gateway resolves parametersRef normally.

Clearing another controller's finalizer is deliberate. The wedge only exists because our ownerRef marked a class that Envoy Gateway won't release, and there's no other way out of it. Gateways lose their managed class for a few seconds while the class is re-created, so the proxy deployment is torn down and re-provisioned.

Test plan

go build, go vet, gofmt and go test ./pkg/controller/gatewayapi/... all pass.

New unit test covers a class with DeletionTimestamp set, and asserts both the release and the tigerastatus message. It fails when the repair is short-circuited.

Checked by hand on a GCP kubeadm cluster running Enterprise master. Waiting, restarting envoy-gateway, annotating the Gateway and deleting the deployment all left it wedged. Clearing the finalizer and reconciling fixed it in 6 seconds.

Not covered: no e2e, and the upstream skip in EG is unchanged since v0.5.0 and still on EG main, so this only handles our side of it.

Release Note

Fixed a bug where deleting and re-creating the GatewayAPI resource could leave gateway deployments running an incorrect image without image pull secrets.

For PR author

  • Tests for change.
  • If changing pkg/apis/, run make gen-files
  • If changing versions, run make gen-versions

Deleting the GatewayAPI CR marks the GatewayClass for deletion through the
ownerRef we set on it. Envoy Gateway's gateway-exists finalizer then blocks
that delete while any Gateway still uses the class, so it never completes.

Envoy Gateway keeps managing a class in that state but skips its parametersRef,
because it only resolves the ref when DeletionTimestamp is nil. Gateway
Deployments fall back to the upstream Envoy image with no imagePullSecrets, and
the class is still reported Accepted=True, so nothing surfaces the fault.
Re-creating the CR restores the EnvoyProxy but cannot clear a DeletionTimestamp.

Watch GatewayClass, clear the finalizers on a terminating one so the delete can
finish, and report it on TigeraStatus rather than healing silently.
@electricjesus
electricjesus requested a review from a team as a code owner September 1, 2026 18:25
Copilot AI lite review requested due to automatic review settings September 1, 2026 18:25
@marvin-tigera marvin-tigera added this to the v1.45.0 milestone Sep 1, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes a Calico Enterprise Gateway API edge case where a GatewayClass can become stuck in Terminating (blocked by Envoy Gateway’s finalizer), causing Envoy Gateway to skip parametersRef resolution and potentially roll gateway Deployments back to an upstream Envoy image without expected pull secrets.

Changes:

  • Adds a lazy watch on GatewayClass resources so the controller reconciles when a class becomes wedged in Terminating.
  • Introduces reconcile logic to “unwedge” terminating GatewayClasses by clearing finalizers and surfacing the repair via TigeraStatus.
  • Adds a unit test covering the wedged-Terminating repair path and its status reporting.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
pkg/controller/gatewayapi/gatewayapi_controller.go Adds GatewayClass watch wiring and implements the “unwedge terminating GatewayClass” repair logic surfaced through status.
pkg/controller/gatewayapi/gatewayapi_controller_test.go Adds a unit test validating that a terminating GatewayClass is released and the repair is reported via SetDegraded.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +689 to +702
blocking := 0
for j := range gateways {
if string(gateways[j].Spec.GatewayClassName) == name {
blocking++
}
}

reqLogger.Info("GatewayClass is stuck terminating; clearing finalizers so it can be re-created",
"gatewayClass", name, "deletionTimestamp", gc.DeletionTimestamp,
"finalizers", gc.Finalizers, "blockingGateways", blocking)

patched := gc.DeepCopy()
patched.Finalizers = nil
if err := r.client.Patch(ctx, patched, client.MergeFrom(gc)); err != nil {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants