From e197aba625b38fdb6ba5a6f62cffa2366a897823 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Mon, 27 Apr 2026 03:21:45 +0500 Subject: [PATCH 1/3] feat(operator): make HelmRelease Interval configurable, override to 30s in E2E Adds --helmrelease-interval (default 5m, matching today's behaviour) to the operator and a corresponding cozystackOperator.helmReleaseInterval value on the cozy-installer chart (default empty -> no flag rendered -> operator default 5m applies). E2E install sets it to 30s. Rationale: with the 5m default, dependency- blocked HRs (waiting on cert-manager webhooks, CRDs) are requeued only every 5 min, producing an 8-10 min dead zone in the E2E install where the fast pack of HRs is Ready but the slow tail hasn't been retried yet. 30s collapses the gap. Production defaults are unchanged. The override is opt-in per install. Signed-off-by: Myasnikov Daniil --- cmd/cozystack-operator/main.go | 16 ++++++++++++++-- hack/e2e-install-cozystack.bats | 1 + internal/operator/package_reconciler.go | 6 ++++-- .../installer/templates/cozystack-operator.yaml | 3 +++ packages/core/installer/values.yaml | 3 +++ 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/cmd/cozystack-operator/main.go b/cmd/cozystack-operator/main.go index e215b779..01ce097f 100644 --- a/cmd/cozystack-operator/main.go +++ b/cmd/cozystack-operator/main.go @@ -83,6 +83,7 @@ func main() { var disableTelemetry bool var telemetryEndpoint string var telemetryInterval string + var helmReleaseInterval string var cozyValuesSecretName string var cozyValuesSecretNamespace string var cozyValuesNamespaceSelector string @@ -107,6 +108,10 @@ func main() { "Endpoint for sending telemetry data") flag.StringVar(&telemetryInterval, "telemetry-interval", "15m", "Interval between telemetry data collection (e.g. 15m, 1h)") + flag.StringVar(&helmReleaseInterval, "helmrelease-interval", "5m", + "Reconcile interval applied to HelmReleases created by the Package reconciler. "+ + "Lower values speed up dependency-blocked retries (e.g. during E2E install) at the cost of "+ + "controller load. Production default 5m matches existing behaviour.") flag.StringVar(&platformSourceURL, "platform-source-url", "", "Platform source URL (oci:// or https://). If specified, generates OCIRepository or GitRepository resource.") flag.StringVar(&platformSourceName, "platform-source-name", "cozystack-platform", "Name for the generated platform source resource and PackageSource") flag.StringVar(&platformSourceRef, "platform-source-ref", "", "Reference specification as key=value pairs (e.g., 'branch=main' or 'digest=sha256:...,tag=v1.0'). For OCI: digest, semver, semverFilter, tag. For Git: branch, tag, semver, name, commit.") @@ -122,6 +127,12 @@ func main() { ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + hrIntervalDuration, err := time.ParseDuration(helmReleaseInterval) + if err != nil { + setupLog.Error(err, "invalid --helmrelease-interval value", "value", helmReleaseInterval) + os.Exit(1) + } + config := ctrl.GetConfigOrDie() // Create a direct client (without cache) for pre-start operations @@ -258,8 +269,9 @@ func main() { // Setup Package reconciler if err := (&operator.PackageReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + HelmReleaseInterval: hrIntervalDuration, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Package") os.Exit(1) diff --git a/hack/e2e-install-cozystack.bats b/hack/e2e-install-cozystack.bats index cf66b73f..3a02fc8c 100644 --- a/hack/e2e-install-cozystack.bats +++ b/hack/e2e-install-cozystack.bats @@ -13,6 +13,7 @@ --install \ --namespace cozy-system \ --create-namespace \ + --set cozystackOperator.helmReleaseInterval=30s \ --wait \ --timeout 2m diff --git a/internal/operator/package_reconciler.go b/internal/operator/package_reconciler.go index 0e724e49..d7cd1151 100644 --- a/internal/operator/package_reconciler.go +++ b/internal/operator/package_reconciler.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "strings" + "time" cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" helmv2 "github.com/fluxcd/helm-controller/api/v2" @@ -58,7 +59,8 @@ func parseCRDPolicy(install *cozyv1alpha1.ComponentInstall) helmv2.CRDsPolicy { // PackageReconciler reconciles Package resources type PackageReconciler struct { client.Client - Scheme *runtime.Scheme + Scheme *runtime.Scheme + HelmReleaseInterval time.Duration } // +kubebuilder:rbac:groups=cozystack.io,resources=packages,verbs=get;list;watch;create;update;patch;delete @@ -214,7 +216,7 @@ func (r *PackageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct Labels: labels, }, Spec: helmv2.HelmReleaseSpec{ - Interval: metav1.Duration{Duration: 5 * 60 * 1000000000}, // 5m + Interval: metav1.Duration{Duration: r.HelmReleaseInterval}, ChartRef: &helmv2.CrossNamespaceSourceReference{ Kind: "ExternalArtifact", Name: artifactName, diff --git a/packages/core/installer/templates/cozystack-operator.yaml b/packages/core/installer/templates/cozystack-operator.yaml index aded0995..8389aca4 100644 --- a/packages/core/installer/templates/cozystack-operator.yaml +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -68,6 +68,9 @@ spec: {{- if .Values.cozystackOperator.disableTelemetry }} - --disable-telemetry {{- end }} + {{- if .Values.cozystackOperator.helmReleaseInterval }} + - --helmrelease-interval={{ .Values.cozystackOperator.helmReleaseInterval }} + {{- end }} - --platform-source-name=cozystack-platform - --platform-source-url={{ .Values.cozystackOperator.platformSourceUrl }} {{- if .Values.cozystackOperator.platformSourceRef }} diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index eef691ea..f31400ec 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -4,6 +4,9 @@ cozystackOperator: image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.3.0@sha256:62574f12486bb40c901cf5ed484cca264405ce5810196d86555cbb27cce1ba48 platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages' platformSourceRef: 'digest=sha256:a0b9ef938446b3132d3d22ad2262beb1027c48c9037b6c2346fdc2f19acd3036' + # When non-empty, overrides the operator's --helmrelease-interval flag + # (operator default: 5m). E2E sets this to 30s; production should leave empty. + helmReleaseInterval: "" # Generic variant configuration (only used when cozystackOperator.variant=generic) cozystack: # Kubernetes API server host (IP only, no protocol/port) From c4c70e0e18063a58e5ac4b4daa1901996aa95087 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 28 Apr 2026 12:40:13 +0500 Subject: [PATCH 2/3] feat(operator): switch HelmRelease retries to Strategy=RetryOnFailure with configurable RetryInterval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The operator generated HelmReleases with `Install.Remediation{Retries: -1}` and `Upgrade.Remediation{Retries: -1}`, which is the v2 way of saying "retry forever, never remediate". Combined with the spec-level `Interval: 5m`, this meant a failed install/upgrade waited a full 5 minutes before the next attempt — even when the underlying race that caused the failure (e.g. a chart artifact not quite Ready, a HR dependency reconciling out of order) cleared in seconds. Audit of the last 30 successful PR runs found that every single run had its `Install Cozystack` step's `kubectl wait hr/seaweedfs-system` time out at the 2-minute mark and recover only after an inline `flux reconcile --force` workaround. Same root cause: 5-minute retry interval was longer than the test's wait window. That workaround can be removed once this change lands. Switch to `Strategy.Name=RetryOnFailure` with `RetryInterval` exposed via a new `--helmrelease-retry-interval` operator flag (default 30s): - Functionally equivalent to the previous configuration ("retry forever on failure") since `Retries: -1` meant remediation never fired anyway. - Decouples retry-on-failure timing from `spec.Interval` so failed releases recover at 30s without polling healthy releases at the same cadence (which would multiply controller load by ~10x for no benefit). - Helm chart exposes `cozystackOperator.helmReleaseRetryInterval`, defaulting to empty (operator uses its own 30s default). - Same flag pattern as the existing `--helmrelease-interval` (5m default), also added in this PR via cherry-pick. Verified: go build green, go test ./internal/operator/... pass, helm template renders both flags conditionally on values. Signed-off-by: Myasnikov Daniil --- cmd/cozystack-operator/main.go | 19 ++++++++++++--- internal/operator/package_reconciler.go | 24 ++++++++++++------- .../templates/cozystack-operator.yaml | 3 +++ packages/core/installer/values.yaml | 4 ++++ 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/cmd/cozystack-operator/main.go b/cmd/cozystack-operator/main.go index 01ce097f..fcc8b1b1 100644 --- a/cmd/cozystack-operator/main.go +++ b/cmd/cozystack-operator/main.go @@ -84,6 +84,7 @@ func main() { var telemetryEndpoint string var telemetryInterval string var helmReleaseInterval string + var helmReleaseRetryInterval string var cozyValuesSecretName string var cozyValuesSecretNamespace string var cozyValuesNamespaceSelector string @@ -112,6 +113,12 @@ func main() { "Reconcile interval applied to HelmReleases created by the Package reconciler. "+ "Lower values speed up dependency-blocked retries (e.g. during E2E install) at the cost of "+ "controller load. Production default 5m matches existing behaviour.") + flag.StringVar(&helmReleaseRetryInterval, "helmrelease-retry-interval", "30s", + "Retry interval applied to Install.Strategy and Upgrade.Strategy of HelmReleases created "+ + "by the Package reconciler. With Strategy.Name=RetryOnFailure, this controls how long the "+ + "controller waits between failed install/upgrade attempts. Decoupled from --helmrelease-interval "+ + "(which is the healthy reconcile cadence) so failures recover fast without polling healthy "+ + "releases at the same fast cadence.") flag.StringVar(&platformSourceURL, "platform-source-url", "", "Platform source URL (oci:// or https://). If specified, generates OCIRepository or GitRepository resource.") flag.StringVar(&platformSourceName, "platform-source-name", "cozystack-platform", "Name for the generated platform source resource and PackageSource") flag.StringVar(&platformSourceRef, "platform-source-ref", "", "Reference specification as key=value pairs (e.g., 'branch=main' or 'digest=sha256:...,tag=v1.0'). For OCI: digest, semver, semverFilter, tag. For Git: branch, tag, semver, name, commit.") @@ -132,6 +139,11 @@ func main() { setupLog.Error(err, "invalid --helmrelease-interval value", "value", helmReleaseInterval) os.Exit(1) } + hrRetryIntervalDuration, err := time.ParseDuration(helmReleaseRetryInterval) + if err != nil { + setupLog.Error(err, "invalid --helmrelease-retry-interval value", "value", helmReleaseRetryInterval) + os.Exit(1) + } config := ctrl.GetConfigOrDie() @@ -269,9 +281,10 @@ func main() { // Setup Package reconciler if err := (&operator.PackageReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - HelmReleaseInterval: hrIntervalDuration, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + HelmReleaseInterval: hrIntervalDuration, + HelmReleaseRetryInterval: hrRetryIntervalDuration, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Package") os.Exit(1) diff --git a/internal/operator/package_reconciler.go b/internal/operator/package_reconciler.go index d7cd1151..e2dac1cf 100644 --- a/internal/operator/package_reconciler.go +++ b/internal/operator/package_reconciler.go @@ -59,8 +59,9 @@ func parseCRDPolicy(install *cozyv1alpha1.ComponentInstall) helmv2.CRDsPolicy { // PackageReconciler reconciles Package resources type PackageReconciler struct { client.Client - Scheme *runtime.Scheme - HelmReleaseInterval time.Duration + Scheme *runtime.Scheme + HelmReleaseInterval time.Duration + HelmReleaseRetryInterval time.Duration } // +kubebuilder:rbac:groups=cozystack.io,resources=packages,verbs=get;list;watch;create;update;patch;delete @@ -223,15 +224,22 @@ func (r *PackageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct Namespace: "cozy-system", }, Install: &helmv2.Install{ - Timeout: &metav1.Duration{Duration: 10 * 60 * 1000000000}, // 10m - Remediation: &helmv2.InstallRemediation{ - Retries: -1, + Timeout: &metav1.Duration{Duration: 10 * time.Minute}, + // Strategy=RetryOnFailure (with RetryInterval) replaces the previous + // Remediation{Retries:-1} setup. Functionally equivalent ("retry forever + // on failure"), but decouples retry timing from spec.Interval so failed + // installs recover at HelmReleaseRetryInterval (default 30s) without + // also polling healthy releases at the same fast cadence. + Strategy: &helmv2.InstallStrategy{ + Name: string(helmv2.ActionStrategyRetryOnFailure), + RetryInterval: &metav1.Duration{Duration: r.HelmReleaseRetryInterval}, }, }, Upgrade: &helmv2.Upgrade{ - Timeout: &metav1.Duration{Duration: 10 * 60 * 1000000000}, // 10m - Remediation: &helmv2.UpgradeRemediation{ - Retries: -1, + Timeout: &metav1.Duration{Duration: 10 * time.Minute}, + Strategy: &helmv2.UpgradeStrategy{ + Name: string(helmv2.ActionStrategyRetryOnFailure), + RetryInterval: &metav1.Duration{Duration: r.HelmReleaseRetryInterval}, }, CRDs: parseCRDPolicy(component.Install), }, diff --git a/packages/core/installer/templates/cozystack-operator.yaml b/packages/core/installer/templates/cozystack-operator.yaml index 8389aca4..72ea105c 100644 --- a/packages/core/installer/templates/cozystack-operator.yaml +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -71,6 +71,9 @@ spec: {{- if .Values.cozystackOperator.helmReleaseInterval }} - --helmrelease-interval={{ .Values.cozystackOperator.helmReleaseInterval }} {{- end }} + {{- if .Values.cozystackOperator.helmReleaseRetryInterval }} + - --helmrelease-retry-interval={{ .Values.cozystackOperator.helmReleaseRetryInterval }} + {{- end }} - --platform-source-name=cozystack-platform - --platform-source-url={{ .Values.cozystackOperator.platformSourceUrl }} {{- if .Values.cozystackOperator.platformSourceRef }} diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index f31400ec..b6c93ecd 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -7,6 +7,10 @@ cozystackOperator: # When non-empty, overrides the operator's --helmrelease-interval flag # (operator default: 5m). E2E sets this to 30s; production should leave empty. helmReleaseInterval: "" + # When non-empty, overrides the operator's --helmrelease-retry-interval flag + # (operator default: 30s). Controls how fast failed install/upgrade attempts + # are retried. Decoupled from helmReleaseInterval (healthy reconcile cadence). + helmReleaseRetryInterval: "" # Generic variant configuration (only used when cozystackOperator.variant=generic) cozystack: # Kubernetes API server host (IP only, no protocol/port) From e410b02e1df79b03f025c00bdbdd7cf52df7397d Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 28 Apr 2026 12:44:06 +0500 Subject: [PATCH 3/3] feat(operator): expose Install/Upgrade timeouts and MaxHistory as flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HelmRelease generation in package_reconciler hardcoded three values that have caused recurring pain: - Install.Timeout = 10m (line 214) - Upgrade.Timeout = 10m (line 220) - MaxHistory = unset (Helm default 5) The 10m timeout in particular contradicts the per-Application timeout work done in `pkg/config/config.go` and `pkg/registry/apps/application/rest.go` (commit 7b146cbe), which lets individual Applications override the install/upgrade timeout via annotation. The PackageSource side had no equivalent — etcd works around it by hand-rolling its HR YAML in `packages/apps/tenant/templates/etcd.yaml` to set a 30m timeout, harbor has had multiple commits chasing the same problem from the other side. This closes that gap. New operator flags (each maps to a chart value with empty default; operator uses its own default when value is empty): - `--helmrelease-install-timeout` (default 10m) → Spec.Install.Timeout - `--helmrelease-upgrade-timeout` (default 10m) → Spec.Upgrade.Timeout - `--helmrelease-max-history` (default 5) → Spec.MaxHistory Production behaviour unchanged. E2E and edge cases (cert rotation, slow-installing charts) can override per-cluster without modifying chart manifests. Per-component overrides on `ComponentInstall` (mirroring the existing `UpgradeCRDs` pattern) would be a strictly better fix for charts like etcd that need an outlier timeout — left as a deliberate followup since it touches the Package CR API. Signed-off-by: Myasnikov Daniil --- cmd/cozystack-operator/main.go | 40 +++++++++++++++++-- internal/operator/package_reconciler.go | 16 +++++--- .../templates/cozystack-operator.yaml | 9 +++++ packages/core/installer/values.yaml | 12 ++++++ 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/cmd/cozystack-operator/main.go b/cmd/cozystack-operator/main.go index fcc8b1b1..08f7a860 100644 --- a/cmd/cozystack-operator/main.go +++ b/cmd/cozystack-operator/main.go @@ -85,6 +85,9 @@ func main() { var telemetryInterval string var helmReleaseInterval string var helmReleaseRetryInterval string + var helmReleaseInstallTimeout string + var helmReleaseUpgradeTimeout string + var helmReleaseMaxHistory int var cozyValuesSecretName string var cozyValuesSecretNamespace string var cozyValuesNamespaceSelector string @@ -119,6 +122,18 @@ func main() { "controller waits between failed install/upgrade attempts. Decoupled from --helmrelease-interval "+ "(which is the healthy reconcile cadence) so failures recover fast without polling healthy "+ "releases at the same fast cadence.") + flag.StringVar(&helmReleaseInstallTimeout, "helmrelease-install-timeout", "10m", + "Timeout for the Helm install action of HelmReleases created by the Package reconciler "+ + "(Spec.Install.Timeout). Bounds how long an individual Kubernetes operation (Job/hook/wait) "+ + "may take during install.") + flag.StringVar(&helmReleaseUpgradeTimeout, "helmrelease-upgrade-timeout", "10m", + "Timeout for the Helm upgrade action of HelmReleases created by the Package reconciler "+ + "(Spec.Upgrade.Timeout). Bounds how long an individual Kubernetes operation (Job/hook/wait) "+ + "may take during upgrade.") + flag.IntVar(&helmReleaseMaxHistory, "helmrelease-max-history", 5, + "Number of release revisions Helm keeps for HelmReleases created by the Package reconciler "+ + "(Spec.MaxHistory). 0 means unlimited; 5 matches Helm's default. Lower values reduce "+ + "per-release Secret accumulation in clusters that bounce HRs frequently (e.g. E2E sandboxes).") flag.StringVar(&platformSourceURL, "platform-source-url", "", "Platform source URL (oci:// or https://). If specified, generates OCIRepository or GitRepository resource.") flag.StringVar(&platformSourceName, "platform-source-name", "cozystack-platform", "Name for the generated platform source resource and PackageSource") flag.StringVar(&platformSourceRef, "platform-source-ref", "", "Reference specification as key=value pairs (e.g., 'branch=main' or 'digest=sha256:...,tag=v1.0'). For OCI: digest, semver, semverFilter, tag. For Git: branch, tag, semver, name, commit.") @@ -144,6 +159,20 @@ func main() { setupLog.Error(err, "invalid --helmrelease-retry-interval value", "value", helmReleaseRetryInterval) os.Exit(1) } + hrInstallTimeoutDuration, err := time.ParseDuration(helmReleaseInstallTimeout) + if err != nil { + setupLog.Error(err, "invalid --helmrelease-install-timeout value", "value", helmReleaseInstallTimeout) + os.Exit(1) + } + hrUpgradeTimeoutDuration, err := time.ParseDuration(helmReleaseUpgradeTimeout) + if err != nil { + setupLog.Error(err, "invalid --helmrelease-upgrade-timeout value", "value", helmReleaseUpgradeTimeout) + os.Exit(1) + } + if helmReleaseMaxHistory < 0 { + setupLog.Error(fmt.Errorf("--helmrelease-max-history must be >= 0"), "invalid value", "value", helmReleaseMaxHistory) + os.Exit(1) + } config := ctrl.GetConfigOrDie() @@ -281,10 +310,13 @@ func main() { // Setup Package reconciler if err := (&operator.PackageReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - HelmReleaseInterval: hrIntervalDuration, - HelmReleaseRetryInterval: hrRetryIntervalDuration, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + HelmReleaseInterval: hrIntervalDuration, + HelmReleaseRetryInterval: hrRetryIntervalDuration, + HelmReleaseInstallTimeout: hrInstallTimeoutDuration, + HelmReleaseUpgradeTimeout: hrUpgradeTimeoutDuration, + HelmReleaseMaxHistory: helmReleaseMaxHistory, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Package") os.Exit(1) diff --git a/internal/operator/package_reconciler.go b/internal/operator/package_reconciler.go index e2dac1cf..d13cce33 100644 --- a/internal/operator/package_reconciler.go +++ b/internal/operator/package_reconciler.go @@ -59,9 +59,12 @@ func parseCRDPolicy(install *cozyv1alpha1.ComponentInstall) helmv2.CRDsPolicy { // PackageReconciler reconciles Package resources type PackageReconciler struct { client.Client - Scheme *runtime.Scheme - HelmReleaseInterval time.Duration - HelmReleaseRetryInterval time.Duration + Scheme *runtime.Scheme + HelmReleaseInterval time.Duration + HelmReleaseRetryInterval time.Duration + HelmReleaseInstallTimeout time.Duration + HelmReleaseUpgradeTimeout time.Duration + HelmReleaseMaxHistory int } // +kubebuilder:rbac:groups=cozystack.io,resources=packages,verbs=get;list;watch;create;update;patch;delete @@ -217,14 +220,15 @@ func (r *PackageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct Labels: labels, }, Spec: helmv2.HelmReleaseSpec{ - Interval: metav1.Duration{Duration: r.HelmReleaseInterval}, + Interval: metav1.Duration{Duration: r.HelmReleaseInterval}, + MaxHistory: &r.HelmReleaseMaxHistory, ChartRef: &helmv2.CrossNamespaceSourceReference{ Kind: "ExternalArtifact", Name: artifactName, Namespace: "cozy-system", }, Install: &helmv2.Install{ - Timeout: &metav1.Duration{Duration: 10 * time.Minute}, + Timeout: &metav1.Duration{Duration: r.HelmReleaseInstallTimeout}, // Strategy=RetryOnFailure (with RetryInterval) replaces the previous // Remediation{Retries:-1} setup. Functionally equivalent ("retry forever // on failure"), but decouples retry timing from spec.Interval so failed @@ -236,7 +240,7 @@ func (r *PackageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct }, }, Upgrade: &helmv2.Upgrade{ - Timeout: &metav1.Duration{Duration: 10 * time.Minute}, + Timeout: &metav1.Duration{Duration: r.HelmReleaseUpgradeTimeout}, Strategy: &helmv2.UpgradeStrategy{ Name: string(helmv2.ActionStrategyRetryOnFailure), RetryInterval: &metav1.Duration{Duration: r.HelmReleaseRetryInterval}, diff --git a/packages/core/installer/templates/cozystack-operator.yaml b/packages/core/installer/templates/cozystack-operator.yaml index 72ea105c..39d51210 100644 --- a/packages/core/installer/templates/cozystack-operator.yaml +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -74,6 +74,15 @@ spec: {{- if .Values.cozystackOperator.helmReleaseRetryInterval }} - --helmrelease-retry-interval={{ .Values.cozystackOperator.helmReleaseRetryInterval }} {{- end }} + {{- if .Values.cozystackOperator.helmReleaseInstallTimeout }} + - --helmrelease-install-timeout={{ .Values.cozystackOperator.helmReleaseInstallTimeout }} + {{- end }} + {{- if .Values.cozystackOperator.helmReleaseUpgradeTimeout }} + - --helmrelease-upgrade-timeout={{ .Values.cozystackOperator.helmReleaseUpgradeTimeout }} + {{- end }} + {{- if .Values.cozystackOperator.helmReleaseMaxHistory }} + - --helmrelease-max-history={{ .Values.cozystackOperator.helmReleaseMaxHistory }} + {{- end }} - --platform-source-name=cozystack-platform - --platform-source-url={{ .Values.cozystackOperator.platformSourceUrl }} {{- if .Values.cozystackOperator.platformSourceRef }} diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index b6c93ecd..dcd0a913 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -11,6 +11,18 @@ cozystackOperator: # (operator default: 30s). Controls how fast failed install/upgrade attempts # are retried. Decoupled from helmReleaseInterval (healthy reconcile cadence). helmReleaseRetryInterval: "" + # When non-empty, overrides the operator's --helmrelease-install-timeout flag + # (operator default: 10m). Bounds individual k8s operations (job/hook/wait) + # during Helm install. Charts with long-running install hooks may need longer. + helmReleaseInstallTimeout: "" + # When non-empty, overrides the operator's --helmrelease-upgrade-timeout flag + # (operator default: 10m). Same semantics as helmReleaseInstallTimeout, for + # the Helm upgrade action. + helmReleaseUpgradeTimeout: "" + # When non-empty, overrides the operator's --helmrelease-max-history flag + # (operator default: 5). Number of release revisions Helm keeps per HR. + # Use 0 for unlimited; lower values reduce Secret accumulation in test clusters. + helmReleaseMaxHistory: "" # Generic variant configuration (only used when cozystackOperator.variant=generic) cozystack: # Kubernetes API server host (IP only, no protocol/port)