Skip to content

chore(deps): update dependency crossplane-contrib/provider-helm to v1.5.0 - #141

Merged
patrickleet merged 1 commit into
mainfrom
renovate/local-start-crossplane-bootstrap
Oct 16, 2026
Merged

patrickleet merged 1 commit into
mainfrom
renovate/local-start-crossplane-bootstrap

Conversation

@renovate

@renovate renovate Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Update Change
crossplane-contrib/provider-helm minor v1.4.0 → v1.5.0

Warning

Some dependencies could not be looked up. Check the Dependency Dashboard for more information.


Release Notes

crossplane-contrib/provider-helm

v1.5.0

Compare Source

Release v1.5.0

[!CAUTION]
Charts are now cached under /tmp/content-cache instead of /tmp/charts, and the provider exits at startup if it cannot create that directory. Make sure the provider pod can write to /tmp. A DeploymentRuntimeConfig that runs the provider with a read-only root filesystem needs a writable volume that covers /tmp/content-cache; a volume mounted at /tmp/charts alone is no longer enough. Due to the fact that provider-helm requires multiple write directories under /tmp the simplest approach is to mount a full /tmp path.

[!IMPORTANT]
This release changes how the provider resolves, caches and observes charts, and how spec.forProvider.takeOwnership behaves. Some of these changes act on existing Release objects on the first poll after the upgrade, with no change to their spec: a release with takeOwnership: true whose resources were re-stamped by another actor is repaired or turns failed, and a release with a bare OCI URL stays on its recorded chart.version instead of following the highest semver tag. Review the behavior changes below before upgrading.

Summary

This release makes a Release's deployed chart digest, chart URL and ownership adoption observable through Helm release labels, replaces the filename-keyed chart cache with a content-addressed one, validates chart specs at admission, turns takeOwnership into a standing claim that detects ownership drift, adds a caBundle field for charts served behind a private CA, deprecates the v1alpha1 APIs behind a conversion webhook, shares the target cluster client across reconciles, and fixes ssaForceConflicts on releases installed before Helm v4, plainHTTP/insecureSkipTLSVerify for OCI pulls, and set entries with an optional missing source.

