Skip to content

[Backport release-1.6] fix(api): let the garbage collector finalize Applications - #4683

Merged
myasnikovdaniil merged 1 commit into
cozystack:release-1.6from
yankawai:fix/application-garbage-collection-1.6
Oct 14, 2026
Merged

myasnikovdaniil merged 1 commit into
cozystack:release-1.6from
yankawai:fix/application-garbage-collection-1.6

Conversation

@yankawai

@yankawai europrinter (yankawai) commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Backport of #4610 to release-1.6. Update on this branch has no applyLiveMetadata, so the finalizer merge and the copy of the deletion metadata are done inline, in Update and in its conflict retry. The tests are the same as on main.

An Application and its HelmRelease share one UID, so the garbage collector keeps one graph node for both and may finalize it through the Application endpoint. The API never returned finalizers on an Application, so when the collector dropped foregroundDeletion or orphan through it, the HelmRelease kept foregroundDeletion after Helm had uninstalled the release and the deletion never completed. The Application now carries the two garbage collection finalizers, and an update that adds or removes one of them applies the same change to the HelmRelease. Controller finalizers such as finalizers.fluxcd.io stay owned by the live object.

Validation

  • go test ./pkg/registry/apps/application/... passes on this branch.
  • Making the finalizer merge return the live list turns four tests red, the conflict-retry test among them.
  • No API types change, so code generation is not affected.

Screenshots

Not a UI change.

Downstream repositories

Walked the trigger map against the diff. It changes the Application REST handler and its test, with no chart, values, schema, CRD or hack/ file, so no companion repository needs an update.

Release note

fix(api): an application deleted with foreground or orphan propagation no longer leaves its HelmRelease stuck with the foregroundDeletion or orphan finalizer

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: cozystack/cozystack/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: fe865b4f-8cfe-454b-8e71-029ca1d24ff4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added size/L This PR changes 100-499 lines, ignoring generated files area/api Issues or PRs related to the cozystack-api aggregated API server kind/bug Categorizes issue or PR as related to a bug labels Oct 1, 2026
@yankawai
europrinter (yankawai) marked this pull request as ready for review October 2, 2026 10:32
@yankawai
europrinter (yankawai) force-pushed the fix/application-garbage-collection-1.6 branch from 1ff0580 to 55b5767 Compare October 4, 2026 09:05
@yankawai europrinter (yankawai) changed the title fix(api): backport application garbage-collection finalizers to release-1.6 [Backport release-1.6] fix(api): let the garbage collector finalize Applications Oct 4, 2026
@github-actions github-actions Bot added the area/release Issues or PRs related to release tooling (changelog, backport, release pipeline) label Oct 6, 2026
An Application and its HelmRelease share one UID, so the garbage
collector keeps a single graph node for both and may finalize it through
the Application endpoint. The API never returned finalizers on an
Application, and every update restored the HelmRelease finalizers from
the live object. When the collector came to drop foregroundDeletion or
orphan through the Application, removeFinalizer found no such finalizer
and returned as if it were already gone. The HelmRelease then kept
foregroundDeletion after Helm had uninstalled the release, and the
deletion never completed.

The Application now carries the two garbage collection finalizers, and
an update that adds or removes one of them applies the same change to
the HelmRelease. Controller finalizers such as finalizers.fluxcd.io stay
owned by the live object, so helm uninstall still runs before the
release goes away. The deletion timestamp and grace period are copied
from the live object, which keeps an update of a terminating release
valid.

Backport of 46b5a54 to release-1.6. Update on this branch has no
applyLiveMetadata, so the finalizer merge and the deletion metadata copy
are done inline, in Update and in its conflict retry. The tests are the
same as on main.

