Skip to content

Fix startup xDS snapshot using legacy cluster names after controller restart - #3236

Open
MinuraKariyawasam wants to merge 1 commit into
wso2:mainfrom
MinuraKariyawasam:fix-3197-startup-snapshot-cluster-naming
Open

Fix startup xDS snapshot using legacy cluster names after controller restart#3236
MinuraKariyawasam wants to merge 1 commit into
wso2:mainfrom
MinuraKariyawasam:fix-3197-startup-snapshot-cluster-naming

Conversation

@MinuraKariyawasam

Copy link
Copy Markdown

Purpose

Resolves #3197

Whenever the gateway controller restarts (a helm upgrade, a pod restart, anything), every API that was already deployed starts returning 503 with cluster_not_found. Auth still works (you get 401 without a token), so it looks like the backend is down, but it isn't. The only way to recover is to redeploy every API from the Publisher, and it breaks again on the next restart.

The root cause is an ordering problem in cmd/controller/main.go. The first Envoy xDS snapshot is built before the transformers are wired into the translator. At that point t.transformers is still nil, so the translator quietly falls back to the legacy path, which names clusters cluster_<scheme>_<host>. The policy engine builds its routes later through the transformer path, which names the same clusters upstream_<name>_<host>_<port>. The two sides never agree on a name, so Envoy can't find any cluster the policy engine routes to. Nothing ever rebuilds the snapshot either, because control plane reconciliation skips APIs whose deployed_at hasn't changed. So the broken state stays until someone redeploys by hand.

Goals

  1. The very first snapshot after a restart should use the same cluster and route names the policy engine uses, so already-deployed APIs keep working with no redeploy.
  2. If the legacy path is ever taken unexpectedly again, it should show up in the logs instead of failing silently.

Approach

Fix the order instead of building the snapshot twice. This PR moves the transformer registry construction (policyVersionResolver, RestAPITransformer, LLMTransformer, transform.NewRegistry) and the translator.SetTransformers(...) call up, so they run before the initial snapshotManager.UpdateSnapshot(ctx, ""). The constructors just assemble structs, and everything they need (configStore, db, cfg, policyDefinitions) already exists at that point. policyManager.SetTransformers(transformerRegistry) stays where the policy manager is created and reuses the same registry.

The issue thread suggested a different fix: keep the order as is, and add a second UpdateSnapshot call after the transformers are wired. I went with the reorder instead, for one reason. In a real deployment the gateway runtimes keep serving traffic while the controller restarts, and they reconnect the moment the xDS server starts (which happens before the transformers were wired). With the second-snapshot approach, Envoy would still get the broken legacy snapshot first, live traffic would 503 until the second snapshot arrives, and the routes would churn twice on every controller boot. With the reorder, the first snapshot is already correct, so there is no broken window and no extra rebuild.

The event-gateway controller (event-gateway/gateway-controller/cmd/controller/main.go) has the same startup order and shares the same translator package, so the same reorder is applied there too.

On top of that, the silent fall-through in TranslateConfigs now logs a warning when no transformer is registered for a kind (except WebSubApi, which intentionally uses the legacy path). LlmProviderTemplate objects live in a separate store map and never reach TranslateConfigs, so the warning can't fire for them.

Nothing else changes:

  • If Transform fails for one config, it still falls back to the legacy path with an error log, same as before.
  • WebSubApi translation is untouched.
  • No other module imports pkg/xds or pkg/transform, so the change is contained to the two controller binaries.

Note: event-gateway/gateway-controller/cmd/controller currently fails to compile on main for an unrelated reason (its adminserver.NewServer call was not updated when that function gained a middleware parameter). That breakage exists before this PR and is not touched here. This PR's change to that file was typechecked separately (with that one call patched locally) and go vet passes.

User stories

As an operator running the Platform Gateway against an APIM control plane, I can restart or upgrade the gateway controller without every deployed API breaking with 503 cluster_not_found, and without redeploying each API from the Publisher afterwards.

