feat: add EnvoyProxy.spec.runtime served over RTDS - #9671
Conversation
✅ Deploy Preview for cerulean-figolla-1f9435 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
6dc509f to
dbf0ee3
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6dc509fa14
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| rtds_config: | ||
| ads: {} | ||
| resource_api_version: V3 |
There was a problem hiding this comment.
Keep RTDS from timing out during bootstrap
When the initial RTDS response is delayed, this new rtds_config inherits Envoy's 15s initial_fetch_timeout default (ConfigSource docs), unlike the LDS/CDS config below that explicitly waits indefinitely. In that startup or controller-restart scenario Envoy can initialize this layer without the configured runtime values and run with default circuit-breaker/runtime behavior until a later RTDS update arrives, undermining the no-restart runtime path; please set initial_fetch_timeout: 0s here as well.
Useful? React with 👍 / 👎.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #9671 +/- ##
==========================================
- Coverage 76.19% 76.18% -0.01%
==========================================
Files 261 262 +1
Lines 43528 43563 +35
==========================================
+ Hits 33164 33189 +25
- Misses 8161 8169 +8
- Partials 2203 2205 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
dbf0ee3 to
db92be8
Compare
Signed-off-by: Anurag Aggarwal <kanurag94@gmail.com>
db92be8 to
6b92d4a
Compare
What this PR does / why we need it:
Adds
EnvoyProxy.spec.runtime, a map of Envoy runtime key to value, served to proxies over RTDS.Today there is no API for Envoy runtime values. The only path to
layered_runtimeisspec.bootstrap, and the bootstrap is read at startup, so every change restarts the proxy. The default bootstrap has one static layer (internal/xds/bootstrap/bootstrap.yaml.tpl:50) and no RTDS layer.The RTDS server is already wired but unreachable.
internal/xds/runner/runner.go:266registers it:grep -rn RuntimeType internal/onmainreturns nothing — no code ever emits a Runtime resource, so that registration is dead.What a user cannot do: tune circuit breakers on a cluster Envoy Gateway did not build from its own configuration.
BackendTrafficPolicycircuit breakers reach only IR-derived clusters, applied atinternal/xds/translator/cluster.go:495. A cluster an extension server appends inPostTranslateModifyis not IR-derived, so it keeps Envoy's defaults,upstream_impl.cc:2199-2202:ext_proc opens one gRPC stream per HTTP request, so
max_requests: 1024caps a proxy at 1024 concurrent requests through ext_proc.Editing the cluster to raise it is itself disruptive. A cluster update increments
cluster_manager.cluster_modifiedand runsthread_local_clusters_[info->name()].reset(new_cluster)(cluster_manager_impl.cc:1325). That destroys theClusterEntry, which ownslazy_http_async_client_(cluster_manager_impl.h:716), and~AsyncClientImplresets every active stream (async_client_impl.cc:38-42):ext_proc streams use the async client, not the connection pool, so
drainConnPools()in theClusterEntrydestructor does not cover them.Envoy reads the limit from runtime on every admission check,
basic_resource_impl.h:So a runtime key applies with no
circuit_breakersblock on the cluster, and changing it modifies no cluster and resets no streams.Example
Before — the default bootstrap on
mainhas one static layer and no RTDS layer, so nothing canreach runtime on a running proxy:
After —
spec.runtimeon theEnvoyProxy:The translator emits one Runtime resource. Golden output,
internal/xds/translator/testdata/out/xds-ir/runtime.runtime.yaml:Envoy resolves the key with no
circuit_breakersblock on the cluster. Run againstenvoyproxy/envoy:distroless-devwith a static config whoseslow_clustercarries nocircuit_breakersblock, and a static layer setting the key to 1. Reading/runtimeandfiltering to the circuit breaker entries:
final_valueresolves from the static layer with nothing on the cluster, and the empty secondlayer_valuesentry is the admin layer, which would override it.Values keep their JSON type:
Which issue(s) this PR fixes:
Fixes #9670
Related issues/PRs
rtds_layernamedruntime-0from the default bootstrap, and also dropped thelayered_runtime cannot be modifiedguard.internal/xds/bootstrap/validate.gonow protects onlydynamic_resources(:68) andxds_cluster.loadAssignment(:87).gRPC config: initial fetch timed out for type.googleapis.com/envoy.service.runtime.v3.Runtime. This PR emits the resource unconditionally, including whenspec.runtimeis empty, so that does not come back.JSONPatch.bootstrap.type: Mergeappends tolayersinstead of merging, producing twoglobal_configlayers.JSONPatchwas added as a result.BackendTrafficPolicy. That path is unchanged here; this reaches clusters that path cannot.No prior PR has served RTDS from Envoy Gateway.
Notes for reviewers
Values are
apiextensionsv1.JSON, not strings (api/v1alpha1/envoyproxy_types.go:95). Envoy'screateEntrysends a string"true"throughparseEntryDoubleValue, which fails, leavingbool_value_unset, sogetBooleanreturns the default. Amap[string]stringwould silently disable every boolean runtime guard. Cost isx-kubernetes-preserve-unknown-fields: trueon the map values. Happy to switch tomap[string]stringif you would rather not have freeform JSON, but boolean keys stop working.The layer is
envoy-gateway-runtime, notruntime-0(internal/xds/translator/runtime.go:27). Envoy refuses to start on duplicate layer names.runtime-0was Envoy Gateway's own name before bootstrap: clean layered runtime #2051, so it can still be sitting in a user'sspec.bootstrap, and reusing it would crashloop them. Verified against the pinned image:Bootstrap validation now rejects duplicate runtime layer names. Without it that collision only shows up as a crashloop, since it is an Envoy startup check and not a PGV constraint, so
patchedBootstrap.Validate()does not catch it.testdata/merge/default.in.yamlalready contained anrtds_layernamedruntime-0. That fixture is what surfaced the collision. It is unchanged, andmerge/default.out.yamlnow shows a user layer coexisting with Envoy Gateway's.The layer is listed after
global_config, so runtime values win.SnapshotImpl::SnapshotImpliterates layers in order doingvalues_.erase(kv.first)thenemplace, so later layers replace earlier ones.An unparseable value fails translation, which holds the whole snapshot (
runner.go:335logsskipped publishing xds resources). The API server only admits valid JSON here so this is close to unreachable, and a runtime key that silently fails to apply is worse — a dropped circuit breaker limit leaves the proxy on Envoy's default with nothing to show it. Happy to make it non-fatal likeprocessJSONPatchesif you prefer partial application.spec.bootstrapwithtype: Replacethat omits the RTDS layer leavesspec.runtimewith nowhere to land. Documented on the field and in the task docs, not detected. Happy to add anEnvoyProxystatus condition in this PR if you want it surfaced.Adding the layer changes proxy container arguments, so upgrading rolls managed proxies once. Called out in
release-notes/current/other_changes/9670-bootstrap-rtds-layer.md.spec.extraArgscannot be used for this.internal/infrastructure/common/proxy_args.go:71already passes the whole bootstrap as--config-yaml, andExtraArgsis appended at :98, so a second one is rejected:egctl x translatecannot show these values. Envoy's config dump has no runtime section — the messages are Bootstrap, Secrets, Listeners, Clusters, Routes, ScopedRoutes, Endpoints, Ecds. It shows the RTDS layer in the bootstrap only. The task docs point at the proxy's/runtimeendpoint instead.Existing xDS golden files did not change.
TestTranslateXdscompares listeners, routes, clusters, endpoints and secrets, so the new type does not touch them. The.runtime.yamlassertion is gated onlen(x.Runtime) > 0to avoid adding a golden file to every case. The 83 regenerated files are all the bootstrap layer.xdsWithoutEqualininternal/gatewayapi/translator_test.goneeded the new field. It panics on a field missing from itsir.Xdsmirror, which is deliberate.No e2e test for live RTDS delivery. That needs a cluster. Everything below it is covered; the delivery path itself is not. Can add one in a follow-up.
Verification
Reviewed against the Envoy this repo pins,
envoyproxy/envoy:distroless-dev,1.40.0-dev/6dc58a7029680bb971116fde197d51e5439c776f:circuit_breakers.{cluster}.{priority}.built inClusterInfoImpl::ResourceManagers::load, key suffixes inresource_manager_impl.h, priority namesdefaultandhigh.--mode validategets pastlayered_runtimeand all 3 clusters, failing only on SDS paths absent from the container:paths must refer to an existing path in the system: '/sds/xds-certificate.json' does not exist.Ran:
go build ./...go test ./...— all packages passmake lint—0 issues.make generate manifests helm-template protos go.testdata.completetwice — second run produces no diffEach new test fails without the change:
processRuntimecall fromtranslator.go→TestTranslateXds/runtime:Expectedhas the layer,Actualis+[].validate.go→TestValidateBootstrap/user_bootstrap_with_duplicate_runtime_layer_names:An error is expected but got nil.xdsIR[irKey].Runtime = ...fromgatewayapi/translator.go→TestTranslate/envoyproxy-runtimediffs onRuntime map[string]v1.JSON.bootstrap.yaml.tpl→TestGetRenderedBootstrapConfigdiffs on the six missingenvoy-gateway-runtimelines.PR Checklist
git commit -s).EnvoyProxy.spec.runtimeto configure Envoy runtime values, served over RTDS #9670 was filed ahead of the implementation, but the API has not been agreed yet — it is still labelledtriage. Happy to splitapi/v1alpha1/envoyproxy_types.gointo its own PR and hold this one behind it.go build ./...,go test ./..., andmake lint(0 issues.) pass locally.gen-checkrequires a clean tree so it cannot run against uncommitted work; verified instead by runningmake generate manifests helm-template protos go.testdata.completetwice and confirming the second run produces no diff. CI is the authority.runtimexDS translator case with golden output, anenvoyproxy-runtimegatewayapi case for theEnvoyProxyto IR plumbing, and a bootstrap validation case for the duplicate layer name. Each verified to fail without the change (see Verification).site/content/en/latest/tasks/operations/customize-envoyproxy.md, plus generatedapi/extension_types.md.release-notes/current/new_features/9670-envoyproxy-spec-runtime.mdandrelease-notes/current/other_changes/9670-bootstrap-rtds-layer.md.spec.runtimeis set. The one upgrade effect is a single proxy rollout from the bootstrap change, noted inother_changes.