Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions api/v1alpha1/helm_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ type HelmApplicationSpec struct {
// +kubebuilder:validation:Enum="";NoResourceReconciliation
// +optional
Maintenance string `json:"maintenance,omitempty"`
// Timeout is the time to wait for any individual Kubernetes operation (like Jobs
// for hooks) during the performance of any Helm action. Defaults to 5m.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +kubebuilder:validation:XValidation:rule="duration(self) > duration('0s')",message="timeout must be greater than zero"
// +kubebuilder:validation:XValidation:rule="duration(self) <= duration('2h')",message="timeout must not exceed 2h"
// +optional
Timeout *metav1.Duration `json:"timeout,omitempty"`
}

// The XValidation rule below states the relationship between the two reference
Expand Down
8 changes: 8 additions & 0 deletions api/v1alpha1/helm_cluster_addon.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ type HelmClusterAddonSpec struct {
// +kubebuilder:validation:Enum="";NoResourceReconciliation
// +optional
Maintenance string `json:"maintenance,omitempty"`
// Timeout is the time to wait for any individual Kubernetes operation (like Jobs
// for hooks) during the performance of any Helm action. Defaults to 5m.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +kubebuilder:validation:XValidation:rule="duration(self) > duration('0s')",message="timeout must be greater than zero"
// +kubebuilder:validation:XValidation:rule="duration(self) <= duration('2h')",message="timeout must not exceed 2h"
// +optional
Timeout *metav1.Duration `json:"timeout,omitempty"`
}

type HelmClusterAddonChartRef struct {
Expand Down
10 changes: 10 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crds/doc-ru-helmapplications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ spec:

При значении `NoResourceReconciliation` контроллер прекращает обновление управляемых ресурсов, что позволяет выполнять ручное вмешательство или обслуживание без перезаписи изменений оператором.
При пустом значении (`""`) используется стандартное согласование.
timeout:
description: |
Время ожидания каждой отдельной операции Kubernetes (например, Job для хуков) при выполнении любого действия Helm. По умолчанию — `5m`. Должно быть больше нуля и не может превышать `2h`.
values:
description: Пользовательские значения для релиза HelmApplication.
status:
Expand Down
3 changes: 3 additions & 0 deletions crds/doc-ru-helmclusteraddons.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ spec:
При пустом значении (`""`) используется стандартное согласование.
namespace:
description: Пространство имён для развёртывания релиза аддона.
timeout:
description: |
Время ожидания каждой отдельной операции Kubernetes (например, Job для хуков) при выполнении любого действия Helm. По умолчанию — `5m`. Должно быть больше нуля и не может превышать `2h`.
values:
description: Пользовательские значения для релиза HelmClusterAddon.
status:
Expand Down
11 changes: 11 additions & 0 deletions crds/helmapplications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,17 @@ spec:
- ""
- NoResourceReconciliation
type: string
timeout:
description: |-
Timeout is the time to wait for any individual Kubernetes operation (like Jobs
for hooks) during the performance of any Helm action. Defaults to 5m.
pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$
type: string
x-kubernetes-validations:
- message: timeout must be greater than zero
rule: duration(self) > duration('0s')
- message: timeout must not exceed 2h
rule: duration(self) <= duration('2h')
values:
description: Values holds the values for this HelmApplication release.
x-kubernetes-preserve-unknown-fields: true
Expand Down
11 changes: 11 additions & 0 deletions crds/helmclusteraddons.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ spec:
maxLength: 63
minLength: 3
type: string
timeout:
description: |-
Timeout is the time to wait for any individual Kubernetes operation (like Jobs
for hooks) during the performance of any Helm action. Defaults to 5m.
pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$
type: string
x-kubernetes-validations:
- message: timeout must be greater than zero
rule: duration(self) > duration('0s')
- message: timeout must not exceed 2h
rule: duration(self) <= duration('2h')
values:
description: Values holds the values for this HelmClusterAddon release.
x-kubernetes-preserve-unknown-fields: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func (r *AddonRelease) ChartRef() source.ChartRef {

func (r *AddonRelease) TargetNamespace() string { return r.obj.Spec.Namespace }
func (r *AddonRelease) Values() *apiextensionsv1.JSON { return r.obj.Spec.Values }
func (r *AddonRelease) Timeout() *metav1.Duration { return r.obj.Spec.Timeout }
func (r *AddonRelease) MaintenanceActivated() bool { return r.obj.MaintenanceModeActivated() }
func (r *AddonRelease) MaintenanceEnabled() bool { return r.obj.MaintenanceModeEnabled() }
func (r *AddonRelease) ForceReconcileRequired() bool { return r.obj.ForceReconcileRequired() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func (r *ApplicationRelease) ChartRef() source.ChartRef {

func (r *ApplicationRelease) TargetNamespace() string { return r.obj.Namespace }
func (r *ApplicationRelease) Values() *apiextensionsv1.JSON { return r.obj.Spec.Values }
func (r *ApplicationRelease) Timeout() *metav1.Duration { return r.obj.Spec.Timeout }
func (r *ApplicationRelease) MaintenanceActivated() bool { return r.obj.MaintenanceModeActivated() }

func (r *ApplicationRelease) MaintenanceEnabled() bool { return r.obj.MaintenanceModeEnabled() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ func (s *ReleaseService) SyncReleaseSpec(ctx context.Context, rel source.Release

release.Spec.TargetNamespace = rel.TargetNamespace()
release.Spec.Values = rel.Values()
release.Spec.Timeout = rel.Timeout()
release.Spec.Suspend = rel.MaintenanceActivated()

setReconcileRequestAnnotations(release)
Expand Down Expand Up @@ -162,6 +163,7 @@ func applyHelmReleaseSpec(rel source.Release, existing *helmv2.HelmRelease, sour
existing.Spec.ReleaseName = rel.ReleaseName()
existing.Spec.TargetNamespace = rel.TargetNamespace()
existing.Spec.Values = rel.Values()
existing.Spec.Timeout = rel.Timeout()

existing.Spec.Suspend = rel.MaintenanceActivated()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package services
import (
"context"
"testing"
"time"

helmv2 "github.com/fluxcd/helm-controller/api/v2"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
Expand Down Expand Up @@ -127,6 +128,62 @@ func TestEnsureHelmReleaseKeepsForeignLabels(t *testing.T) {
}
}

// TestEnsureHelmReleaseCarriesTheTimeout pins that spec.timeout reaches the
// HelmRelease for both families, and that removing it from the spec removes it
// from the HelmRelease too, so helm-controller falls back to its own default
// instead of keeping the last value it was given.
func TestEnsureHelmReleaseCarriesTheTimeout(t *testing.T) {
timeout := &metav1.Duration{Duration: 15 * time.Minute}

withTimeout := func(d *metav1.Duration) []source.Release {
app := testApplication()
app.Spec.Timeout = d
addon := testAddon()
addon.Spec.Timeout = d

return []source.Release{adapter.NewApplicationRelease(app), adapter.NewAddonRelease(addon)}
}

for i, rel := range withTimeout(timeout) {
t.Run(rel.Kind(), func(t *testing.T) {
service, c := newReleaseService(t)

release := ensureRelease(t, service, c, rel)
if release.Spec.Timeout == nil || *release.Spec.Timeout != *timeout {
t.Fatalf("timeout = %v, want %v", release.Spec.Timeout, timeout)
}

release = ensureRelease(t, service, c, withTimeout(nil)[i])
if release.Spec.Timeout != nil {
t.Fatalf("timeout = %v, want it unset once the spec drops it", release.Spec.Timeout)
}
})
}
}

// TestSyncReleaseSpecCarriesTheTimeout pins that a timeout changed while the
// release is being deleted reaches the HelmRelease: it bounds the uninstall, and
// raising it is how a stuck uninstall is let through.
func TestSyncReleaseSpecCarriesTheTimeout(t *testing.T) {
app := testApplication()
rel := adapter.NewApplicationRelease(app)
service, c := newReleaseService(t)
release := ensureRelease(t, service, c, rel)

app.Spec.Timeout = &metav1.Duration{Duration: 20 * time.Minute}
if err := service.SyncReleaseSpec(context.Background(), rel, release); err != nil {
t.Fatalf("SyncReleaseSpec returned %v", err)
}

synced := &helmv2.HelmRelease{}
if err := c.Get(context.Background(), client.ObjectKeyFromObject(release), synced); err != nil {
t.Fatalf("getting helm release: %v", err)
}
if synced.Spec.Timeout == nil || *synced.Spec.Timeout != *app.Spec.Timeout {
t.Fatalf("timeout = %v, want %v", synced.Spec.Timeout, app.Spec.Timeout)
}
}

// TestIsDesiredChartDeployedSurvivesASourceKindFlip pins that a revision carrying no
// digest is read as "not the desired chart" rather than indexed into. A release
// installed from a registry keeps an OCI digest in its history; when the repository
Expand Down
2 changes: 2 additions & 0 deletions images/operator-helm-controller/internal/source/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ type Release interface {
// TargetNamespace is where the release is deployed.
TargetNamespace() string
Values() *apiextensionsv1.JSON
// Timeout is nil when the spec leaves it to helm-controller's default.
Timeout() *metav1.Duration
MaintenanceActivated() bool
MaintenanceEnabled() bool
ForceReconcileRequired() bool
Expand Down
Loading