Assisted-by: LLM
Signed-off-by: Yan Bondarenko <[email protected]>
@yankawai
europrinter (yankawai) force-pushed the fix/application-garbage-collection-1.6 branch from 55b5767 to e63439b Compare October 6, 2026 21:56
@myasnikovdaniil
myasnikovdaniil merged commit f50f4a8 into cozystack:release-1.6 Oct 14, 2026
12 of 44 checks passed
myasnikovdaniil added a commit that referenced this pull request Oct 14, 2026
…a and repair empty required fields (#4919)

## What this PR does

Backport of #2914 and #3956 to `release-1.6`. It sits on top of #4683,
which is merged here now.

#2914: `Update` on an Application rebuilds the HelmRelease and sends a
full-replace PUT, so metadata owned by other controllers was dropped,
helm-controller's `finalizers.fluxcd.io` among it. A delete landing
before helm-controller added the finalizer back removed the HelmRelease
without `helm uninstall` and orphaned every resource of the release. Now
`Update` carries over finalizers, ownerReferences and labels/annotations
outside the `apps.cozystack.io-` prefix from the live object,
`mergeMaps` no longer returns the shared config map that requests wrote
into, and a conflict retry rebuilds the carried metadata from the object
it just read instead of writing the stale copy back.

#3956: a required spec field written with nothing under it (`replicas:`)
reached Helm as null, Helm coalescing deletes it together with the chart
default before schema validation, and the whole release failed with
"missing property" for a property the spec does contain. Such a null is
removed on Create and Update now so the chart default applies, and the
client gets a warning with the field name. Optional fields, free-form
subtrees, user-keyed map entries and array elements keep their nulls.
Admission validates the normalized copy, the caller's object is not
mutated anymore.

On this branch #4683 inlined the garbage collection finalizer merge and
the deletion metadata copy, because `applyLiveMetadata` didn't exist
yet. They move into `applyLiveMetadata` here, so `Update` and its
helpers match main. Suspend preservation (#4411) is not on this branch
and not part of this backport.

The example field in #3956, `talos.registryMirrors`, doesn't exist in
the kubernetes chart on this branch, so the tests use required fields
that do (`talos.installerRepository`, `talos.version`,
`controlPlane.replicas`). The node group null is its own case, because
`nodeGroups` is still a typed map here and its entries require
`instanceType`. Three test helpers main has from #3315 are copied into a
test-only file. Production code needed one import beyond the
cherry-picks.

### Validation

- `go build ./...` and `go vet ./pkg/registry/...` are clean and `go
test ./pkg/registry/...` passes on the head, rebased onto release-1.6
after #4683 merged.
- The Helm coalescing test renders this branch's own chart values and
schemas, Helm rejects the empty required fields before the repair and
accepts them after it.
- Reverting `applyLiveMetadata` to the plain live finalizer list,
skipping the retry rebuild, or validating the un-normalized object each
turns the related tests red.

### Downstream repositories

The change touches the Application REST handler and its tests only: no
chart, values, schema, CRD, version enum or `hack/` file, so none of the
trigger map entries match.

- [x] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:

### Release note

```release-note
fix(api): updating an Application no longer strips finalizers, ownerReferences and foreign labels/annotations from its HelmRelease, which could make a following delete skip helm uninstall and orphan the release's resources. A required field written with no value in an application spec is treated as unset so the chart default applies, instead of failing the whole release.
```
myasnikovdaniil added a commit that referenced this pull request Oct 14, 2026
…ed across an app update (#4928)

## What this PR does

Backport of #4411 to `release-1.6`.

`REST.Update` rebuilds the HelmRelease from the Application and writes
it back whole, and Application has no suspension, so every edit through
the API resumes a release that an operator or a controller suspended.
The CNPG restore driver depends on it: it suspends the target release,
patches the Postgres app through this API, purges the Cluster and
resumes only after the purge, so the next render lands
`bootstrap.recovery` on an empty namespace. When the patch resumes the
release, helm-controller upgrades right away, the Cluster gets rendered
while the purge still runs, the driver deletes it, and nothing renders
it again because the values don't change anymore. RestoreJob stays
`Running` until its deadline.

Why it is needed on this line now. The race was hidden by timing: Update
dropped `finalizers.fluxcd.io`, helm-controller spent its first pass
putting it back and requeued, and that delay usually let the purge
finish first. #4683 and #4919 keep the finalizer across the write, so
the delay is gone. After they landed, `postgres-2-backup-roundtrip`
failed on v1.6.5-rc.1
(https://github.com/cozystack/cozystack/actions/runs/38583543706) and in
two earlier PR runs, each time stuck at `TargetPurged: Cluster ...
deleted; awaiting chart-rendered re-bootstrap`. In the snapshot of the
rc run the HelmRelease of the restore target upgraded to v2 at
16:16:13Z, the Cluster was deleted at 16:16:22Z, and no Cluster of the
target exists afterwards.

The cherry-pick conflicted only in comments of `REST.Update`, where this
branch already has the metadata carry-over. The two `Spec.Suspend`
assignments merged unchanged and the comments read as they do on main.

### Validation

- `go build ./...` is clean, `go vet ./pkg/registry/...` is clean, `go
test ./pkg/registry/... ./internal/backupcontroller/...` passes.
- Removing either `Spec.Suspend` assignment turns its test red:
`TestUpdate_PreservesSuspend` for the first write,
`TestUpdate_RefreshesSuspendOnConflictRetry` for the conflict retry.
- Not run here: the restore round trip on a live cluster, the E2E of
this PR covers it.

### Downstream repositories

It changes how the aggregated API writes the HelmRelease back, no
downstream repository restates that.

- [x] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:

### Release note

```release-note
fix(api): app edit through cozystack API no longer resumes a suspended HelmRelease, so postgres restore waits for its purge before the cluster is rendered again.
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api Issues or PRs related to the cozystack-api aggregated API server area/release Issues or PRs related to release tooling (changelog, backport, release pipeline) kind/bug Categorizes issue or PR as related to a bug size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants