From a21f0209bb6e5512eeba8e9804623e38e7f2e345 Mon Sep 17 00:00:00 2001 From: Soumik Ghosh Date: Mon, 10 Aug 2026 15:29:08 +0530 Subject: [PATCH] Fix gatewayAPI CTP schema validation fail --- CHANGELOG.md | 5 +++ ci/test-chart/ci-values/gateway-policies.yaml | 2 +- .../gateway_clienttrafficpolicy_test.yaml | 6 +-- docs/GATEWAYAPI.md | 4 +- src/common/Chart.yaml | 2 +- .../_gateway_clienttrafficpolicy.tpl | 42 ++++++++++--------- src/common/values.yaml | 4 +- 7 files changed, 36 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf43b14..8f3496b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [1.9.4] - 2026-08-10 + +### Fixed +- **`ClientTrafficPolicy` empty-block and invalid-field schema errors**: `_gateway_clienttrafficpolicy.tpl` had two remaining CRD schema mismatches. First, `connection`/`timeout` blocks were gated on parent dict existence rather than leaf field values, so charts using the library's own empty-string defaults (`values.yaml` `clientTraffic.connection`/`timeout`) rendered `connection:`/`timeout:` keys with nothing under them — `null`, rejected by the CRD (`must be of type object: "null"`). All leaf fields now resolved via `dig` and the parent block only renders when a leaf has a real value, which also makes the template safe when `connection`/`timeout`/`http2`/`path` are omitted entirely. Second, `connection.connectionIdleTimeout` was not a field the ClientTrafficPolicy CRD recognizes under `connection` — Envoy Gateway's actual field is `timeout.http.idleTimeout`. **Breaking**: the values key moved and was renamed to match — `clientTraffic.connection.connectionIdleTimeout` is now `clientTraffic.timeout.http.idleTimeout`. + ## [1.9.3] - 2026-08-08 ### Fixed diff --git a/ci/test-chart/ci-values/gateway-policies.yaml b/ci/test-chart/ci-values/gateway-policies.yaml index a7b5a38..affda2f 100644 --- a/ci/test-chart/ci-values/gateway-policies.yaml +++ b/ci/test-chart/ci-values/gateway-policies.yaml @@ -36,9 +36,9 @@ global: enabled: true connection: bufferLimit: "100Mi" - connectionIdleTimeout: "300s" timeout: http: + idleTimeout: "300s" requestReceivedTimeout: "60s" http2: maxConcurrentStreams: 1000 diff --git a/ci/test-chart/tests/gateway_clienttrafficpolicy_test.yaml b/ci/test-chart/tests/gateway_clienttrafficpolicy_test.yaml index 31839aa..22bfea0 100644 --- a/ci/test-chart/tests/gateway_clienttrafficpolicy_test.yaml +++ b/ci/test-chart/tests/gateway_clienttrafficpolicy_test.yaml @@ -42,15 +42,15 @@ tests: - equal: path: spec.connection.bufferLimit value: 100Mi - - equal: - path: spec.connection.connectionIdleTimeout - value: 300s - it: ClientTrafficPolicy should have correct timeout settings documentSelector: path: kind value: ClientTrafficPolicy asserts: + - equal: + path: spec.timeout.http.idleTimeout + value: 300s - equal: path: spec.timeout.http.requestReceivedTimeout value: 60s diff --git a/docs/GATEWAYAPI.md b/docs/GATEWAYAPI.md index d750aa1..dfa1666 100644 --- a/docs/GATEWAYAPI.md +++ b/docs/GATEWAYAPI.md @@ -866,9 +866,9 @@ global: enabled: true connection: bufferLimit: "100Mi" # 100MB max request body - connectionIdleTimeout: "300s" timeout: http: + idleTimeout: "300s" requestReceivedTimeout: "60s" http2: maxConcurrentStreams: 1000 @@ -1060,7 +1060,7 @@ global: |-----------|------|---------|-------------| | `enabled` | bool | `false` | Enable ClientTrafficPolicy (attaches to Gateway) | | `connection.bufferLimit` | string | `""` | Client request buffer limit (e.g., "100Mi") - equivalent to nginx proxy-body-size | -| `connection.connectionIdleTimeout` | string | `""` | Client connection idle timeout (e.g., "300s") | +| `timeout.http.idleTimeout` | string | `""` | Client connection idle timeout (e.g., "300s") | | `timeout.http.requestReceivedTimeout` | string | `""` | Request received timeout (e.g., "60s") - equivalent to nginx client_body_timeout | | `http2.maxConcurrentStreams` | int | `0` | Max concurrent HTTP/2 streams | | `path.disableMergeSlashes` | bool | `false` | Preserve consecutive slashes in request paths (envoy merges them by default) | diff --git a/src/common/Chart.yaml b/src/common/Chart.yaml index 32f21e2..6ccc9d4 100644 --- a/src/common/Chart.yaml +++ b/src/common/Chart.yaml @@ -15,7 +15,7 @@ type: library # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.9.3 +version: 1.9.4 # This is the version number of the application being deployed. This version number should be diff --git a/src/common/templates/_gateway_clienttrafficpolicy.tpl b/src/common/templates/_gateway_clienttrafficpolicy.tpl index ea77c00..de504db 100644 --- a/src/common/templates/_gateway_clienttrafficpolicy.tpl +++ b/src/common/templates/_gateway_clienttrafficpolicy.tpl @@ -33,37 +33,39 @@ spec: - group: gateway.networking.k8s.io kind: Gateway name: {{ include "harnesscommon.tplvalues.render" ( dict "value" $parentRef.name "context" $) }} - {{- if or $clientPolicy.connection $clientPolicy.timeout $clientPolicy.http2 $clientPolicy.path }} - {{- if $clientPolicy.path }} + {{- $pathDisableMergeSlashes := dig "path" "disableMergeSlashes" "" $clientPolicy }} + {{- $pathEscapedSlashesAction := dig "path" "escapedSlashesAction" "" $clientPolicy }} + {{- $connectionBufferLimit := dig "connection" "bufferLimit" "" $clientPolicy }} + {{- $timeoutIdleTimeout := dig "timeout" "http" "idleTimeout" "" $clientPolicy }} + {{- $timeoutRequestReceivedTimeout := dig "timeout" "http" "requestReceivedTimeout" "" $clientPolicy }} + {{- $http2MaxConcurrentStreams := dig "http2" "maxConcurrentStreams" 0 $clientPolicy | int }} + {{- if or $pathDisableMergeSlashes $pathEscapedSlashesAction $connectionBufferLimit $timeoutIdleTimeout $timeoutRequestReceivedTimeout (gt $http2MaxConcurrentStreams 0) }} + {{- if or $pathDisableMergeSlashes $pathEscapedSlashesAction }} path: - {{- if $clientPolicy.path.disableMergeSlashes }} - disableMergeSlashes: {{ $clientPolicy.path.disableMergeSlashes }} + {{- if $pathDisableMergeSlashes }} + disableMergeSlashes: {{ $pathDisableMergeSlashes }} {{- end }} - {{- if $clientPolicy.path.escapedSlashesAction }} - escapedSlashesAction: {{ $clientPolicy.path.escapedSlashesAction }} + {{- if $pathEscapedSlashesAction }} + escapedSlashesAction: {{ $pathEscapedSlashesAction }} {{- end }} {{- end }} - {{- if $clientPolicy.connection }} + {{- if $connectionBufferLimit }} connection: - {{- if $clientPolicy.connection.bufferLimit }} - bufferLimit: {{ $clientPolicy.connection.bufferLimit }} - {{- end }} - {{- if $clientPolicy.connection.connectionIdleTimeout }} - connectionIdleTimeout: {{ $clientPolicy.connection.connectionIdleTimeout }} - {{- end }} + bufferLimit: {{ $connectionBufferLimit }} {{- end }} - {{- if $clientPolicy.timeout }} + {{- if or $timeoutIdleTimeout $timeoutRequestReceivedTimeout }} timeout: - {{- if $clientPolicy.timeout.http }} http: - {{- if $clientPolicy.timeout.http.requestReceivedTimeout }} - requestReceivedTimeout: {{ $clientPolicy.timeout.http.requestReceivedTimeout }} + {{- if $timeoutIdleTimeout }} + idleTimeout: {{ $timeoutIdleTimeout }} + {{- end }} + {{- if $timeoutRequestReceivedTimeout }} + requestReceivedTimeout: {{ $timeoutRequestReceivedTimeout }} {{- end }} - {{- end }} {{- end }} - {{- if and $clientPolicy.http2 $clientPolicy.http2.maxConcurrentStreams (gt ($clientPolicy.http2.maxConcurrentStreams | int) 0) }} + {{- if gt $http2MaxConcurrentStreams 0 }} http2: - maxConcurrentStreams: {{ $clientPolicy.http2.maxConcurrentStreams | int }} + maxConcurrentStreams: {{ $http2MaxConcurrentStreams }} {{- end }} {{- end }} {{- end }} {{/* if parentRef.name */}} diff --git a/src/common/values.yaml b/src/common/values.yaml index 45c9b7f..bcb39c1 100644 --- a/src/common/values.yaml +++ b/src/common/values.yaml @@ -129,11 +129,11 @@ global: connection: # e.g., "1024Mi" - equivalent to nginx proxy-body-size bufferLimit: "" - # e.g., "3600s" - connectionIdleTimeout: "" # Client-side timeouts timeout: http: + # e.g., "3600s" + idleTimeout: "" # e.g., "3600s" - equivalent to nginx client_body_timeout requestReceivedTimeout: "" # HTTP/2 settings