Skip to content

fix(operator): the Package reconciler rewrites a HelmRelease that another Package owns #4975

Open

Description

@myasnikovdaniil

Summary

createOrUpdateHelmRelease (package_reconciler.go L458-L506) looks HelmRelease up by (namespace, name). If one exists, it replaces spec, labels, annotations and ownerReferences with values computed for the current Package (L501-L504) and calls Update. It never checks who owns the existing object.

So two Packages that resolve to same (install.namespace, install.releaseName) fight over one release, and the later reconcile wins. The function comes from the first version of Package reconciler (f28d639ae, 2025-12-24), so it predates marketplace.

Impact

Before marketplace a Package came from cluster admin. Since #4308 connecting a tapped repository creates registration Package for default variant, so third-party content reaches this path without admin writing a manifest. Repository that publishes component with install: {namespace: cozy-cert-manager, releaseName: cert-manager} points at the platform's own release and replaces its spec and ownerReferences, and disconnecting it then deletes registration Package, with garbage collection following the replaced ownerReference.

Clash doesn't need hostile publisher. Platform ships release cozy-system/postgres-rd (postgres-application.yaml L26-L30), and cozypkg init postgres scaffolds component postgres-rd with install.namespace: cozy-system (init.go L70-L85). Release name defaults to component name (package_reconciler.go L302-L305), so third-party postgres app built from the scaffold targets the platform release by default. PackageSource collision check compares only PackageSource names, it doesn't catch this.

Same function is in v1.6.4 (package_reconciler.go L386-L432), so this is not marketplace only. Any Package goes through it, including one made with cozypkg add, and the fix needs a backport to release-1.6.

Not run on a cluster. What I read in code is the missing ownership check and the ownerReference replacement. What I inferred is the fight over the release and the garbage collection outcome through a tap.

#4361 and #4424 fixed several ApplicationDefinitions claiming one kind (updateHelmReleasesForAppDef), that is a different path and this function is untouched. Operator already refuses to apply over PackageSource it does not own (#4001, main.go L801-L812), and the review of #3940 put it as "the platform should never force-apply over an object it does not own". HelmRelease is second place that needs it.

Suggested fix

  • Before the update compare controller ownerReference of existing HelmRelease with current Package (metav1.IsControlledBy). On mismatch write nothing, set condition on Package and emit event naming current owner.
  • HelmRelease with no controller owner (made by hand or by installer) needs a decision. Upgrades of existing clusters must keep working, so safe default is to refuse unless release already carries this Package's labels. Up to whoever implements it.
  • For planned moves of release between Packages require annotation on HelmRelease that names the Package allowed to take it.

Acceptance

  • Package A owns a HelmRelease, Package B resolves to same name. After B reconcile release is unchanged and B reports condition naming A.
  • Reconcile of A still updates the release.
  • Cluster upgraded from 1.6 reconciles all platform releases as before.
  • Unit tests for the three cases.

Activity

  1. added
    kind/bugCategorizes issue or PR as related to a bug
    securitySecurity-related issues and features
    priority/important-soonMust be staffed and worked on either currently, or very soon, ideally in time for the next release
    area/platformIssues or PRs related to platform infrastructure (bundle, flux, talos, installer)
    on Oct 20, 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/platformIssues or PRs related to platform infrastructure (bundle, flux, talos, installer)kind/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 releasesecuritySecurity-related issues and featurestriage/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