Repository navigation
Description
Describe the bug
ConvertHelmReleaseToApplicationWithMonitor calls r.applySpecDefaults(&app).
So every GET, LIST and WATCH returns the spec with the schema's defaults filled
in. Create does not apply them: the HelmRelease's spec.values are the spec
exactly as the client sent it.
Update starts from the defaulted object returned by r.Get, applies the
change and writes app.Spec as the new values. The first update to any
Application created with a partial spec therefore writes the defaults into
spec.values, even when the update only touches metadata. That bumps the
HelmRelease's generation, and Flux runs a Helm upgrade the user never asked for.
Any client that sends only the fields it sets is affected, including a
dashboard form or kubectl apply with a minimal manifest.
Environment
- Cozystack v1.6.2; the code path is unchanged on
mainas of 2026-09-18 (8799a4e) - Flux helm-controller v1.5.0
- Kubernetes v1.34.3, Talos v1.12.6
- Provider: Hetzner bare metal, single node
To Reproduce
- Create a
VMDiskwith onlyspec.source.image.nameandspec.storage. - Record
kubectl -n <ns> get helmrelease vm-disk-<name> -o jsonpath='{.metadata.generation} {.spec.values}'. - Patch only an annotation on the
VMDisk. - Repeat step 2. The generation has gone up by one, and
spec.valueshas
gained the schema defaults.
Expected behaviour
A metadata-only change does not change the HelmRelease's values, and does not
upgrade the release.
Actual behaviour
The first write of any kind upgrades the release. For a VMInstance that
upgrade then hangs; see #3734.
It also freezes the application on today's defaults. Once the first update has
written them into spec.values, they are no longer defaults but values the user
appears to have set. A later chart release that changes a default never
reaches an application that has been written to even once, and nothing
distinguishes it from one where the user chose the old value. For example, open
PR #3936 moves RabbitMQ's schema default resourcesPreset from t1.nano to
s1.nano. A RabbitMQ created without resourcesPreset and later annotated,
relabelled or edited in any way now has resourcesPreset: t1.nano in its
values, and keeps it after #3936 ships. #3956 names this hazard as the reason
not to write schema defaults into the spec. Update does exactly that today,
silently, and only to applications that have been touched.
Logs
The HelmRelease spec.values before and after one annotation patch. Generation
went from 1 to 2 for both.
vm-disk-probe:
+ "optical": false,
"storage": "2Gi",
+ "storageClass": "replicated"vm-instance-probe:
+ "cloudInit": "",
+ "cloudInitSeed": "",
+ "cpuModel": "",
+ "external": false,
+ "externalAllowICMP": true,
+ "externalMethod": "PortList",
+ "externalPorts": [22],
+ "gpus": [],
+ "networks": [],
+ "resources": {},
+ "runStrategy": "Always",
+ "subnets": []Additional context
A possible fix, in Update: before writing spec.values, drop every field
that was absent from the stored values and whose incoming value equals the
default applySpecDefaults filled in on read. A field the client really changed
survives, a metadata-only update leaves the values byte-identical, and a default
stays a default. The reverse, applying defaults in Create so the two paths
agree, would remove the unexpected upgrade but freeze every application at
creation instead, for the reason #3956 gives.
Setting a field to exactly its current default then does not pin it. That seems
the right trade: it is what an absent field means already.
Checklist
- I have checked the documentation
- I have searched for similar issues
- I have included all required information
- I have provided clear steps to reproduce
- I have included relevant logs
Activity