Highlights
  • Ownership drift detection: takeOwnership now keeps a Release's claim on its resources live instead of adopting them once and never looking again (#​418)
  • Observable deploy facts: the deployed chart digest, chart URL and ownership adoption are recorded as Helm release labels, so URL-only deployments detect drift and the digest pin survives status resets (#​385)
  • Content-addressed chart cache: charts are cached by the digest the registry or repository index advertises, so a chart from one source is never served to a Release that points at another, and re-pushed tags are picked up (#​409)
  • Chart spec validation: CEL rules reject never-valid chart specs at admission, and chart.version is reconciled with the version embedded in an OCI URL (#​405)
  • New API field: spec.forProvider.caBundle on Release for trusting a private CA when pulling from a chart repository or OCI registry (#​390)
  • API deprecation: v1alpha1 Release, ProviderConfig and ProviderConfigUsage are deprecated in favor of v1beta1; Release gets a conversion webhook (#​400)
  • Performance: one target cluster client per ProviderConfig credential set instead of one per reconcile, with provider_helm_client_cache_* metrics (#​410); opt-out of the Secret informer cache and CRD schema stripping in the manager cache (#​392)
  • Bug fixes: ssaForceConflicts on releases installed before Helm v4 (#​406), plainHTTP and insecureSkipTLSVerify for OCI chart pulls (#​408), set entries with an optional missing source (#​371)
  • Security updates: golang.org/x/net bumped to v0.60.0, addressing five HTTP/2 advisories in net/http including the critical GO-2026-6612 (#​420); go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc bumped to v0.21.0 (addresses CVE-2026-81871 / GHSA-w34q-cm8f-9c5x) (#​401)

Ownership drift detection

spec.forProvider.takeOwnership is now a standing claim (both cluster-scoped and namespaced APIs). Previously it adopted pre-existing resources on the first deploy and never looked at them again, so a resource re-stamped by another actor stayed foreign until something else triggered an upgrade.

While takeOwnership: true is set, every poll compares the app.kubernetes.io/managed-by label and the meta.helm.sh/release-name and meta.helm.sh/release-namespace annotations of every chart-rendered resource against the release, and reports the observation out of date when any differ so that the next deploy re-stamps them. Resources that already match are left untouched, and status.synced and the Available condition are not affected by this check: the release is still the chart, values and version that were asked for. Adopting pre-existing resources stays a one-time operation on the first deploy, recorded in status.atProvider.ownershipTaken, which is now informational. The check fetches only object metadata, one lookup per rendered resource per poll. Thanks @​stevendborrelli! (#​418)

[!WARNING]
If another actor (kubectl, a GitOps controller, the Helm CLI) has re-stamped app.kubernetes.io/managed-by or the meta.helm.sh/release-name / meta.helm.sh/release-namespace annotations on one of those resources under its own server-side apply field manager, the repair upgrade fails with an apply conflict and the Release turns failed on the first poll after upgrading the provider, with no change to its spec. Set spec.forProvider.ssaForceConflicts: true to let the provider take the fields back, or remove takeOwnership to stop the retries. Releases still on client-side apply (installed before v1.4.0 and never upgraded with ssaForceConflicts) are not checked: their upgrades cannot rewrite ownership values the chart renders unchanged, so detection for them would retry forever. They migrate to server-side apply when ssaForceConflicts is set.

Chart resolution, observation and caching

Three changes rework how a Release selects, observes and caches its chart (#​385, #​405, #​409, fixing #​382, #​383, #​384 and #​404). Together they close the cases where a URL change went undetected, a digest pin was silently ignored, a chart cached for one Release was served to another, or an invalid chart spec failed with an opaque Helm error at deploy time.

Deploy facts as Helm release labels (#​385)

The facts a Release needs to observe about its own deploy are persisted as custom Helm release labels, which Helm stores on the release Secret atomically with the deploy and returns with the release. Status fields were not reliable for this: crossplane-runtime resets status set during Create when it persists critical annotations, and the observation zeroed ownershipTaken on every poll, which broke the re-adoption guard one poll after each upgrade (#​382). Labels written on install and upgrade:

  • release.helm.crossplane.io/digest-hash: sha256-<first 56 hex> of the effective chart digest. Used for drift detection only; pull-time verification keeps the full digest.
  • release.helm.crossplane.io/url-hash: sha256-<first 56 hex> of sha256(url). A URL-only deployment now detects a changed URL and upgrades.
  • release.helm.crossplane.io/ownership-taken: sticky true once takeOwnership was exercised, so later deploys never silently re-adopt.

Observe rehydrates status.atProvider.digest and status.atProvider.ownershipTaken from these labels and falls back to the previously persisted status for releases deployed before label support.

Chart spec validation and OCI URL versions (#​405)
  • Two CEL rules on spec.forProvider.chart reject never-valid specs at admission: name and repository are required when url is not set, and digest needs an OCI pull source (an oci:// URL, or an oci:// repository when url is not set). Without url, an empty name or repository also fails with a clear error before any pull instead of an opaque Helm error.
  • The version embedded in an OCI URL is reconciled with chart.version, mirroring the digest handling: a conflicting pair is rejected, and a bare OCI URL (no tag, no digest) deploys chart.version instead of silently pulling the highest semver tag. The field docs for repository, name, version and digest spell out what applies when url is set.
  • When both url and repository are set, registry credential resolution scopes to the host in url, the actual pull source, instead of repository.

[!NOTE]
Two different comparisons are involved, with different rules. The tag in an OCI URL and chart.version are two spec fields that both go to Helm, and Helm rejects a tag and --version that differ as strings, so the provider compares them as plain strings: :v1.2.3 next to version: 1.2.3 is a conflict. chart.version and the deployed chart's Chart.yaml version are compared as semver when both parse, so version: v1.2.3 on a bare OCI URL or in repository mode is up to date against a chart that declares 1.2.3. That second comparison only runs where chart.version selects the chart; for a tagged URL the tag selects it and the version is never compared with Chart.yaml.

Content-addressed chart cache (#​409)

Charts are cached under /tmp/content-cache, keyed by the sha256 digest the pull source advertises: the manifest digest from the OCI registry, or the tarball digest from the repository index. A chart from another source is stored under its own digest, so it cannot be served for a lookup that resolved elsewhere, whatever name and version it advertises. A pinned OCI digest is its own key, so a cached chart is served without contacting the registry, also when the Release has pull credentials. For a tag or version the provider asks the registry which manifest it points at, one request per deploy, so a re-pushed tag is picked up. A tag or version next to a pinned digest is validated against the registry before the cache lookup, so a spec whose version belongs to another digest fails whether or not the pinned chart is already cached. Classic repository tarballs are verified against the digest in index.yaml before they enter the cache. A cache hit is logged at Info level as chart served from content cache. The old filename-keyed /tmp/charts cache is gone, and the cache directory is created once at startup.

Behavior changes for existing releases

[!WARNING]
A Release created on v1.4.x with an OCI URL tag that differs from the chart's declared version as a string, typically a v-prefixed tag such as :v1.2.3 on a chart whose Chart.yaml says 1.2.3, reports ReconcileError on the first poll after upgrading the provider, with no change to its spec: v1.4.x late-initialized chart.version to 1.2.3 from the deployed chart, and that value now conflicts with the tag. The deployed release keeps running. Remove chart.version from the live object, or set it to the tag; neither triggers a deploy. A GitOps re-apply does not remove the field, since the provider wrote it:

kubectl patch releases.helm.crossplane.io <name> --type=json \
  -p '[{"op":"remove","path":"/spec/forProvider/chart/version"}]'

For the namespaced API use releases.helm.m.crossplane.io with -n <namespace>.

  • A bare OCI URL deploys chart.version. Provider versions up to v1.4.x late-initialized chart.version in URL mode too, so every Release created with a bare OCI URL already carries one. Such a Release stays on that version instead of following the highest semver tag, and one that had since moved to a newer chart is redeployed at the recorded version. Bumping chart.version now triggers an upgrade. Clearing it lets the next deploy pull the highest semver tag again, but does not trigger a deploy by itself. (#​405)
  • A tagged OCI URL whose tag differs from chart.version is a reconcile error. Provider versions up to v1.4.x ignored chart.version for the pull in URL mode but still compared it with the deployed chart, so such a pair ran a Helm upgrade on every poll with no error (#​130). It now reports ReconcileError with conflicting version input: URL contains :latest but spec.forProvider.chart.version is 1.2.3 before any deploy, and the deployed release keeps running until chart.version is removed or set to the tag. Tags equal to the chart version, which helm push produces, are unaffected; a v-prefixed tag next to a version that v1.4.x late-initialized is not, see the warning above. (#​405)
  • Switching a Release from repository mode to a tagged OCI URL needs chart.version set to the tag or removed in the same change. A version left from repository mode, including a late-initialized one, conflicts with the tag and is rejected. In v1.4.x, setting only url went undetected. (#​385, #​405)
  • URL mode no longer late-initializes chart.name and chart.version. The deployed version stays visible in status.atProvider.version. (#​385)
  • A digest next to a non-OCI URL is rejected at admission and at deploy time. It was never applied, so removing it does not change what is deployed. An existing Release that carries one keeps running and fails with that error at its next deploy. (#​405)
  • chart.version is compared with the deployed chart version as semver when both parse, in URL and repository mode alike, so v1.2.3 matches a chart whose Chart.yaml says 1.2.3. A Release whose version differed from the chart's declared version only by a leading v was redeployed on every poll; it is now up to date. A semver range or any other value that does not parse is still compared verbatim (#​108). (#​405)
  • A deploy of an OCI chart by tag or version needs the registry to resolve the tag, even when the chart is cached. A chart pulled once used to be served from disk for the lifetime of the pod, so a re-pushed tag went unnoticed until a restart. Only a chart pinned by digest alone deploys from the cache with no registry access. (#​409)
  • A tag or version set next to an OCI digest is checked against the registry on every deploy, also when the pinned chart is cached. A Release with pull credentials logs in for the check, so its deploy fails when the registry is unreachable; without credentials Helm skips the check when it cannot look the tag up. (#​409)
  • A classic repository chart whose tarball does not match the digest in the repository's index.yaml fails to deploy with chart downloaded from <url> does not match the digest advertised by its repository index. (#​409)
  • A chart referenced by a direct non-OCI URL is downloaded on every deploy and cached by its content hash. It used to be cached under the URL's basename and never fetched again while the pod lived. (#​409)
  • Charts are cached under /tmp/content-cache, and /tmp/charts is no longer used. The provider exits at startup if it cannot create /tmp/content-cache. (#​409)
  • A Release deployed before release labels whose chart.digest differs from status.atProvider.digest gets one Helm upgrade after the provider upgrade, which pulls the pinned chart and writes the digest-hash label. This includes a pin that was removed on an earlier provider version, where the removal was ignored and status still records the digest. (#​409, fixes #​404)

Deprecated v1alpha1 APIs

The v1alpha1 versions of Release, ProviderConfig and ProviderConfigUsage are deprecated in favor of v1beta1 and carry the deprecated marker, so the API server warns on requests to them. The Release CRD is now served with a conversion webhook on the provider's webhook server, with v1beta1 as the storage version: shared fields are converted through an unstructured round-trip, and fields that exist only in v1beta1 are preserved in a helm.crossplane.io/release-conversion-data annotation when read through v1alpha1 and restored on the way back, so a write through the deprecated API does not zero fields it cannot represent. ProviderConfig and ProviderConfigUsage need no webhook because their schemas are compatible. Migrate manifests to v1beta1; v1alpha1 is scheduled for removal in a future major release (#​213). The repository also gains an example-lint target and CI job that validate the example manifests against the shipped CRDs with kubectl-validate. (#​400)

New Features

  • Added spec.forProvider.caBundle to Release (both cluster-scoped and namespaced APIs): a ValueFromSource reference to a PEM-encoded CA bundle in a ConfigMap or Secret key (default key ca.crt), used to verify the chart repository's or OCI registry's TLS certificate without disabling verification. The bundle is concatenated with the system trust store, so a chart from a public CA keeps working next to a private one. See the commented usage in the OCI example. Thanks @​barunavo! (#​390, fixes #​391)
  • Target cluster clients are built once per distinct ProviderConfig credential set and shared by the cluster-scoped and namespaced Release controllers, instead of once per reconcile. The cache is sized at startup from the existing ProviderConfigs and exposes provider_helm_client_cache_size, provider_helm_client_cache_entries and provider_helm_client_cache_events_total metrics. For a Release with spec.connectionDetails, aggregated discovery dropped from two requests per reconcile to none. Two behavior changes come with it: client-side rate limiting is off for target cluster clients, the Helm client included (QPS = -1), and the provider exits at startup when it cannot list its ProviderConfigs or when reading their credentials takes longer than 300 seconds. (#​410)
  • Added the --enable-secret-cache flag (default true, env ENABLE_SECRET_CACHE). Set it to false to read Secrets with direct API calls instead of the informer cache, trading API server load for memory. The manager cache also strips the schema from cached CustomResourceDefinition objects. (#​392)

Bug Fixes

  • Fixed ssaForceConflicts: true failing with invalid client update option(s): forceConflicts enabled when serverSideApply disabled on releases installed before v1.4.0 (Helm v3). Helm v4's auto apply mode falls back to client-side apply when the previous revision recorded no apply method, so the flag could never be used on such releases. ssaForceConflicts now also enables server-side apply on the upgrade and rollback actions, and Helm migrates the release's managed fields from client-side to server-side apply during that upgrade. Behavior is unchanged when the flag is not set. Thanks @​jerryguowei! (#​406, fixes #​403)
  • Fixed spec.forProvider.plainHTTP and spec.forProvider.insecureSkipTLSVerify being ignored for OCI chart pulls and registry login. Helm v4 uses the preset registry client as-is and dropped the HTTP fallback Helm v3 had, so pulls failed with server gave HTTP response to HTTPS client or x509: certificate signed by unknown authority. Both settings now apply to the registry client itself and combine with caBundle. Thanks @​bcanvural! (#​408, fixes #​407)
  • Fixed a set entry whose valueFrom points at an optional: true Secret or ConfigMap failing the whole values composition with missing value for --set when the source or its key is missing. Such entries are now skipped; non-optional references still fail. (#​371, fixes #​265)
  • Fixed a digest pin added to or removed from a Release deployed before release labels being ignored: Observe copied the spec digest into an empty status digest, so the pin was marked satisfied without pulling the chart, and the removal was never noticed. Observe no longer copies the spec digest into status. (#​409, fixes #​404)
  • Fixed the chart cache directory being created with a non-atomic os.Stat then os.Mkdir on every reconcile, which made every reconcile but the first fail with mkdir /tmp/content-cache: file exists when many Releases reconciled at once on a fresh pod. The directory is created once at startup. (#​409)
  • Fixed the cluster-scoped Release reconciler not protecting its ProviderConfig from deletion. crossplane-runtime's default ProviderConfigUsage cleaner (crossplane-runtime#1113) looks for a single ProviderConfigUsage kind per scope and silently fell back to a no-op because the cluster scheme registers both v1alpha1 and v1beta1. The connector's usage tracker is now passed explicitly, so a ProviderConfig stays protected while a cluster-scoped Release that uses it is terminating. (#​399)
  • The install instructions in the README now resolve the latest semver tag instead of a hard-coded version. (#​373, fixes #​291)

Security & Dependency Updates

  • Updated golang.org/x/net from v0.58.0 to v0.60.0, addressing five HTTP/2 advisories in net/http: CVE-2026-78663 / GO-2026-6612 (double flow control refund on server streams, critical), CVE-2026-78659 / GO-2026-6603 (server memory exhaustion through Trailer headers), CVE-2026-78660 / GO-2026-6610 (transport accepts malformed framing-related headers), CVE-2026-78669 / GO-2026-6611 (excessive CPU from repeated initial window changes) and CVE-2026-97032 / GO-2026-6617 (server crash from an HPACK encoder race). golang.org/x/crypto, x/mod, x/term and x/text moved with it (#​420)
  • Updated go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc to v0.21.0, addressing CVE-2026-81871 / GHSA-w34q-cm8f-9c5x (#​401)
  • Updated crossplane-runtime from v2.3.3 to a v2.5.0 pre-release commit (9d02af3, 2026-09-22) for the ProviderConfig deletion protection and cache performance work (#​392, #​399)
  • Updated github.com/crossplane/crossplane/apis/v2 from v2.3.4 to v2.4.2 (#​392, #​413)
  • Updated provider-kubernetes from v1.3.0 to v1.4.1, which brings Kubernetes libraries from v0.36.3 to v0.37.1, controller-runtime from v0.24.1 to v0.25.2, go-containerregistry to v0.22.1 and grpc to v1.84.0 (#​410), and kubelogin from v0.1.4 to v0.2.20, which fixes the Azure (AKS) authentication regression shipped in provider-kubernetes v1.4.0 (#​419)
  • Updated sigs.k8s.io/kustomize from v0.21.1 to v0.21.2 (#​412)
  • Updated Go from 1.26.5 to 1.27.2 (#​380, #​386, #​410, #​417)
  • Updated the gcr.io/distroless/static base image digest (#​393, #​411)
  • Updated CI tooling: Crossplane 2.4.2 and Crossplane CLI v2.5.0 in e2e, kind v0.33.0, golangci-lint v2.14.0, docker/buildx v0.38.0 (#​356, #​378, #​389, #​395, #​396, #​414, #​415)
  • Updated GitHub Actions dependencies (#​365, #​379, #​381, #​387, #​397)

Full Changelog: crossplane-contrib/provider-helm@v1.4.0...v1.5.0


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@coderabbitai

coderabbitai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 12a283bd-6ee6-428e-bd64-3cd30258e60b

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
  • Autofix · 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.

@patrickleet
patrickleet merged commit 7cac06a into main Oct 16, 2026
14 checks passed
@renovate
renovate Bot deleted the renovate/local-start-crossplane-bootstrap branch October 16, 2026 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant