From 12a027f037d5b7049a6b6529f226c992daf75fca Mon Sep 17 00:00:00 2001 From: Kadir Can Yildirim <252162627+kadircanyildirm-crypto@users.noreply.github.com> Date: Sun, 2 Aug 2026 20:04:25 +0300 Subject: [PATCH 1/4] fix: validate tracing provider completeness after EnvoyProxy merge Drop the 'host or backendRefs needs to be set' CEL rule from TracingProvider so a per-Gateway EnvoyProxy can override a single field (e.g. serviceName) and inherit the rest via mergeType. The completeness check now runs in processTracing after the GatewayClass-level and Gateway-level configs are merged, surfacing an InvalidParameters Gateway condition instead of an admission error. Fixes #9527 Co-Authored-By: Claude Fable 5 Signed-off-by: Kadir Can Yildirim <252162627+kadircanyildirm-crypto@users.noreply.github.com> --- api/v1alpha1/envoyproxy_tracing_types.go | 6 ++++- .../gateway.envoyproxy.io_envoyproxies.yaml | 2 -- .../gateway.envoyproxy.io_envoyproxies.yaml | 2 -- internal/gatewayapi/listener.go | 10 ++++--- internal/gatewayapi/listener_test.go | 26 +++++++++++++++++++ .../bug_fixes/9527-tracing-cel-merge.md | 1 + test/cel-validation/envoyproxy_test.go | 9 ++++--- 7 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 release-notes/current/bug_fixes/9527-tracing-cel-merge.md diff --git a/api/v1alpha1/envoyproxy_tracing_types.go b/api/v1alpha1/envoyproxy_tracing_types.go index 11eaeb686d..7db261f0b6 100644 --- a/api/v1alpha1/envoyproxy_tracing_types.go +++ b/api/v1alpha1/envoyproxy_tracing_types.go @@ -36,7 +36,11 @@ const ( // TracingProvider defines the tracing provider configuration. // -// +kubebuilder:validation:XValidation:message="host or backendRefs needs to be set",rule="has(self.host) || self.backendRefs.size() > 0" +// A provider is only required to set host or backendRefs after the +// GatewayClass-level and Gateway-level EnvoyProxy configs are merged +// (see EnvoyProxySpec.MergeType), so completeness is validated during +// translation instead of by a CEL rule here. +// // +kubebuilder:validation:XValidation:message="BackendRefs must be used, backendRef is not supported.",rule="!has(self.backendRef)" // +kubebuilder:validation:XValidation:message="BackendRefs only support Service and Backend kind.",rule="has(self.backendRefs) ? self.backendRefs.all(f, f.kind == 'Service' || f.kind == 'Backend') : true" // +kubebuilder:validation:XValidation:message="BackendRefs only support Core and gateway.envoyproxy.io group.",rule="has(self.backendRefs) ? (self.backendRefs.all(f, f.group == \"\" || f.group == 'gateway.envoyproxy.io')) : true" diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml index 9e52118274..75cce2c965 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -18193,8 +18193,6 @@ spec: - type type: object x-kubernetes-validations: - - message: host or backendRefs needs to be set - rule: has(self.host) || self.backendRefs.size() > 0 - message: BackendRefs must be used, backendRef is not supported. rule: '!has(self.backendRef)' - message: BackendRefs only support Service and Backend kind. diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index 256ae54218..d0c88ab131 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -18192,8 +18192,6 @@ spec: - type type: object x-kubernetes-validations: - - message: host or backendRefs needs to be set - rule: has(self.host) || self.backendRefs.size() > 0 - message: BackendRefs must be used, backendRef is not supported. rule: '!has(self.backendRef)' - message: BackendRefs only support Service and Backend kind. diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go index b7f6ca661c..88e0b55989 100644 --- a/internal/gatewayapi/listener.go +++ b/internal/gatewayapi/listener.go @@ -994,11 +994,13 @@ func (t *Translator) processTracing(gwCtx *GatewayContext, envoyproxy *egv1a1.En // fallback to host and port // TODO: remove support for Host/Port in v1.2 if len(ds) == 0 { - var host string - var port uint32 - if tracing.Provider.Host != nil { - host, port = *tracing.Provider.Host, uint32(tracing.Provider.Port) + // Validated here instead of by a CRD CEL rule so that a partial provider + // (e.g. only serviceName) can be completed by the GatewayClass-level and + // Gateway-level EnvoyProxy merge before the check runs. + if tracing.Provider.Host == nil { + return nil, fmt.Errorf("host or backendRefs needs to be set on the tracing provider after merging EnvoyProxy configs") } + host, port := *tracing.Provider.Host, uint32(tracing.Provider.Port) ds = destinationSettingFromHostAndPort(settingName, host, port) authority = host } diff --git a/internal/gatewayapi/listener_test.go b/internal/gatewayapi/listener_test.go index 13005bcaf7..279e70b94e 100644 --- a/internal/gatewayapi/listener_test.go +++ b/internal/gatewayapi/listener_test.go @@ -934,6 +934,32 @@ func TestProcessTracingServiceName(t *testing.T) { mergeGateways: true, expectedServiceName: "test-gateway-class", // Should use gateway class name when merging }, + { + name: "tracing provider without backendRefs or host", + gateway: &gwapiv1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-gateway", + Namespace: "test-namespace", + }, + }, + envoyProxy: &egv1a1.EnvoyProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-proxy", + Namespace: "test-namespace", + }, + Spec: egv1a1.EnvoyProxySpec{ + Telemetry: &egv1a1.ProxyTelemetry{ + Tracing: &egv1a1.ProxyTracing{ + Provider: egv1a1.TracingProvider{ + Type: egv1a1.TracingProviderTypeOpenTelemetry, + ServiceName: new("only-name-overridden"), + }, + }, + }, + }, + }, + expectError: true, + }, } for _, tc := range cases { diff --git a/release-notes/current/bug_fixes/9527-tracing-cel-merge.md b/release-notes/current/bug_fixes/9527-tracing-cel-merge.md new file mode 100644 index 0000000000..bcce3b24d4 --- /dev/null +++ b/release-notes/current/bug_fixes/9527-tracing-cel-merge.md @@ -0,0 +1 @@ +Fixed CRD validation rejecting a per-Gateway EnvoyProxy that overrides only part of the tracing provider (e.g. `serviceName`) when relying on `mergeType` to inherit the rest: the host/backendRefs completeness check now runs after the GatewayClass-level and Gateway-level configs are merged, surfacing an `InvalidParameters` Gateway condition instead of blocking admission. diff --git a/test/cel-validation/envoyproxy_test.go b/test/cel-validation/envoyproxy_test.go index 770dcc6494..c157a62afb 100644 --- a/test/cel-validation/envoyproxy_test.go +++ b/test/cel-validation/envoyproxy_test.go @@ -1311,19 +1311,22 @@ func TestEnvoyProxyProvider(t *testing.T) { }, }, { - desc: "tracing-empty-backend", + // A partial provider must be accepted at admission so it can be + // completed by the GatewayClass/Gateway EnvoyProxy merge; completeness + // is validated during translation instead. + desc: "tracing-partial-provider-for-merge", mutate: func(envoy *egv1a1.EnvoyProxy) { envoy.Spec = egv1a1.EnvoyProxySpec{ Telemetry: &egv1a1.ProxyTelemetry{ Tracing: &egv1a1.ProxyTracing{ Provider: egv1a1.TracingProvider{ - Type: egv1a1.TracingProviderTypeOpenTelemetry, + Type: egv1a1.TracingProviderTypeOpenTelemetry, + ServiceName: new("my-override"), }, }, }, } }, - wantErrors: []string{"host or backendRefs needs to be set"}, }, { desc: "valid-tracing-service-name", From f6ae08905e743a9ee8b1498f6d39a2ba7d4cd2af Mon Sep 17 00:00:00 2001 From: Kadir Can Yildirim <252162627+kadircanyildirm-crypto@users.noreply.github.com> Date: Sun, 2 Aug 2026 20:20:54 +0300 Subject: [PATCH 2/4] docs: regenerate API reference for TracingProvider comment change Co-Authored-By: Claude Fable 5 Signed-off-by: Kadir Can Yildirim <252162627+kadircanyildirm-crypto@users.noreply.github.com> --- site/content/en/latest/api/extension_types.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 5115c98ca5..fd6da0a127 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -6525,6 +6525,11 @@ _Appears in:_ TracingProvider defines the tracing provider configuration. +A provider is only required to set host or backendRefs after the +GatewayClass-level and Gateway-level EnvoyProxy configs are merged +(see EnvoyProxySpec.MergeType), so completeness is validated during +translation instead of by a CEL rule here. + _Appears in:_ - [ProxyTracing](#proxytracing) From 1e85b90bce0d2179c4fee826e1ed3e7b090f1de3 Mon Sep 17 00:00:00 2001 From: Kadir Can Yildirim <252162627+kadircanyildirm-crypto@users.noreply.github.com> Date: Mon, 3 Aug 2026 11:16:52 +0300 Subject: [PATCH 3/4] Regenerate gateway-crds-helm template snapshots Co-Authored-By: Claude Fable 5 Signed-off-by: Kadir Can Yildirim <252162627+kadircanyildirm-crypto@users.noreply.github.com> --- test/helm/gateway-crds-helm/all.out.yaml | 2 -- test/helm/gateway-crds-helm/e2e.out.yaml | 2 -- test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml | 2 -- 3 files changed, 6 deletions(-) diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index 5cefe156f4..999f646811 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -52059,8 +52059,6 @@ spec: - type type: object x-kubernetes-validations: - - message: host or backendRefs needs to be set - rule: has(self.host) || self.backendRefs.size() > 0 - message: BackendRefs must be used, backendRef is not supported. rule: '!has(self.backendRef)' - message: BackendRefs only support Service and Backend kind. diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml index 25daff866b..6d93ffbd6b 100644 --- a/test/helm/gateway-crds-helm/e2e.out.yaml +++ b/test/helm/gateway-crds-helm/e2e.out.yaml @@ -27997,8 +27997,6 @@ spec: - type type: object x-kubernetes-validations: - - message: host or backendRefs needs to be set - rule: has(self.host) || self.backendRefs.size() > 0 - message: BackendRefs must be used, backendRef is not supported. rule: '!has(self.backendRef)' - message: BackendRefs only support Service and Backend kind. diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml index 9fa651b4d8..aa17b2b566 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -27997,8 +27997,6 @@ spec: - type type: object x-kubernetes-validations: - - message: host or backendRefs needs to be set - rule: has(self.host) || self.backendRefs.size() > 0 - message: BackendRefs must be used, backendRef is not supported. rule: '!has(self.backendRef)' - message: BackendRefs only support Service and Backend kind. From 14f48529837c7020cab0f26f2f159ad43aaa0c5a Mon Sep 17 00:00:00 2001 From: Kadir Can Yildirim <252162627+kadircanyildirm-crypto@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:33:01 +0300 Subject: [PATCH 4/4] Skip tracing with a log message instead of failing the Gateway An incomplete tracing provider now turns tracing off for that Gateway instead of setting Accepted=False, so an observability misconfiguration does not stop the proxy from being provisioned. Signed-off-by: Kadir Can Yildirim <252162627+kadircanyildirm-crypto@users.noreply.github.com> --- api/v1alpha1/envoyproxy_tracing_types.go | 5 +++-- internal/gatewayapi/listener.go | 11 ++++++++--- internal/gatewayapi/listener_test.go | 11 +++-------- .../current/bug_fixes/9527-tracing-cel-merge.md | 2 +- site/content/en/latest/api/extension_types.md | 5 +++-- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/api/v1alpha1/envoyproxy_tracing_types.go b/api/v1alpha1/envoyproxy_tracing_types.go index 7db261f0b6..f4fcfb15e5 100644 --- a/api/v1alpha1/envoyproxy_tracing_types.go +++ b/api/v1alpha1/envoyproxy_tracing_types.go @@ -38,8 +38,9 @@ const ( // // A provider is only required to set host or backendRefs after the // GatewayClass-level and Gateway-level EnvoyProxy configs are merged -// (see EnvoyProxySpec.MergeType), so completeness is validated during -// translation instead of by a CEL rule here. +// (see EnvoyProxySpec.MergeType), so completeness is checked during +// translation instead of by a CEL rule here. A provider that is still +// incomplete after the merge turns tracing off for that Gateway. // // +kubebuilder:validation:XValidation:message="BackendRefs must be used, backendRef is not supported.",rule="!has(self.backendRef)" // +kubebuilder:validation:XValidation:message="BackendRefs only support Service and Backend kind.",rule="has(self.backendRefs) ? self.backendRefs.all(f, f.kind == 'Service' || f.kind == 'Backend') : true" diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go index 88e0b55989..7768b24ca9 100644 --- a/internal/gatewayapi/listener.go +++ b/internal/gatewayapi/listener.go @@ -994,11 +994,16 @@ func (t *Translator) processTracing(gwCtx *GatewayContext, envoyproxy *egv1a1.En // fallback to host and port // TODO: remove support for Host/Port in v1.2 if len(ds) == 0 { - // Validated here instead of by a CRD CEL rule so that a partial provider + // Checked here instead of by a CRD CEL rule so that a partial provider // (e.g. only serviceName) can be completed by the GatewayClass-level and - // Gateway-level EnvoyProxy merge before the check runs. + // Gateway-level EnvoyProxy merge before the check runs. An incomplete + // provider only turns tracing off, it does not stop the Gateway from + // being provisioned. if tracing.Provider.Host == nil { - return nil, fmt.Errorf("host or backendRefs needs to be set on the tracing provider after merging EnvoyProxy configs") + t.Logger.Info("Disabling tracing because the merged tracing provider sets neither host nor backendRefs", + "gateway", utils.NamespacedName(gwCtx.Gateway).String(), + "envoyProxy", utils.NamespacedName(envoyproxy).String()) + return nil, nil } host, port := *tracing.Provider.Host, uint32(tracing.Provider.Port) ds = destinationSettingFromHostAndPort(settingName, host, port) diff --git a/internal/gatewayapi/listener_test.go b/internal/gatewayapi/listener_test.go index 279e70b94e..451a45fcd3 100644 --- a/internal/gatewayapi/listener_test.go +++ b/internal/gatewayapi/listener_test.go @@ -762,7 +762,6 @@ func TestProcessTracingServiceName(t *testing.T) { envoyProxy *egv1a1.EnvoyProxy mergeGateways bool expectedServiceName string - expectError bool }{ { name: "no tracing configuration", @@ -935,7 +934,7 @@ func TestProcessTracingServiceName(t *testing.T) { expectedServiceName: "test-gateway-class", // Should use gateway class name when merging }, { - name: "tracing provider without backendRefs or host", + name: "tracing provider without backendRefs or host disables tracing", gateway: &gwapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gateway", @@ -958,7 +957,8 @@ func TestProcessTracingServiceName(t *testing.T) { }, }, }, - expectError: true, + // An empty expectedServiceName asserts that no tracing config is built. + expectedServiceName: "", }, } @@ -1023,11 +1023,6 @@ func TestProcessTracingServiceName(t *testing.T) { Gateway: tc.gateway, }, tc.envoyProxy, tc.mergeGateways, resources) - if tc.expectError { - assert.Error(t, err) - return - } - require.NoError(t, err) if tc.expectedServiceName == "" { diff --git a/release-notes/current/bug_fixes/9527-tracing-cel-merge.md b/release-notes/current/bug_fixes/9527-tracing-cel-merge.md index bcce3b24d4..680cd1374e 100644 --- a/release-notes/current/bug_fixes/9527-tracing-cel-merge.md +++ b/release-notes/current/bug_fixes/9527-tracing-cel-merge.md @@ -1 +1 @@ -Fixed CRD validation rejecting a per-Gateway EnvoyProxy that overrides only part of the tracing provider (e.g. `serviceName`) when relying on `mergeType` to inherit the rest: the host/backendRefs completeness check now runs after the GatewayClass-level and Gateway-level configs are merged, surfacing an `InvalidParameters` Gateway condition instead of blocking admission. +Fixed CRD validation rejecting a per-Gateway EnvoyProxy that overrides only part of the tracing provider (e.g. `serviceName`) when relying on `mergeType` to inherit the rest. The host/backendRefs completeness check now runs after the GatewayClass-level and Gateway-level configs are merged, and a provider that is still incomplete turns tracing off with a log message instead of blocking admission or the Gateway. diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index fd6da0a127..a29cfe7295 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -6527,8 +6527,9 @@ TracingProvider defines the tracing provider configuration. A provider is only required to set host or backendRefs after the GatewayClass-level and Gateway-level EnvoyProxy configs are merged -(see EnvoyProxySpec.MergeType), so completeness is validated during -translation instead of by a CEL rule here. +(see EnvoyProxySpec.MergeType), so completeness is checked during +translation instead of by a CEL rule here. A provider that is still +incomplete after the merge turns tracing off for that Gateway. _Appears in:_ - [ProxyTracing](#proxytracing)