Automation tests

  • Unit tests: the full gateway-controller module suite passes (30 packages, including pkg/xds, pkg/transform, cmd/controller). The event-gateway/gateway-controller module has no unit tests outside the pre-broken cmd/controller package (see the note above).
  • Integration tests: verified by hand using the steps from [Bug]: Gateway controller restart silently breaks all API traffic with 503 cluster_not_found until every API is redeployed #3197. Deploy an API top-down from the Publisher (200), restart the controller with kubectl rollout restart (used to give 503 cluster_not_found, now gives 200), and check Envoy's /clusters, which shows the upstream_<name>_<host>_<port> names right after the restart.

Security checks

Samples

N/A

Related PRs

None

Test environment

Go (toolchain go1.26.5), macOS (Apple Silicon). Reproduced and verified on: Gateway 1.2.0 Helm chart on k3s v1.35.0, WSO2 APIM 4.7.0 control plane, PostgreSQL 16.

…restart

The initial Envoy xDS snapshot was generated before the transformer
registry was wired into the translator. With t.transformers still nil,
the translator silently fell back to the legacy translation path, which
names clusters "cluster_<scheme>_<host>", while the policy engine's
routes (built later via the transformer path) reference
"upstream_<name>_<host>_<port>". After any controller restart, every
request to a previously deployed API failed with 503 cluster_not_found
until the API was manually redeployed, since control-plane
reconciliation skips APIs whose deployed_at is unchanged.

Fix: build the transformer registry and call translator.SetTransformers
before the initial snapshot is generated, so the very first snapshot
already uses transformer-path naming. This also avoids the transient
mismatch window a regenerate-after-wiring approach would leave for
already-running gateway runtimes that reconnect as soon as the xDS
server starts. The event-gateway controller has the same
initialization order, so the same reorder is applied there.

Also log a warning when the translator falls back to the legacy path
for a non-WebSubApi kind, so this class of regression is visible in
logs instead of failing silently.

Resolves wso2#3197
@CLAassistant

CLAassistant commented Aug 17, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a35e4f53-f07a-4d37-9bcc-99bd190ea16d

📥 Commits

Reviewing files that changed from the base of the PR and between a13e138 and b1aa2eb.

📒 Files selected for processing (3)
  • event-gateway/gateway-controller/cmd/controller/main.go
  • gateway/gateway-controller/cmd/controller/main.go
  • gateway/gateway-controller/pkg/xds/translator.go

Included review availability: Your plan includes up to 1 review per rolling hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The controller now creates one transformer registry before the initial xDS snapshot, shares it with Envoy translation and policy management, and warns when non-WebSubApi configurations lack a registered transformer.

Changes

Transformer startup and translation

Layer / File(s) Summary
Early shared registry initialization
event-gateway/.../cmd/controller/main.go, gateway/.../cmd/controller/main.go
The controller creates REST and LLM transformers before the initial xDS snapshot, configures the Envoy translator, and reuses the registry for the policy manager.
Missing transformer handling
gateway/gateway-controller/pkg/xds/translator.go
TranslateConfigs logs warnings for missing transformers on non-WebSubApi configurations and preserves fallback translation.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to b1aa2

The PR makes a localized startup-ordering fix so existing APIs retain consistent routing after controller restarts, with added warning logs for unexpected legacy translation. No actionable merge-blocking risk remains after normal checks and review.

Possibly related PRs

  • wso2/api-platform#3223: Modifies controller startup transformer wiring and adds missing-transformer warnings in TranslateConfigs.

Suggested reviewers: anugayan

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the startup xDS snapshot issue caused by legacy cluster names after a controller restart.
Description check ✅ Passed The description covers the required purpose, goals, approach, testing, security checks, samples, related PRs, and test environment; only Documentation is missing.
Linked Issues check ✅ Passed The changes directly address issue #3197 by ensuring restart snapshots use transformer-based names and preserve API traffic without redeployment.
Out of Scope Changes check ✅ Passed The transformer initialization changes and fallback warning are related to the restart failure and its silent legacy-path behavior.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Gateway controller restart silently breaks all API traffic with 503 cluster_not_found until every API is redeployed

2 participants