mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-03 18:47:04 +09:00
release.cozystack.io/helm-install-timeout sets both Install.Timeout and Upgrade.Timeout to one value. Add release.cozystack.io/helm-upgrade-timeout so an ApplicationDefinition can override only Upgrade.Timeout — letting a kind keep a short install budget but a longer upgrade budget (or vice versa). When set it wins over the value the install annotation would otherwise apply to the upgrade side. - config: add HelmUpgradeTimeoutAnnotation + ReleaseConfig.HelmUpgradeTimeout; generalize ParseHelmInstallTimeoutAnnotation to ParseHelmTimeoutAnnotation since it now parses both annotations (identical Flux unit validation). - server: parse the new annotation at startup with the same loud-fail behavior; update --helmrelease-upgrade-timeout help. - rest: apply HelmUpgradeTimeout after the install-annotation copy so the upgrade-specific value wins; restore the accurate carrier comment. - tests: cover the upgrade-only override, the asymmetric precedence, and the install-sets-both regression guard. Behavior is unchanged for every existing ApplicationDefinition (none set the new annotation today). Address review feedback from coderabbitai on rest.go. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
158 lines
6.8 KiB
Go
158 lines
6.8 KiB
Go
/*
|
|
Copyright 2024 The Cozystack Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"time"
|
|
)
|
|
|
|
// HelmInstallTimeoutAnnotation is the ApplicationDefinition metadata
|
|
// annotation key that overrides the Flux HelmRelease Install.Timeout and
|
|
// Upgrade.Timeout for a given Application kind.
|
|
const HelmInstallTimeoutAnnotation = "release.cozystack.io/helm-install-timeout"
|
|
|
|
// HelmUpgradeTimeoutAnnotation is the ApplicationDefinition metadata
|
|
// annotation key that overrides only the Flux HelmRelease Upgrade.Timeout
|
|
// for a given Application kind. When set it takes precedence over the
|
|
// Upgrade.Timeout value HelmInstallTimeoutAnnotation would otherwise apply,
|
|
// letting a kind keep a short install budget but a longer upgrade budget
|
|
// (or vice versa).
|
|
const HelmUpgradeTimeoutAnnotation = "release.cozystack.io/helm-upgrade-timeout"
|
|
|
|
// helmTimeoutPattern mirrors the CRD validation pattern used by Flux
|
|
// helm-controller on HelmReleaseSpec.Install.Timeout (ms/s/m/h units only).
|
|
// time.ParseDuration accepts ns/us/µs, but Flux rejects them - parsing here
|
|
// with the same shape avoids feeding the controller a value it will later
|
|
// reject at webhook time. See
|
|
// github.com/fluxcd/helm-controller/api/v2 HelmReleaseSpec.Install.Timeout
|
|
// in the go module cache.
|
|
var helmTimeoutPattern = regexp.MustCompile(`^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$`)
|
|
|
|
// ParseHelmTimeoutAnnotation parses the value of a Flux HelmRelease timeout
|
|
// annotation (release.cozystack.io/helm-install-timeout or
|
|
// release.cozystack.io/helm-upgrade-timeout). The empty string is treated as
|
|
// "unset" and returns (0, nil) so callers can leave the corresponding
|
|
// ReleaseConfig field zeroed and let flux defaults apply. Values accepted by
|
|
// time.ParseDuration but rejected by Flux (ns/us/µs) return a helpful
|
|
// error instead of silently parsing and failing later at HelmRelease
|
|
// admission.
|
|
func ParseHelmTimeoutAnnotation(raw string) (time.Duration, error) {
|
|
if raw == "" {
|
|
return 0, nil
|
|
}
|
|
if !helmTimeoutPattern.MatchString(raw) {
|
|
return 0, fmt.Errorf("must match %s (Flux accepts ms/s/m/h units only), got %q",
|
|
helmTimeoutPattern, raw)
|
|
}
|
|
d, err := time.ParseDuration(raw)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("time.ParseDuration(%q): %w", raw, err)
|
|
}
|
|
return d, nil
|
|
}
|
|
|
|
// ParsePositiveDuration parses raw as a time.Duration and rejects malformed
|
|
// or non-positive values. Flux HelmRelease fields (Interval, Timeout,
|
|
// RetryInterval) require strictly positive durations, so a misconfigured
|
|
// flag must fail fast at startup rather than propagating into every HR.
|
|
// Shared between cozystack-operator and cozystack-api so both paths reject
|
|
// the same set of inputs at startup.
|
|
func ParsePositiveDuration(flagName, raw string) (time.Duration, error) {
|
|
d, err := time.ParseDuration(raw)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid duration for %s=%q: %w", flagName, raw, err)
|
|
}
|
|
if d <= 0 {
|
|
return 0, fmt.Errorf("%s must be > 0 (got %q)", flagName, raw)
|
|
}
|
|
return d, nil
|
|
}
|
|
|
|
// ResourceConfig represents the structure of the configuration file.
|
|
type ResourceConfig struct {
|
|
Resources []Resource `yaml:"resources"`
|
|
}
|
|
|
|
// Resource describes an individual resource.
|
|
type Resource struct {
|
|
Application ApplicationConfig `yaml:"application"`
|
|
Release ReleaseConfig `yaml:"release"`
|
|
}
|
|
|
|
// ApplicationConfig contains the application settings.
|
|
type ApplicationConfig struct {
|
|
Kind string `yaml:"kind"`
|
|
Singular string `yaml:"singular"`
|
|
Plural string `yaml:"plural"`
|
|
ShortNames []string `yaml:"shortNames"`
|
|
OpenAPISchema string `yaml:"openAPISchema"`
|
|
}
|
|
|
|
// ReleaseConfig contains the release settings.
|
|
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
|
|
// 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
|
|
// 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
|
|
// ApplicationDefinition at start-up.
|
|
HelmUpgradeTimeout time.Duration `yaml:"helmUpgradeTimeout,omitempty"`
|
|
// HelmReleaseInterval is the global default for Spec.Interval on
|
|
// HelmReleases generated by cozystack-api. Set from the api server
|
|
// --helmrelease-interval flag; mirrors the cozystack-operator flag of
|
|
// the same name so both HelmRelease-generating paths use the same
|
|
// reconcile cadence.
|
|
HelmReleaseInterval time.Duration `yaml:"helmReleaseInterval,omitempty"`
|
|
// HelmReleaseRetryInterval is the global default for
|
|
// Install/Upgrade.Strategy.RetryInterval. Decoupled from
|
|
// HelmReleaseInterval so failed install/upgrade retries recover fast
|
|
// 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.
|
|
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.
|
|
HelmReleaseUpgradeTimeout time.Duration `yaml:"helmReleaseUpgradeTimeout,omitempty"`
|
|
// HelmReleaseMaxHistory is the global default for Spec.MaxHistory.
|
|
// 0 means unlimited per Helm semantics; matches the cozystack-operator
|
|
// flag of the same name. No omitempty: 0 ("unlimited") must survive a
|
|
// round-trip distinct from an unset field if ReleaseConfig is ever
|
|
// marshalled.
|
|
HelmReleaseMaxHistory int `yaml:"helmReleaseMaxHistory"`
|
|
}
|
|
|
|
// ChartRefConfig references a Flux source artifact for the Helm chart.
|
|
type ChartRefConfig struct {
|
|
Kind string `yaml:"kind"`
|
|
Name string `yaml:"name"`
|
|
Namespace string `yaml:"namespace"`
|
|
}
|