From d7402582f3a1a8cd2c9a6eac88d36feb17e40376 Mon Sep 17 00:00:00 2001 From: Renuka Fernando Date: Thu, 13 Aug 2026 23:00:04 +0530 Subject: [PATCH] fix(controller): rebuild Envoy xDS snapshot after wiring transformers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The initial Envoy snapshot was generated before SetTransformers ran, so it fell back to the legacy translation path — naming clusters "cluster__" and routes without the header-hash discriminator. The policy engine's resources are built from the transformer path ("upstream___" clusters, header-hashed route names), so the two disagreed until the first redeploy: cluster-header APIs failed with cluster_not_found and header-matched routes returned 500 "policy chain not found". - Regenerate the Envoy snapshot after transformers are wired and runtime configs are loaded, before the policy snapshot is built - Warn when TranslateConfigs silently falls back to the legacy path because no transformer is registered for a non-WebSub kind Signed-off-by: Renuka Fernando --- .../gateway-controller/cmd/controller/main.go | 16 ++++++++++++++++ gateway/gateway-controller/pkg/xds/translator.go | 11 +++++++++++ 2 files changed, 27 insertions(+) diff --git a/gateway/gateway-controller/cmd/controller/main.go b/gateway/gateway-controller/cmd/controller/main.go index ec2549f67..45c332f3f 100644 --- a/gateway/gateway-controller/cmd/controller/main.go +++ b/gateway/gateway-controller/cmd/controller/main.go @@ -469,6 +469,22 @@ func main() { slog.Int("total_apis", len(loadedAPIs)), slog.Int("configs_loaded", loadedCount)) + // Regenerate the Envoy xDS snapshot now that the transformers are wired (above) + // and runtime configs are loaded. The initial snapshot generated earlier ran + // before SetTransformers, so it fell back to the legacy translation path — naming + // clusters "cluster__" and routes without the header-hash + // discriminator. The policy engine's resources are keyed off the transformer path + // ("upstream___" clusters, header-hashed route names), so without + // this rebuild Envoy and the policy engine disagree: cluster-header APIs fail with + // cluster_not_found and header-matched routes 500 with "policy chain not found" + // until the first redeploy happens to re-run the transformer path. + log.Info("Regenerating xDS snapshot via transformer path after wiring transformers") + ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) + if err := snapshotManager.UpdateSnapshot(ctx, ""); err != nil { + log.Warn("Failed to regenerate xDS snapshot after transformer init", slog.Any("error", err)) + } + cancel() + // Generate initial policy snapshot log.Info("Generating initial policy xDS snapshot") ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) diff --git a/gateway/gateway-controller/pkg/xds/translator.go b/gateway/gateway-controller/pkg/xds/translator.go index ae27e6832..580a335b7 100644 --- a/gateway/gateway-controller/pkg/xds/translator.go +++ b/gateway/gateway-controller/pkg/xds/translator.go @@ -747,6 +747,17 @@ func (t *Translator) TranslateConfigs( routesList, clusterList, err = t.eventGatewayHooks.TranslateWebSubAPI(t, cfg, configs) } } else { + // No transformer produced routes for a non-WebSub kind. If the kind has + // no transformer registered at all, we silently fell back here — a nil or + // unwired transformer map returns ok=false at the check above, with no + // error. Surface it, because the resulting Envoy snapshot uses legacy + // cluster/route naming that the policy engine's transformer-path resources + // don't match. (A transformer that errored is already logged above.) + if _, ok := t.transformers[cfg.Kind]; !ok { + log.Warn("No transformer registered for API kind; using legacy translation path", + slog.String("kind", cfg.Kind), + slog.String("id", cfg.UUID)) + } routesList, clusterList, err = t.translateAPIConfig(cfg, configs) } if err != nil {