docs(api): clarify kind-wide Helm timeout overrides

Timeout annotations are read from an ApplicationDefinition and therefore
affect every Application of that kind. Describing them as per-Application
hides their actual scope and the upgrade override precedence.

Fixes cozystack/cozystack#4382

Assisted-by: LLM
Signed-off-by: frolov.so <opsmon.sh@yandex.com>
This commit is contained in:
frolov.so authored and Aleksei Sviridkin committed 2026-10-10 22:36:54 +03:00
1 parent f4bde9e50d
commit 9b35fa0b48
5 files changed
+43 -40

No files matched your search

+16 -16
View File
@@ -157,15 +157,15 @@ func NewCommandStartCozyServer(ctx context.Context, defaults *CozyServerOptions)
"same cadence.")
flags.StringVar(&o.HelmReleaseInstallTimeout, "helmrelease-install-timeout", o.HelmReleaseInstallTimeout,
"Default timeout for the Helm install action of HelmReleases generated from Application "+
"resources (Spec.Install.Timeout). Overridden per-Application by the "+
"release.cozystack.io/helm-install-timeout annotation on the ApplicationDefinition; the "+
"resources (Spec.Install.Timeout). Overridden for an Application kind by the "+
"release.cozystack.io/helm-install-timeout annotation on its ApplicationDefinition; the "+
"same annotation also overrides --helmrelease-upgrade-timeout.")
flags.StringVar(&o.HelmReleaseUpgradeTimeout, "helmrelease-upgrade-timeout", o.HelmReleaseUpgradeTimeout,
"Default timeout for the Helm upgrade action of HelmReleases generated from Application "+
"resources (Spec.Upgrade.Timeout). Overridden per-Application by the "+
"release.cozystack.io/helm-install-timeout annotation (which sets both install and "+
"upgrade), or by release.cozystack.io/helm-upgrade-timeout to override only the upgrade "+
"side for a kind that needs an asymmetric budget.")
"resources (Spec.Upgrade.Timeout). Overridden for an Application kind by the "+
"release.cozystack.io/helm-install-timeout annotation on its ApplicationDefinition (which "+
"sets both install and upgrade), or with higher precedence by "+
"release.cozystack.io/helm-upgrade-timeout to override only the upgrade side.")
flags.IntVar(&o.HelmReleaseMaxHistory, "helmrelease-max-history", o.HelmReleaseMaxHistory,
"Number of release revisions Helm keeps for HelmReleases generated from Application "+
"resources (Spec.MaxHistory). 0 means unlimited; 5 matches Helm's default.")
@@ -331,11 +331,11 @@ func buildResourceFromCRD(crd v1alpha1.ApplicationDefinition, hrFlags helmReleas
Name: crd.Spec.Release.ChartRef.Name,
Namespace: crd.Spec.Release.ChartRef.Namespace,
},
// Per-Application HelmRelease generation defaults from server
// flags. The same five values are applied to every Resource,
// matching cozystack-operator's PackageReconciler. The
// per-Application HelmInstallTimeout annotation populated below
// still wins over HelmReleaseInstallTimeout/UpgradeTimeout.
// HelmRelease generation defaults from server flags. The same five
// values are applied to every Resource, matching cozystack-operator's
// PackageReconciler. The kind-wide HelmInstallTimeout and
// HelmUpgradeTimeout overrides from the ApplicationDefinition
// populated below still take precedence over these defaults.
HelmReleaseInterval: hrFlags.interval,
HelmReleaseRetryInterval: hrFlags.retryInterval,
HelmReleaseInstallTimeout: hrFlags.installTimeout,
@@ -348,11 +348,11 @@ func buildResourceFromCRD(crd v1alpha1.ApplicationDefinition, hrFlags helmReleas
WaitStrategy: crd.Spec.Release.WaitStrategy,
HealthCheckExprs: crd.Spec.Release.HealthCheckExprs,
}
// Per-Application HelmRelease Install/Upgrade timeout. Applications
// whose parent chart contains asynchronously-provisioned resources
// the chart itself depends on (for example, the Kamaji-provisioned
// admin-kubeconfig Secret for Kubernetes tenants) need a longer
// wait budget than the Flux default. Consumed by the REST storage
// Kind-wide HelmRelease Install/Upgrade timeout from this
// ApplicationDefinition. Kinds whose parent chart contains asynchronously-
// provisioned resources the chart itself depends on (for example, the
// Kamaji-provisioned admin-kubeconfig Secret for Kubernetes tenants) need a
// longer wait budget than the Flux default. Consumed by the REST storage
// layer when building the HelmRelease Spec. The parser rejects
// units Flux would reject at webhook time, so a bad annotation
// surfaces as a loud startup failure instead of a silent drop to
+9 -8
View File
@@ -211,14 +211,14 @@ type ReleaseConfig struct {
Prefix string `yaml:"prefix"`
Labels map[string]string `yaml:"labels"`
ChartRef ChartRefConfig `yaml:"chartRef"`
// HelmInstallTimeout is a per-Application override of Install.Timeout
// and Upgrade.Timeout. When non-zero, it wins over
// HelmInstallTimeout is a per-kind override of Install.Timeout and
// Upgrade.Timeout. When non-zero, it wins over
// HelmReleaseInstallTimeout / HelmReleaseUpgradeTimeout below.
// Populated from the release.cozystack.io/helm-install-timeout
// annotation on the ApplicationDefinition at start-up.
HelmInstallTimeout time.Duration `yaml:"helmInstallTimeout,omitempty"`
// HelmUpgradeTimeout is a per-Application override of Upgrade.Timeout
// only. When non-zero, it wins over both HelmReleaseUpgradeTimeout and
// HelmUpgradeTimeout is a per-kind override of Upgrade.Timeout only.
// When non-zero, it wins over both HelmReleaseUpgradeTimeout and
// the Upgrade.Timeout value HelmInstallTimeout would otherwise apply,
// so a kind can carry an asymmetric install/upgrade budget. Populated
// from the release.cozystack.io/helm-upgrade-timeout annotation on the
@@ -236,12 +236,13 @@ type ReleaseConfig struct {
// without polling healthy releases at the same cadence.
HelmReleaseRetryInterval time.Duration `yaml:"helmReleaseRetryInterval,omitempty"`
// HelmReleaseInstallTimeout is the global default for
// Spec.Install.Timeout. Overridden per-Application by HelmInstallTimeout
// when the annotation is set.
// Spec.Install.Timeout. Overridden for this Application kind by
// HelmInstallTimeout from its ApplicationDefinition.
HelmReleaseInstallTimeout time.Duration `yaml:"helmReleaseInstallTimeout,omitempty"`
// HelmReleaseUpgradeTimeout is the global default for
// Spec.Upgrade.Timeout. Overridden per-Application by HelmInstallTimeout
// when the annotation is set.
// Spec.Upgrade.Timeout. Overridden for this Application kind by
// HelmInstallTimeout, or by HelmUpgradeTimeout with higher precedence,
// from its ApplicationDefinition.
HelmReleaseUpgradeTimeout time.Duration `yaml:"helmReleaseUpgradeTimeout,omitempty"`
// HelmReleaseMaxHistory is the global default for Spec.MaxHistory.
// 0 means unlimited per Helm semantics; matches the cozystack-operator
+3 -2
View File
@@ -1789,8 +1789,9 @@ func latestMonitorTime(m *cozyv1alpha1.WorkloadMonitor) metav1.Time {
// from Spec.Interval — the previous coupling caused failed installs to
// retry only every 5m, exceeding E2E budgets.
func (r *REST) convertApplicationToHelmRelease(app *appsv1alpha1.Application) (*helmv2.HelmRelease, error) {
// Per-Application annotation overrides win over the global defaults
// (HelmReleaseInstallTimeout / HelmReleaseUpgradeTimeout):
// Kind-wide annotation overrides from this kind's ApplicationDefinition
// win over the global defaults (HelmReleaseInstallTimeout /
// HelmReleaseUpgradeTimeout):
// - HelmInstallTimeout (release.cozystack.io/helm-install-timeout)
// sets both Install.Timeout and Upgrade.Timeout;
// - HelmUpgradeTimeout (release.cozystack.io/helm-upgrade-timeout)
@@ -204,11 +204,11 @@ func TestConvertApplicationToHelmRelease_ZeroMaxHistory(t *testing.T) {
}
}
// HelmInstallTimeout (the per-Application annotation override) wins over the
// global HelmReleaseInstallTimeout / HelmReleaseUpgradeTimeout defaults. This
// is the contract that lets ApplicationDefinitions like Kubernetes (with its
// long Kamaji bootstrap) opt out of the global default without operators
// having to widen the global flag for every kind.
// HelmInstallTimeout (the kind-wide ApplicationDefinition annotation override)
// wins over the global HelmReleaseInstallTimeout / HelmReleaseUpgradeTimeout
// defaults. This is the contract that lets ApplicationDefinitions like
// Kubernetes (with its long Kamaji bootstrap) opt out of the global default
// without operators having to widen the global flag for every kind.
//
// The asymmetric-globals case pins that the override wipes both install and
// upgrade timeouts to the same value, even when the globals differ — a
@@ -19,11 +19,11 @@ const (
testGlobalUpgradeTimeout = 10 * time.Minute
)
// newRESTForTimeout builds a REST struct focused on the per-Application
// HelmInstallTimeout annotation override path. Global HelmRelease* defaults
// are populated with production-shaped values so the unset-annotation case
// exercises "global default applies". The full spec contract is covered by
// rest_helmrelease_spec_test.go.
// newRESTForTimeout builds a REST struct focused on the kind-wide
// ApplicationDefinition HelmInstallTimeout override path. Global HelmRelease*
// defaults are populated with production-shaped values so the unset-annotation
// case exercises "global default applies". The full spec contract is covered
// by rest_helmrelease_spec_test.go.
func newRESTForTimeout(kind, prefix string, helmInstallTimeout time.Duration) *REST {
return &REST{
kindName: kind,
@@ -137,10 +137,11 @@ func TestConvertApplicationToHelmRelease_AppliesReleaseConfigTimeout(t *testing.
}
}
// HelmUpgradeTimeout (the per-Application release.cozystack.io/helm-upgrade-timeout
// annotation override) overrides only Upgrade.Timeout, and wins over the
// Upgrade.Timeout value HelmInstallTimeout would otherwise apply. This lets a
// kind carry an asymmetric budget — e.g. a short install but a long upgrade.
// HelmUpgradeTimeout (the kind-wide ApplicationDefinition
// release.cozystack.io/helm-upgrade-timeout override) changes only
// Upgrade.Timeout and wins over the value HelmInstallTimeout would otherwise
// apply. This lets a kind carry an asymmetric budget — e.g. a short install but
// a long upgrade.
func TestConvertApplicationToHelmRelease_UpgradeTimeoutAnnotation(t *testing.T) {
cases := []struct {
name string