Repository navigation
Conversation
…e URI Update the Actuator OIDC token audience and ESS actuator target URL in the Helm chart ConfigMaps to use the in-cluster Kubernetes service URI (https://actuator.xmanager.xmc.svc.cluster.local/actuate) instead of constructing an external actuator.api.<env>.<domain> hostname. Also remove the unused legacy Ingress BackendConfig template (deploy/helm/templates/ess/grpc-backend-config.yaml) and the dead xmc_iap_secret Kubernetes Secret module from the CTK v2 blueprint, as GKE Gateway API deployments do not consume v1 Ingress BackendConfig resources. TAG=agy CONV=c5f694f5-b286-42df-821a-8d5c1a9350de
There was a problem hiding this comment.
Code Review
This pull request removes legacy GKE Ingress BackendConfig resources and updates the actuator and ESS configmaps to use a cluster-local actuator URI instead of an external one. However, the proposed cluster-local URI (https://actuator.xmanager.xmc.svc.cluster.local/actuate) contains an extra .xmanager label, which violates standard Kubernetes DNS formatting (<service-name>.<namespace>.svc.cluster.local) and will lead to DNS resolution failures. Please update the URI to https://actuator.xmc.svc.cluster.local/actuate across the configmaps and test files.
Use https://actuator.xmc.svc.cluster.local/actuate so the hostname matches standard Kubernetes service DNS format (<service>.<namespace>.svc.cluster.local) within the xmc namespace. Comment link: #1546 (comment) Comment link: #1546 (comment) Comment link: #1546 (comment) TAG=agy CONV=c5f694f5-b286-42df-821a-8d5c1a9350de
TAG=agy CONV=c5f694f5-b286-42df-821a-8d5c1a9350de
- Extracted `xmanager-cloud.actuatorAudience` into `deploy/helm/templates/_helpers.tpl` and referenced it from `actuator-config` and `ess-config` (#1546 (comment)) - Documented the `xmc-tasks-stream` Redis drain requirement for upgrading from the legacy audience in `_helpers.tpl` and the PR assumptions (#1546 (comment)) - Moved `test_chart_omits_legacy_ingress_backend_config` to `test/helm/httproute_config_test.py` and updated `test/helm/actuator_dev_oidc_config_test.py` to parse and assert equality of the rendered `audience` and `ess_actuator_url` values (#1546 (comment)) TAG=agy CONV=c5f694f5-b286-42df-821a-8d5c1a9350de
| {{/* | ||
| Logical JWT/OIDC audience shared by the ESS task signer and the actuator validator. | ||
| Never dialed; must be identical on both sides. Upgrading from the legacy | ||
| https://actuator.api.<env>.<domain>/actuate value changes the JWT aud contract | ||
| while ess and actuator Deployments roll independently; quiesce launches until the | ||
| xmc-tasks-stream Redis backlog is 0 before running helm upgrade. | ||
| */}} |
There was a problem hiding this comment.
[Priority: Actionable] deploy/helm/templates/_helpers.tpl:122-127
Observation: Thanks for documenting the drain step. Looking closer, the wording from my earlier suggestion turns out to be too loose to prevent task loss in practice:
- More than launches enqueue signed tasks. Stop/delete (
batchstopworkunits.go:148), restart (batchrestartworkunits.go:182), update (updateworkunit.go:101) and the ESS resumption worker (resumption/worker.go:335) all do. A global scheduling pause doesn't help either: signature validation (workunit_task_handler.go:265) runs before the pause check (:214), and DELETE is never paused (pause_handler.go:102). - The window is the rollout itself, not just the backlog before it. The actuator (
replicas: 1, default RollingUpdate) keeps its old pod until the new one is Ready, while ESS rolls separately. So new ESS pods can sign with the newaudfor the old actuator, and old ESS pods with the oldaudfor the new actuator. Producers must stay stopped until both rollouts finish, not only until the backlog drains. - "Backlog is 0" can't be checked with
XLEN. The stream is only trimmed by approximateMAXLEN ~10000(publisher.go:49-50), and XACK doesn't delete entries, soXLENnever reaches 0. The real signal isXINFO GROUPS xmc-tasks-stream→actuator-groupwithpending=0andlag=0(the same thingbacklog.go:42-81computes).
Risk / Rule: An operator following this comment can still send tasks to xmc-dlq-stream, where nothing replays them and their Operation is never marked done. Operators also don't read _helpers.tpl: the upgrade paths (INSTALL.md "Upgrading an Existing Installation" Step 4, dev_env.py gke-upgrade) never mention the drain. Documentation Alignment (AGENTS.md principle 5); accurate operational contracts (architectural_contracts.md).
Suggestion: Keep only the lasting rule in this comment, and move the one-time procedure into INSTALL.md Step 4 (or a docs/migrations/ runbook, like Step 6). For example: scale experiment-state-server-deployment to 0 → wait for actuator-group pending=0/lag=0 → helm upgrade → kubectl rollout status for the actuator → scale ESS back. The alternative is to have the actuator accept both audiences for one release.
| {{/* | |
| Logical JWT/OIDC audience shared by the ESS task signer and the actuator validator. | |
| Never dialed; must be identical on both sides. Upgrading from the legacy | |
| https://actuator.api.<env>.<domain>/actuate value changes the JWT aud contract | |
| while ess and actuator Deployments roll independently; quiesce launches until the | |
| xmc-tasks-stream Redis backlog is 0 before running helm upgrade. | |
| */}} | |
| {{/* | |
| Logical JWT/OIDC audience shared by the ESS task signer (ess_actuator_url) and the actuator | |
| validator (audience). Never dialed; must be identical on both sides. See INSTALL.md | |
| "Upgrading an Existing Installation" for the one-time cutover from the legacy | |
| https://actuator.api.<env>.<domain>/actuate audience. | |
| */}} |
Summary
Replaces the legacy external
https://actuator.api.<env>.<domain>/actuateURI in the Helm ConfigMaps with a sharedxmanager-cloud.actuatorAudiencehelper (https://actuator.xmc.svc.cluster.local/actuate) and removes the unused legacy IngressBackendConfig(grpc-backend-config.yaml) andxmc_iap_secretblueprint module.Motivation & Context
Following the migration from Cloud Tasks HTTP push to in-cluster Redis Streams (
use_redis_queue: true) and from legacy GKE v1 Ingress to the GKE Gateway API (HTTPRoute), the Actuator service only runs on a cluster-localClusterIP(actuator-serviceon HTTP:8080) and no longer exposes/actuateexternally. However, the Actuator (audience) and ESS (ess_actuator_url) Helm ConfigMaps still constructed an externalhttps://actuator.api.<env>.<domain>/actuateJWT/OIDC audience identifier, and the Helm chart and Cluster Toolkit v2 blueprint still provisioned an unused v1 IngressBackendConfig(shared-backend-config) and dummyxmc-iap-secretthat GKE Gateway API ignores.Key Changes
xmanager-cloud.actuatorAudienceonce indeploy/helm/templates/_helpers.tpland referenced it in bothdeploy/helm/templates/actuator/configmap.yaml(audience) anddeploy/helm/templates/ess/configmap.yaml(ess_actuator_url).deploy/helm/templates/ess/grpc-backend-config.yamland its entry intest/e2e/lib/helm_deployer.py.xmc_iap_secretmodule fromdeploy/ctk_v2/xmanager-cloud.yaml.test/helm/actuator_dev_oidc_config_test.pyto parse and assert equality ofactuator-configaudienceandess-configess_actuator_urlagainsthttps://actuator.xmc.svc.cluster.local/actuate, and addedtest_chart_omits_legacy_ingress_backend_configtotest/helm/httproute_config_test.py.Assumptions
https://actuator.xmc.svc.cluster.local/actuatesatisfies all active OIDC token and per-task Tink JWT signature validation paths — verified againstinternal/task_queue/client.go:304-307andcmd/actuator/workunittaskhandler/workunit_task_handler.go:346,365where ESS signs tasks withaud = ess_actuator_urland Actuator validates againstaudience— if either side drifted from the sharedxmanager-cloud.actuatorAudiencetemplate helper, signature validation would fail for every task.xmc-tasks-streamRedis stream (quiescing work unit launches until the stream backlog is0) before running the one-timehelm upgradefrom the legacyhttps://actuator.api.<env>.<domain>/actuateaudience toxmanager-cloud.actuatorAudience— verified againstcmd/actuator/workunittaskhandler/workunit_task_handler.go:265-266,365andinternal/kubernetes/common/redis/consumer.go:234-237, whereactuatorandessDeployments roll independently on their ownchecksum/configand Actuator treats JWT signature/audience mismatches as non-retryable (moving messages toxmc-dlq-streamwithout marking the SpannerOperationdone) — if tasks signed with the old audience are backlogged or enqueued by old ESS pods while new Actuator pods consume during rollout, those tasks will land inxmc-dlq-stream.BackendConfig/shared-backend-configorSecret/xmc-iap-secret— verified by searching alldeploy/helm/templates/**anddeploy/ctk_v2/**manifests, wherecloud.google.com/backend-configannotations are absent and GKE Gateway API routes (HTTPRoute) do not read v1 IngressBackendConfigCRDs — if an out-of-tree v1 Ingress manifest referencedshared-backend-config, it would fail to find theBackendConfig.Verification & Testing
bazel test --test_env=PATH --test_env=XMC_REQUIRE_HELM=1 //test/helm:actuator_dev_oidc_config_test //test/helm:httproute_config_test./.githooks/pre-commit(includingpython3 ./tools/run_affected_tests.py --skip-emulator-check)test/helmand all 23 affected Bazel test targets (//test/e2e/...) passed (0Trivy misconfigurations,0lint issues).Related Issues / Bugs
N/A
TAG=agy
CONV=c5f694f5-b286-42df-821a-8d5c1a9350de