Repository navigation
fix/values: Sync documented vs effective Helm values - #1018
Draft
marcleblanc2 wants to merge 14 commits into
Draft
marcleblanc2 wants to merge 14 commits into
marcleblanc2 wants to merge 14 commits into
Conversation
All four charts documented sourcegraph.nameOverride, but sourcegraph.name reads the top-level nameOverride, so the documented key did nothing. Document the top-level key, which is the Helm convention and what a parent chart sets for a subchart, and remove the unused one. Renders do not change. The subchart example aliases the chart as sg, which replaces the chart name in the app.kubernetes.io/name selector label. It now sets sg.nameOverride: sourcegraph to keep the selectors of a standalone install. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d360-e0b4-71d1-8ded-2e4851b93a8d
The chart documented storageClass.create, provisioner, type, parameters, and allowedTopologies, copied from the sourcegraph chart, but it has no StorageClass template, so they never had an effect. Keep storageClass.name, which the private-docker-registry volume uses. Renders do not change. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d360-e0b4-71d1-8ded-2e4851b93a8d Co-authored-by: Amp <amp@ampcode.com>
sourcegraph-executor-dind documented executor.maximumNumJobs but never rendered it, and refused EXECUTOR_MAXIMUM_NUM_JOBS in executor.env, so executors always ran the executor's default of 1 job. Render it, with a default of 1 to keep that behavior. Allow EXECUTOR_DOCKER_ADD_HOST_GATEWAY and EXECUTOR_KEEP_WORKSPACES in env, since they have no value. The dind and migrator READMEs listed per-component nodeSelector, affinity, tolerations, and podSecurityContext values that no template read, and the k8s chart documented global ones it never read. Each pod now uses its own value, then the global sourcegraph one, and values.yaml documents them. The migrator's values.yaml lists the values its Job reads instead of a generic block copied from the sourcegraph chart. Remove the k8s chart's unread executor.securityContext.privileged and unused scheduling helpers. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d360-e0b4-71d1-8ded-2e4851b93a8d Co-authored-by: Amp <amp@ampcode.com>
Document storageClass.allowVolumeExpansion, reclaimPolicy, and volumeBindingMode, frontend.serviceType and serviceAnnotations, openTelemetry.gateway.env, and redisExporter.env, which templates read but values.yaml did not list, with the defaults the templates already used. allowVolumeExpansion could not be set to false; render the value directly. Say when traces.processors applies, and fix the value path in the exporters error. Fix example override files that set values no template reads: basic, custom-security-context, openshift, and opentelemetry-exporter, which also used the removed jaeger exporter and bare $VAR expansion. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d360-e0b4-71d1-8ded-2e4851b93a8d Co-authored-by: Amp <amp@ampcode.com>
Use the default the chart documented, and the k8s executor chart uses. Executors that do not set it go from 1 job at a time to 10 on upgrade. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d360-e0b4-71d1-8ded-2e4851b93a8d Co-authored-by: Amp <amp@ampcode.com>
…lls only A release already installed with the alias has the alias in its selectors. Adding nameOverride to it fails the upgrade, because Kubernetes does not allow changing a selector.
…v rules Covers the paths the review of #1018 found untested: - nodeSelector, affinity, and tolerations in the migrator, k8s executor, dind executor, and private-docker-registry: the workload's own value replaces the sourcegraph.* one, and sourcegraph.* is used when it is empty. The two sides use different keys, so a merge fails the tests. - The migrator Job has no pod securityContext unless migrator.podSecurityContext is set. - A single dind executor (no queues) gets the default EXECUTOR_MAXIMUM_NUM_JOBS of 10. - executor.env and a queue's env accept EXECUTOR_DOCKER_ADD_HOST_GATEWAY and EXECUTOR_KEEP_WORKSPACES, and still refuse EXECUTOR_MAXIMUM_NUM_JOBS. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d404-b1a7-712c-8b3c-b729976e9f0c Co-authored-by: Amp <amp@ampcode.com>
A null or empty executor.maximumNumJobs rendered an empty EXECUTOR_MAXIMUM_NUM_JOBS, and the executor exited with `invalid int ""` in a crash loop. `helm upgrade --reuse-values` from a chart older than 7.4.0, which has no such value, gets there without anyone writing null. The chart now fails the render with a message instead, so the upgrade stops before it changes the running executor. 0 still renders, since it pauses the executor. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d404-b1a7-712c-8b3c-b729976e9f0c Co-authored-by: Amp <amp@ampcode.com>
The chart ignored queues[].maximumNumJobs and refuses EXECUTOR_MAXIMUM_NUM_JOBS in a queue's env, so every queue got executor.maximumNumJobs. A queue's own value now wins; a queue that sets none, null, or an empty string uses executor.maximumNumJobs. 0 is kept, because it pauses that queue's executor. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d404-b1a7-712c-8b3c-b729976e9f0c Co-authored-by: Amp <amp@ampcode.com>
The docs said every selector uses app.kubernetes.io/name. In the sourcegraph chart the grafana StatefulSet and jaeger selectors do not, and none do with sourcegraph.selectorLabelsOverride. In the dind chart the private-docker-registry Service does not. The migrator chart has no selector, so its nameOverride can change on an installed release.
…lues docs Amp-Thread-ID: https://ampcode.com/threads/T-01a1d404-b1a7-712c-8b3c-b729976e9f0c Co-authored-by: Amp <amp@ampcode.com>
…tor pod, not job pods The docs called sourcegraph.nodeSelector, affinity, and tolerations the default for every pod. The chart renders one pod, the executor. The executor creates job pods itself, from executor.kubernetesJob.node and executor.kubernetesJob.pod, and they take none of these values. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d404-b1a7-712c-8b3c-b729976e9f0c Co-authored-by: Amp <amp@ampcode.com>
…og heading Amp-Thread-ID: https://ampcode.com/threads/T-01a1d404-b1a7-712c-8b3c-b729976e9f0c Co-authored-by: Amp <amp@ampcode.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Open issues
Found in review on 2026-11-13 (review thread). This PR stays a draft until the first two are settled.
storageClass.allowVolumeExpansionrenders empty when the key is missing or null.helm upgrade --reuse-valuesfrom a release made by an older chart rendersallowVolumeExpansion:with no value, wheremainrenderstrue. Applying that render to an existing StorageClass on EKS changed it fromtruetofalse. Tested fix:allowVolumeExpansion: {{ ne (toString .Values.storageClass.allowVolumeExpansion) "false" }}, plus a test for the null casedindcontainer, which has no resources value;executor.resourcesonly reaches theexecutorcontainer. Pick one: defaultexecutor.maximumNumJobsto 1 (no change for existing releases), or keep 10, mark the changelog entry**BREAKING:**, and correct the advice here and invalues.yaml. The load test under 10 concurrent dind jobs under load shows what 10 costs: one over-sized batch of jobs takes all 10 down, and no pod resources value can bound themexecutor.maximumNumJobs: 1000000in a values file renders"1e+06"EXECUTOR_MAXIMUM_NUM_JOBS: ""for a nullexecutor.maximumNumJobs, as it does onmainFixed since the review:
subchartexample and the changelog now saysg.nameOverrideis for new installs only (5ba343a).a805a0d):nodeSelector,affinity, andtolerationsprecedence in both directions for the migrator, k8s executor, dind executor, andprivate-docker-registry; the migrator's omitted podsecurityContext; dind's default of 10 jobs on a single executor; and theexecutor.envallow / deny rules.helm unittestnow passes 263, 12, 9, and 13 cases (sourcegraph, dind, k8s, migrator). The new cases fail 9 of 34 againstmain's templates, and each of 16 one-line template mutations fails at least one case. If issue 2 settles on a default of 1, the dind default case changes with it.executor.maximumNumJobs(c205543), where it rendered an empty variable and the executor crash-looped withinvalid int "".helm upgrade --reuse-valuesfrom a chart older than 7.4.0 gets there without anyone writing null. Tested onm5with a temporary release made frommain's chart minus that default:--reuse-valuesto the fixed chart failed withexecutor.maximumNumJobs is required, for example 10and left the release at revision 1 and the pod untouched; adding--set executor.maximumNumJobs=1upgraded it, 2/2 ready with 0 restarts.0still renders, since it pauses the executor.queues[].maximumNumJobs(a521e88). Before, the chart ignored the field and refusedEXECUTOR_MAXIMUM_NUM_JOBSin a queue'senv, so every queue renderedexecutor.maximumNumJobs. A queue that sets none, null, or an empty string still usesexecutor.maximumNumJobs;0is kept and pauses that queue. Two new cases; each of 6 one-line template mutations fails one of them. Tested onm5with a temporary two-queue release andexecutor.maximumNumJobs: 3: thecodeintelexecutor, set to 2, ran withEXECUTOR_MAXIMUM_NUM_JOBS=2and thebatchesexecutor, set to nothing, with3; both pods were 2/2 ready and sent heartbeats. Changing only thecodeintellimit to 5, then 0, replaced only thecodeintelpod.helm unittestnow passes 263, 16, 9, and 13 cases.nameOverridedocs no longer say every selector usesapp.kubernetes.io/name(2d074c5). Rendered withnameOverride: customto count: insourcegraph, 38 of 39 selectors carry it (not the grafana StatefulSet's, nor jaeger's when enabled, which are fixed tojaeger), and none withsourcegraph.selectorLabelsOverride; in dind, the Deployment and StatefulSet do and theprivate-docker-registryService does not; in k8s both selectors do, so that string is unchanged. The migrator has no selector, so its docs now say the label is on the Job and its pod and can change on an installed release, which them5migrator test above already did (revision 3, new Job, labelcustom).values.yamland READMEs (a4a9dfb).sourcegraph.nodeSelector,affinity, andtolerationsthe default for "every pod" (538a002). They are for the executor pod; the executor creates job pods itself fromexecutor.kubernetesJob.nodeandexecutor.kubernetesJob.pod. This is a docs fix, not a chart fix: a fallback would move the job pods of any release that shares a values file with thesourcegraphchart, andsourcegraph.affinitycannot be expressed in the executor's settings, which take one required node term and required pod terms only. Tested onm5with the live executor release and a batch job per step: with onlysourcegraph.nodeSelectorandsourcegraph.tolerationsset, the executor pod got both and the job pod got neither; withexecutor.kubernetesJob.node.selectorand.tolerationsadded, the job pod got those and nothing fromsourcegraph.*. Rolled back to revision 30's manifest afterward, 0 diff lines.## 8.0.0in the executorCHANGELOG.md(55c0ec9,26e9c54).Problem
Customers (and their AI assistants) read our charts' Values.yaml files, and configure values in their override files, and nothing changes, because the values we document are very different from the values the charts consume
Some values worked but weren't documented
Some values were read incorrectly, eg.
storageClass.allowVolumeExpansion: falsewas ignored becausetruewas an inline default which took precedencesourcegraph.nameOverride, in all four charts; the charts read a top-levelnameOverridedind's
executor.maximumNumJobs: dind never rendered it, and refusedEXECUTOR_MAXIMUM_NUM_JOBSinexecutor.env, so executors always ran 1 job, the executor's default, while the chart documented 10Per-component
nodeSelector,affinity,tolerations, andpodSecurityContextthat the dind and migrator READMEs list, and global ones the k8s chart documentsdind's
storageClass.create,provisioner,type,parameters, andallowedTopologies; dind has no StorageClass templateThe migrator's generic values block, copied from the
sourcegraphchart, and the k8s chart'sexecutor.securityContext.privilegedExample override files:
basic(wrong key names),custom-security-context(container fields under the pod),openshift(twoserviceAccountkeys),opentelemetry-exporter(the removedjaegerexporter)Solution
This PR fixes the documentation which was incorrect, and fixes the configs which were broken
Amp rendered every one of the 730 values in the four charts, and checked the examples, READMEs, and doc site against the templates
nameOverride: document the top-level key, which is the Helm convention and what a parent chart sets for a subchart (
<alias>.nameOverride). Analiasreplaces the chart name in the selectors, so thesubchartexample now setssg.nameOverride: sourcegraphdind job limit: render
executor.maximumNumJobs, with the documented default of 10, as in the k8s chart.EXECUTOR_DOCKER_ADD_HOST_GATEWAYandEXECUTOR_KEEP_WORKSPACES, which have no value, can now be set inexecutor.envScheduling: each pod uses its own
nodeSelector,affinity, andtolerations, then the globalsourcegraph.*ones: the k8s executor, the dind executor and registry, and the migrator Job. The dind registry getspodSecurityContext. These are now invalues.yaml, instead of README proseNot done, on purpose: changing the effective top-level nameOverride key work the way
sourcegraph.nameOverridewas documented; anyone who set it has default selectors today, and would get new ones, so their next upgrade would failCustomer-impacting changes on their next upgrade:
dind executors that don't set
executor.maximumNumJobsgo from 1 job at a time to 10, and their pods restart once. Before upgrading, sizeexecutor.resourcesandexecutor.storagefor 10 concurrent jobs, or setexecutor.maximumNumJobs: 1A release that set values that were ignored now gets them. For example, a k8s executor release with
sourcegraph.nodeSelectorgets its executor pod rescheduled; job pods are not affectedOtherwise default renders don't change:
sourcegraphandk8sare byte-identical tomain. The migrator Job drops asecurityContext: nullTesting
helm unittestandhelm lintpass on all four charts. Each new case fails onmain, exceptnameOverride, which passes there too because no template changed: dindmaximumNumJobson every queue, per-component scheduling in dind and the migrator, global fallback in k8s, the registry'spodSecurityContext, andallowVolumeExpansion: falseRendered every chart with defaults and with each example, before and after. The only changes are the intended ones above; the
openshiftexample renders byte-identical, so the keys removed from it were unusedReran the per-value render audit on the three smaller charts. Every documented value now changes the render, except ones that need another value set first (
executor.storage.classwithstorage.type: ephemeral,sourcegraph.image.defaultTagwithuseGlobalTagAsDefault), and the k8s chart'ssourcegraph.labels, which Render sourcegraph.labels on every object, and add storage and ServiceAccount labels #1008 fixesThe
subchartexample, against this branch's chart:app.kubernetes.io/namesourcegraphnameOverride: customcustomsg, defaultsgsg,sg.nameOverride: sourcegraphsourcegraphsourcegraph.nameOverride,sg.sourcegraph.nameOverride, orglobal.nameOverrideOn a cluster
Tested at
4549ed9on them5test instance (EKS, Sourcegraph 8.1.4873), with temporary releases next to the live one, plus one real upgrade and rollback of the k8s executor release. The livesourcegraphrelease was upgraded later, see Upgrade of the livesourcegraphrelease.storageClass.allowVolumeExpansion: falsemaincreates the class withtrue, this branch withfalse; the default is stilltruesourcegraph.nodeSelectorset, the pod gets it;executor.nodeSelectorreplaces it. The pod came back Ready and sent heartbeatsexecutor.maximumNumJobsEXECUTOR_MAXIMUM_NUM_JOBS=10is in the pod, which restarts once and sends heartbeatspodSecurityContext,executor.envEXECUTOR_MAXIMUM_NUM_JOBSinexecutor.envis still refusedmigrator.nodeSelectorreplaces the global one, nosecurityContext: null, and the Job completed withargs: ["validate"]opentelemetry-exporterexamplesmain, the collector rejects all three withunknown type: "jaeger". On this branch all three validate and the collector starts. Real traces: see Traces through theotlpexportercustom-security-contextexamplemain, the API server rejects the render (strict server dry run); on this branch it passesnameOverridespec.selector ... field is immutable, as the changelog warnshelm unittestsourcegraph, dind, k8s, migrator)Upgrade of the live
sourcegraphreleaseReal upgrades of the
m5release at26e9c54, 2026-11-14, with the instance's own values file. The release was deployed from a test branch, so its manifest differed frommain's by 358 lines before this PR is counted. After each step: all 28 pods ready, the API and a search answered, and a server-side batch job completed through the executor. Log:logs/live-upgrade-m5.login thename-overrideworktree.main's. No object was recreated (same UIDs, ClusterIPs, and volumes)mainmainto this branchmainto this branch with--reuse-valuesstorageClass.allowVolumeExpansion, the condition of open issue 1, butm5setsstorageClass.create: false, so no StorageClass is rendered and the shared one stayedallowVolumeExpansion: trueSo for a release whose values touch none of the changed templates, upgrading from
mainto this branch changes nothing on the cluster. Not tested on the live release:storageClass.create: true(tested with a temporary release above), becausem5shares its StorageClass with other instances.Traces through the
otlpexporterTested at
26e9c54onm5, 2026-11-14: the live release upgraded to this branch with each example's values, and real Sourcegraph traces followed from the frontend to a test Jaeger (frontend,otel-agent,otel-collector,otlpexporter, Jaeger). Each request was a GraphQL search sent withX-Sourcegraph-Should-Trace; its trace ID, from theX-Traceresponse header, was then looked up in Jaeger. Log:logs/otlp-traces-m5.login thename-overrideworktree.override.yamlfrontendandzoekt-webserver. 0 of 20 searches sent without the headeroverride.yamlandoverride-processor.yamloverride-tls.yamlas written, to a server whose certificate is from a private CAauthentication handshake failed: with noca_file, it checks the server against the public rootsoverride-tls.yamlplustls.ca_file: /tls/ca.crt, with the CA added to the same SecretWhat the test needed that the examples do not say:
"observability.tracing"object.m5's had none, and the first run found 0 of 20, with no span reachingotel-agenthelm upgradedoes not restart theotel-collectorpod; it kept the old config until the pod was deleted. Render sourcegraph.labels on every object, and add storage and ServiceAccount labels #1008 adds the checksum annotation that fixes thisopenTelemetry.gateway.commandkeeps its own--config, so the exporters are ignored.m5sets one; the test reset it to the chart's commandThe release was rolled back to its starting manifest afterward, and the site config, test Jaeger, collector, and Secrets were removed.
10 concurrent dind jobs under load
Tested at
26e9c54onm5, 2026-11-14, with a temporary dind release on its own node (m5a.xlarge: 4 vCPU, 16 GiB, 80 GiB disk), the chart's default of 10 jobs, and no resources values, so the pod isBestEffort. Each job was a server-side batch spec whose step holds memory, writes a file inside its container, and spins one core. The live k8s executor was scaled to 0 for the test, so every job went to the dind executor. Log:logs/load-test-m5.login thename-overrideworktree.dockerd, itscontainerd, and the container shims, not the jobs. Thedindcontainer restarted (exit 137), all 10 jobs failed withexit code 125, and the pulled images were lost. No eviction and noMemoryPressureconditionDiskPressureand evicted the pod. The replacement pod started on another node, the 10 jobs were reset and started again there, and began filling that node tooWhat the test showed about the chart, all of it also true on
main:/docker/<id>at the node's cgroup root, withoom_score_adj0, whiledockerdhas 1000.kubectl top podshowed 22m CPU for thedindcontainer while the node ran at 4000m. So a resources value onexecutorordindcannot bound the jobs, and under memory pressure the kernel kills the Docker daemon before the jobsdindcontainer when it evicted the pod, while the jobs had written over 40 GiB through it. Docker's disk use is not counted against the podstep.docker.step.0.postexits 1 with git'sdetected dubious ownership. The executor container clones as uid 100 and thebatcheshelpercontainer runs as root. The load tiers above ran withexecutor.podSecurityContext.runAsUser: 0, which makes the job completeNot tested: the same loads on a node shared with other pods, and a
codeinteljob.Linked PRs
Render sourcegraph.labels on every object, and add storage and ServiceAccount labels #1008 renders
sourcegraph.labelsin the k8s chart. Conflicts with this PR in dind's_helpers.tpland README, and the executorCHANGELOG.md; whichever merges second needs a rebaseThe docs repo has five related fixes, not in this PR (trace sampling, Grafana snippet,
$JAEGER_HOST,max_traces, EKS StorageClass provisioner)Amp threads
The audit and these fixes
Review and cluster tests
Changelog
nameOverrideis a top-level value in all four charts;sourcegraph.nameOverridenever had an effect. Set it only on a new installdind:
executor.maximumNumJobsnow works, with a default of 10. Executors that don't set it go from 1 job at a time to 10.EXECUTOR_DOCKER_ADD_HOST_GATEWAYandEXECUTOR_KEEP_WORKSPACEScan be set inexecutor.env. Each entry inqueuescan set its ownmaximumNumJobsPer-component
nodeSelector,affinity, andtolerationswork in the k8s, dind, and migrator charts, falling back to the globalsourcegraph.*ones. Releases that had set these values now get them on upgrade.Removed values that never had an effect: dind
storageClass.*exceptname, k8sexecutor.securityContext.privileged, and the migrator's generic blockstorageClass.allowVolumeExpansion: falsenow works