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 {