Skip to content

cozystack-api: spec defaults are applied when an Application is read but not when it is created, so its first update upgrades the Helm release #4342

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 main as 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

  1. Create a VMDisk with only spec.source.image.name and spec.storage.
  2. Record kubectl -n <ns> get helmrelease vm-disk-<name> -o jsonpath='{.metadata.generation} {.spec.values}'.
  3. Patch only an annotation on the VMDisk.
  4. Repeat step 2. The generation has gone up by one, and spec.values has
    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

  1. added
    kind/bugCategorizes issue or PR as related to a bug
    area/apiIssues or PRs related to the cozystack-api aggregated API server
    triage/acceptedIndicates an issue is ready to be actively worked on
    priority/important-soonMust be staffed and worked on either currently, or very soon, ideally in time for the next release
    and removed
    triage/needs-triageIndicates an issue needs triage by a maintainer
    on Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/apiIssues or PRs related to the cozystack-api aggregated API serverkind/bugCategorizes issue or PR as related to a bugpriority/important-soonMust be staffed and worked on either currently, or very soon, ideally in time for the next releasetriage/acceptedIndicates an issue is ready to be actively worked on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions