Skip to content

feat(operator): add per-package upgradeCRDs policy for HelmRelease - #2427

Merged
Aleksei Sviridkin (lexfrei) merged 4 commits into
mainfrom
worktree-tranquil-conjuring-moore
Apr 27, 2026
Merged

Aleksei Sviridkin (lexfrei) merged 4 commits into
mainfrom
worktree-tranquil-conjuring-moore

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Add an opt-in upgradeCRDs field to ComponentInstall in PackageSource. The field maps directly to HelmRelease.Spec.Upgrade.CRDs so a component author can declare how Flux should handle CRDs from the chart's crds/ directory when the release is upgraded.

The helm-controller default on upgrade is Skip, which means CRDs added by a chart bump never reach clusters that already have the release installed — they must be applied manually with kubectl apply --filename charts/.../crds/. This surfaces on every upgrade of an operator whose CRD set expands between versions (etcd-operator, cnpg, kubevirt, kamaji, etc.).

Setting upgradeCRDs: CreateReplace lets Flux apply new CRDs declaratively with the chart.

Values are restricted to Skip, Create, CreateReplace via a kubebuilder enum marker. Empty / unset preserves the existing helm-controller default, so every current PackageSource keeps working unchanged.

Migration is out of scope here — follow-ups will opt individual packages in case-by-case.

Relation to existing CRD management approach

The project convention (per #377) is to extract CRDs into a dedicated Helm chart that reconciles ahead of the operator chart. This PR does not replace that pattern — it complements it for charts that keep CRDs inline under charts/<name>/crds/ where extraction isn't practical (vendored upstream charts with tightly coupled CRDs). Packages that already split CRDs out can leave upgradeCRDs unset and keep using their existing separate chart.

Release note

feat(operator): add opt-in `upgradeCRDs` field to `PackageSource` component `install` block to control how CRDs from the chart's `crds/` directory are applied on HelmRelease upgrades (`Skip` by default; use `CreateReplace` for operators whose CRD set expands between versions).

Summary by CodeRabbit

  • New Features

    • Configurable CRD upgrade policy for component installs: Skip, Create, CreateReplace — controls CRD handling during package upgrades and preserves controller default when unset.
  • Documentation

    • Guidance on CRD upgrade semantics, advice to use CreateReplace for specific operators, and warning about potential data-loss risks; clarified contributor scope examples and PR template guidance.
  • Tests

    • Added tests validating CRD policy parsing and presence of the CRD policy enum in the published schema.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a mechanism to manage CRD upgrades for Helm releases within the Cozystack operator. By adding an opt-in upgradeCRDs field, component authors can now declaratively specify how CRDs should be handled during upgrades, addressing issues where CRDs from updated charts were not being applied to existing clusters. The change maintains backward compatibility by defaulting to the existing behavior when the field is unset.

Highlights

  • New Configuration Field: Added an optional upgradeCRDs field to the ComponentInstall struct in PackageSource to allow per-package control over CRD handling during Helm releases.
  • Policy Mapping: Implemented parseCRDPolicy to map the new configuration to Flux's helmv2.CRDsPolicy, ensuring compatibility with existing helm-controller defaults.
  • Documentation and Testing: Updated documentation to explain the new policy and added comprehensive unit tests to verify policy parsing and CRD schema validation.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@dosubot dosubot Bot added the kind/feature Categorizes issue or PR as related to a new feature label Apr 20, 2026
@coderabbitai

coderabbitai Bot commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds an optional upgradeCRDs enum field to ComponentInstall, exposes it in the PackageSource CRD schema and docs, maps it into HelmRelease.Spec.Upgrade.CRDs in the reconciler, and adds unit tests verifying parsing and CRD schema enum presence.

Changes

Cohort / File(s) Summary
API Types
api/v1alpha1/packagesource_types.go
Add UpgradeCRDs string field to ComponentInstall with kubebuilder/OpenAPI enum validation: Skip, Create, CreateReplace.
CRD Manifest
internal/crdinstall/manifests/cozystack.io_packagesources.yaml
Extend PackageSource CRD schema: add spec.variants[].components[].install.upgradeCRDs (string enum) with descriptive text mapping to HelmRelease.Spec.Upgrade.CRDs and default behavior note.
Reconciler Logic
internal/operator/package_reconciler.go
Add parseCRDPolicy helper and set HelmRelease.Spec.Upgrade.CRDs from component.Install.UpgradeCRDs when constructing/updating HelmRelease.
Tests
internal/operator/package_reconciler_test.go
Add tests: TestParseCRDPolicy (validates mapping to helmv2 CRDs policy constants) and TestPackageSourceCRDHasUpgradeCRDsEnum (ensures CRD manifest includes the enum).
Documentation
docs/agents/overview.md
Document install.upgradeCRDs option, allowed values and semantics, operator-specific guidance, and caution about CreateReplace risks.
Contributor Guidance
docs/agents/contributing.md, .github/PULL_REQUEST_TEMPLATE.md
Revise contributing/PR guidance to prefer the most specific Conventional Commit scope and add operator example in system scopes.

Sequence Diagram(s)

sequenceDiagram
    participant User as PackageSource spec
    participant Reconciler as Operator Reconciler
    participant K8s as Kubernetes API
    participant HelmCtrl as helm-controller (helmv2)
    User->>Reconciler: ComponentInstall with install.upgradeCRDs
    Reconciler->>Reconciler: parseCRDPolicy(UpgradeCRDs)
    Reconciler->>K8s: Apply/Update HelmRelease (Spec.Upgrade.CRDs set)
    HelmCtrl->>K8s: On HelmRelease upgrade, apply CRDs per Spec.Upgrade.CRDs
    K8s-->>HelmCtrl: CRD create/replace/skip responses
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through types and CRD fields bright,
I nudged upgrades to skip, create, or rewrite,
Tests peered under leaves to check my trail,
Docs waved carrots telling which path to sail,
A tiny change, a gentle patch, a rabbit’s code exhale. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding a per-package upgradeCRDs policy for HelmRelease, which matches the primary feature implementation across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-tranquil-conjuring-moore

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 and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the UpgradeCRDs field to the PackageSource API, allowing users to specify how CRDs are handled during Helm upgrades with options for Skip, Create, or CreateReplace. The implementation includes updates to the API types, documentation, generated manifests, and the operator's reconciliation logic. Review feedback identifies a violation of the repository's conventional commit scope requirements and suggests a minor code simplification in the reconciler to remove a redundant nil check.

// loss if upstream drops fields from a CRD with live objects.
// +optional
// +kubebuilder:validation:Enum=Skip;Create;CreateReplace
UpgradeCRDs string `json:"upgradeCRDs,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The commit message uses the scope operator, which is not listed as a valid scope in the repository style guide (lines 42-45). Please consider using a valid meta scope like api or a system scope like platform, or update the style guide if operator is intended to be a permanent scope.

References
  1. The repository style guide defines a specific list of valid scopes for conventional commits. (link)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Scopes section in docs/agents/contributing.md was intended as a list of examples, not an exhaustive enumeration. Reworded the section to make that explicit and added operator as an example scope (e52cf38). Contributors should pick the most specific scope and extend the list when a genuinely new area appears.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also mirrored the same wording in .github/PULL_REQUEST_TEMPLATE.md for consistency with human contributors (57e4859).

// Empty / nil preserves the helm-controller default (Skip on upgrade);
// the CRD enum marker restricts the string to Skip/Create/CreateReplace.
func parseCRDPolicy(install *cozyv1alpha1.ComponentInstall) helmv2.CRDsPolicy {
if install == nil || install.UpgradeCRDs == "" {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The install == nil check is redundant here because the only caller of parseCRDPolicy (at line 234) is inside a loop where component.Install has already been verified as non-nil (at line 162). While safe, it can be simplified for clarity.

Suggested change
if install == nil || install.UpgradeCRDs == "" {
if install.UpgradeCRDs == "" {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the install == nil branch deliberately. While the sole current caller in Reconcile guards the nil via if component.Install == nil { continue }, treating parseCRDPolicy as safe for nil input keeps the helper independently correct. A future caller that skips the outer guard would otherwise panic on a deref with no hint that the helper relied on an external invariant — two lines of defensive code are a cheap price for that decoupling.

@dosubot dosubot Bot added size/M This PR changes 30-99 lines, ignoring generated files and removed size/S This PR changes 10-29 lines, ignoring generated files labels Apr 20, 2026
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Apr 27, 2026
Add an opt-in UpgradeCRDs field to ComponentInstall that maps to
HelmRelease.Spec.Upgrade.CRDs, allowing a PackageSource component to
declare how Flux should handle CRDs from the chart's crds/ directory
on upgrade.

The helm-controller default on upgrade is Skip, which means new CRDs
added between chart versions never reach existing clusters and must be
applied manually. Setting upgradeCRDs: CreateReplace makes Flux apply
new CRDs declaratively with the chart.

Allowed values are restricted to Skip, Create, CreateReplace via a
kubebuilder enum marker. Empty / unset preserves the existing Flux
default, so all existing PackageSource resources keep working.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Describe when to set upgradeCRDs: CreateReplace (operators that evolve
their CRD set additively between versions) and the data-loss risk of
enabling it on operators that drop fields.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
The Scopes section was read as an exhaustive enumeration, which led to
review feedback flagging any scope outside the list as invalid. The
intent has always been that contributors pick the most specific scope
for the change and extend the list when a genuinely new area appears.
Reword the section accordingly and add operator as an example scope.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
Match the wording adopted in docs/agents/contributing.md so that human
contributors and AI agents see the same guidance in both places.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/operator/package_reconciler_test.go (1)

78-138: Solid guard against silent CRD-schema drift.

Walking the OpenAPI schema down to spec.variants.items.components.items.install.upgradeCRDs and asserting all three enum entries effectively catches an accidental removal of the kubebuilder marker followed by a CRD regeneration. The t.Fatal on missing field plus per-value t.Errorf give clear failure messages.

One minor nit (optional): the loop over crd.Spec.Versions continues past any version missing the field, so if v1alpha1 ever ships alongside another version that lacks upgradeCRDs, the test would still pass via whichever version has it. Not actionable today (only v1alpha1 exists), just noting for future-proofing — pinning to v.Name == "v1alpha1" would make intent explicit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/operator/package_reconciler_test.go` around lines 78 - 138,
TestPackageSourceCRDHasUpgradeCRDsEnum currently scans all crd.Spec.Versions and
accepts the first version that contains the upgradeCRDs field, which could mask
cases where other versions lack it; narrow the search to the intended version by
only inspecting versions with v.Name == "v1alpha1" (or explicitly the desired
version string) inside the loop over crd.Spec.Versions so the assertion targets
that specific API version (update the loop/filter around crd.Spec.Versions and
the logic that finds spec.Properties["spec"] ...
install.Properties["upgradeCRDs"] accordingly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/operator/package_reconciler_test.go`:
- Around line 78-138: TestPackageSourceCRDHasUpgradeCRDsEnum currently scans all
crd.Spec.Versions and accepts the first version that contains the upgradeCRDs
field, which could mask cases where other versions lack it; narrow the search to
the intended version by only inspecting versions with v.Name == "v1alpha1" (or
explicitly the desired version string) inside the loop over crd.Spec.Versions so
the assertion targets that specific API version (update the loop/filter around
crd.Spec.Versions and the logic that finds spec.Properties["spec"] ...
install.Properties["upgradeCRDs"] accordingly).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8c46aaf4-4902-4180-95f9-2ed7f859e9b1

📥 Commits

Reviewing files that changed from the base of the PR and between 57e4859 and 32ae993.

📒 Files selected for processing (7)
  • .github/PULL_REQUEST_TEMPLATE.md
  • api/v1alpha1/packagesource_types.go
  • docs/agents/contributing.md
  • docs/agents/overview.md
  • internal/crdinstall/manifests/cozystack.io_packagesources.yaml
  • internal/operator/package_reconciler.go
  • internal/operator/package_reconciler_test.go
✅ Files skipped from review due to trivial changes (3)
  • docs/agents/contributing.md
  • .github/PULL_REQUEST_TEMPLATE.md
  • docs/agents/overview.md

@lexfrei
Aleksei Sviridkin (lexfrei) merged commit 62d2516 into main Apr 27, 2026
7 checks passed
@lexfrei
Aleksei Sviridkin (lexfrei) deleted the worktree-tranquil-conjuring-moore branch April 27, 2026 09:31
Andrei Kvapil (kvaps) added a commit that referenced this pull request May 1, 2026
…2428)

## What this PR does

Bumps the vendored `etcd-operator` chart from `v0.4.2` to `v0.4.3` and
wires the new backup machinery through the cozystack `Etcd` app API.

Upstream v0.4.3 ships two new CRDs — `EtcdBackup` (one-shot snapshot)
and `EtcdBackupSchedule` (periodic snapshots) — along with the
`EtcdCluster.spec.bootstrap` restore field, a new `OPERATOR_IMAGE` env
on the operator deployment, and a replacement of the deprecated
`gcr.io/kubebuilder/kube-rbac-proxy` image. The chart portion of this PR
is a clean re-vendor via `make update` — no local patches.

On top of the bump, `packages/extra/etcd` exposes a new optional
`backup.*` values block. When `backup.enabled=true`, the chart renders
an `EtcdBackupSchedule` referencing the platform `EtcdCluster/etcd` and
a `Secret/etcd-s3-creds` with the S3 credentials expected by the
backup-agent. The values field names (`destinationPath`, `endpointURL`,
`s3AccessKey`, `s3SecretKey`, `schedule`) mirror the existing `postgres`
backup pattern for consistency. The chart `fail`s at template render if
any of the four required S3 fields are empty or if `destinationPath` is
malformed (missing `s3://` prefix, `//` segments, no bucket).

Test coverage: 15 `helm-unittest` cases across
`tests/backup-secret_test.yaml` and
`tests/etcd-backup-schedule_test.yaml` (wired into `make test`, picked
up by `hack/helm-unit-tests.sh`), plus three bats cases in
`hack/e2e-apps/etcd.bats` covering the baseline `Etcd` app, the `backup:
{}` default-off path, and the full `backup.enabled=true` case that
verifies the `EtcdBackupSchedule` materializes and the operator
generates the downstream `CronJob`.

`packages/core/platform/sources/etcd-operator.yaml` now sets
`upgradeCRDs: CreateReplace` on the etcd-operator component, so Flux
applies the new CRDs on upgrade of the chart instead of silently
skipping them.

Depends on #2427.

### Scope deferred to follow-ups

- The one-shot `EtcdBackup` CRD is vendored but not exposed through this
chart — users can still hand-apply the CRD directly.
- `EtcdCluster.spec.bootstrap` (restore from an existing snapshot) is
likewise vendored but not exposed. Both gaps are called out in
`packages/extra/etcd/README.md`.

### Release note

```release-note
feat(etcd): update etcd-operator to v0.4.3 and expose S3 backup schedules on the Etcd app. Set `backup.enabled=true` along with `backup.s3AccessKey`, `backup.s3SecretKey`, `backup.destinationPath`, and `backup.endpointURL` to schedule periodic snapshots.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Scheduled S3 backups for etcd clusters (configurable cron, generated
S3 credentials) and restore/init from S3 or PVC via new backup
resources.

* **Tests**
* New end-to-end tests for etcd lifecycle and backup scenarios; added
Helm unit tests for backup templates and validations.

* **Documentation**
  * Backup configuration guide and parameter reference added.

* **Chores**
* Operator chart version bump; RBAC, webhooks, and deployment wiring
updated; added chart test target.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
myasnikovdaniil added a commit that referenced this pull request May 5, 2026
…interval, timeouts, max-history) (#2509)

## Why

The `cozystack-operator` Package reconciler builds every `HelmRelease`
it generates with a handful of hardcoded values. Three of those have
caused recurring pain across the project:

- **`Spec.Interval = 5m`** (line 220 before this PR) — too long for E2E
install where dependent HRs reconcile out of order; previously addressed
by a manual `flux reconcile --force` workaround in
`hack/e2e-install-cozystack.bats`.
- **`Install.Remediation{Retries: -1}` + `Upgrade.Remediation{Retries:
-1}`** — semantically "retry forever, never remediate", but the
retry-on-failure timing was tied to `Spec.Interval` (5m). Audit of the
last 30 successful PR runs found that **every** run hit `kubectl wait
hr/seaweedfs-system` timeouts at the 2-min mark, recovering only after
the inline `flux reconcile --force` workaround.
- **`Install.Timeout = 10m` / `Upgrade.Timeout = 10m`** — directly
contradicts the per-Application timeout work in `pkg/config/config.go` +
`pkg/registry/apps/application/rest.go` (commit `7b146cbe`).
`packages/apps/tenant/templates/etcd.yaml` hand-rolls its HR YAML to
bypass this for cert rotation; harbor has had multiple commits chasing
the same problem.
- **`Spec.MaxHistory`** unset → Helm default 5. Test clusters bounce HRs
frequently and accumulate release-history Secrets.

This PR exposes all four as operator flags + helm chart values, with
defaults that preserve current behavior.

## What

### New operator flags

| Flag | Default | HR field |
|---|---|---|
| `--helmrelease-interval` | `5m` | `Spec.Interval` |
| `--helmrelease-retry-interval` | `30s` |
`Spec.{Install,Upgrade}.Strategy.RetryInterval` |
| `--helmrelease-install-timeout` | `10m` | `Spec.Install.Timeout` |
| `--helmrelease-upgrade-timeout` | `10m` | `Spec.Upgrade.Timeout` |
| `--helmrelease-max-history` | `5` | `Spec.MaxHistory` |

### Strategy switch

`Install.Strategy.Name` and `Upgrade.Strategy.Name` are now set to
`RetryOnFailure` (instead of leaving the default `RemediateOnFailure`),
with `RetryInterval` set from the new flag. Functionally equivalent to
the previous `Remediation{Retries:-1}` ("retry forever") since
remediation never fired anyway, but **decouples retry-on-failure timing
from `Spec.Interval`** so failed releases recover at 30s without polling
healthy releases at the same fast cadence.

### Chart exposure

`packages/core/installer/values.yaml` gets five new optional values
(`cozystackOperator.helmRelease{Interval,RetryInterval,InstallTimeout,UpgradeTimeout,MaxHistory}`),
each defaulting to empty so the operator's own default applies. The
chart conditionally renders the corresponding flag only when the value
is set.

## Production safety

- All static defaults match the previous hardcoded values (5m / 10m /
10m / 5). No new code path runs until an admin explicitly sets a flag.
- **One intentional behavior change: failed-release retry cadence**
drops from ~5m (previously coupled to `Spec.Interval`) to 30s
(`RetryInterval` default). This is the whole motivation for the strategy
switch — the recurring `kubectl wait hr/seaweedfs-system` flakes
documented above were caused by the previous coupling. Healthy-release
reconcile cadence is unchanged at 5m in production; CI overrides this to
30s via `--set cozystackOperator.helmReleaseInterval=30s` in
`hack/e2e-install-cozystack.bats` for faster convergence on E2E
clusters.
- `RetryOnFailure` is functionally equivalent to `RemediateOnFailure +
Retries:-1` since neither ever triggers remediation.
- Empty value in the chart → flag not rendered → operator falls back to
its built-in default. (`helmReleaseMaxHistory: 0` — unlimited history
per Helm semantics — is also honored; the chart guards on `ne (toString
…) ""` rather than truthiness so int `0` is not silently dropped.)

## Verification

- `go build ./...` clean
- `go test ./internal/operator/...` passes (covers `Interval`,
`MaxHistory`, `Install/Upgrade.Timeout`, `Strategy.Name`,
`RetryInterval`, `ChartRef` propagation, plus a regression test for
`MaxHistory=0`)
- `helm template installer packages/core/installer` renders cleanly with
default and overridden values; `--set
cozystackOperator.helmReleaseMaxHistory=0` correctly emits
`--helmrelease-max-history=0`

## Followups (deliberately not bundled)

- **Per-component timeout overrides** on `ComponentInstall` (mirroring
the `UpgradeCRDs` pattern from #2427). Charts like etcd that need a 30m
timeout would set it on their `Component` rather than tuning a global
flag. Touches the Package CR API + codegen — bigger lift, separate PR.
- **`Remediation.Retries` as a flag.** No current pain; skip.
- **`createOrUpdateHelmRelease` field preservation.** Currently
preserves `Suspend` but not `Test`/`Rollback`/`Uninstall`. Would
silently overwrite an SRE's manual edit. Minor cleanliness, separate PR.

## Test plan

- [ ] CI passes E2E with `helmReleaseInterval=30s` set in install bats
(existing T1 test path)
- [ ] No regression on production install path (default values)
- [ ] HR objects in a healthy cluster render with
`Strategy.Name=RetryOnFailure` and `RetryInterval=30s`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Operator and Helm chart now accept CLI flags and Helm values to
configure HelmRelease reconciliation intervals, retry behavior,
install/upgrade timeouts, and history retention.

* **Bug Fixes**
* Operator now validates HelmRelease timing/history inputs on startup
and will exit if values are invalid or non-positive.

* **Tests**
* Added unit and e2e coverage to verify HelmRelease configuration and a
chart install path using a custom reconciliation interval.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature Categorizes issue or PR as related to a new feature lgtm This PR has been approved by a maintainer size/M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants