diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 5fd44bbe0..56e531975 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -91,6 +91,14 @@ jobs: kubectl -n helm-system rollout status deploy/helm-controller --timeout=1m env: KUBEBUILDER_ASSETS: ${{ github.workspace }}/kubebuilder/bin + - name: Test samples + run: | + kubectl create ns samples + kubectl -n samples apply -f config/samples + kubectl -n samples wait hr/podinfo-ocirepository --for=condition=ready --timeout=4m + kubectl -n samples wait hr/podinfo-gitrepository --for=condition=ready --timeout=4m + kubectl -n samples wait hr/podinfo-helmrepository --for=condition=ready --timeout=4m + kubectl delete ns samples - name: Install sources run: | kubectl -n helm-system apply -f config/testdata/sources diff --git a/Makefile b/Makefile index a75752f6b..b7266e9c3 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ manifests: controller-gen # Generate API reference documentation api-docs: gen-crd-api-reference-docs - $(GEN_CRD_API_REFERENCE_DOCS) -api-dir=./api/v2beta2 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/api/v2beta2/helm.md + $(GEN_CRD_API_REFERENCE_DOCS) -api-dir=./api/v2 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/api/v2/helm.md # Run go mod tidy tidy: diff --git a/PROJECT b/PROJECT index 200d2d5e4..0a947b8b7 100644 --- a/PROJECT +++ b/PROJECT @@ -7,5 +7,8 @@ resources: - group: helm kind: HelmRelease version: v2beta2 -storageVersion: v2beta2 +- group: helm + kind: HelmRelease + version: v2 +storageVersion: v2 version: "2" diff --git a/README.md b/README.md index 4db5a1fc8..34cb40640 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ operator. * Supports `HelmChart` artifacts produced from `HelmRepository`, `GitRepository` and `Bucket` sources * Fetches artifacts produced by [source-controller][] from `HelmChart` - objects + and `OCIRepository` objects * Watches `HelmChart` objects for revision changes (including semver ranges for charts from `HelmRepository` sources) * Performs automated Helm actions, including Helm tests, rollbacks and @@ -49,7 +49,7 @@ operator. ## Specifications -* [API](docs/spec/v2beta2/README.md) +* [API](docs/spec/v2/README.md) * [Controller](docs/spec/README.md) [source-controller]: https://github.com/fluxcd/source-controller diff --git a/api/v2/annotations.go b/api/v2/annotations.go new file mode 100644 index 000000000..2bd14057c --- /dev/null +++ b/api/v2/annotations.go @@ -0,0 +1,84 @@ +/* +Copyright 2024 The Flux 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 v2 + +import "github.com/fluxcd/pkg/apis/meta" + +const ( + // ForceRequestAnnotation is the annotation used for triggering a one-off forced + // Helm release, even when there are no new changes in the HelmRelease. + // The value is interpreted as a token, and must equal the value of + // meta.ReconcileRequestAnnotation in order to trigger a release. + ForceRequestAnnotation string = "reconcile.fluxcd.io/forceAt" + + // ResetRequestAnnotation is the annotation used for resetting the failure counts + // of a HelmRelease, so that it can be retried again. + // The value is interpreted as a token, and must equal the value of + // meta.ReconcileRequestAnnotation in order to reset the failure counts. + ResetRequestAnnotation string = "reconcile.fluxcd.io/resetAt" +) + +// ShouldHandleResetRequest returns true if the HelmRelease has a reset request +// annotation, and the value of the annotation matches the value of the +// meta.ReconcileRequestAnnotation annotation. +// +// To ensure that the reset request is handled only once, the value of +// HelmReleaseStatus.LastHandledResetAt is updated to match the value of the +// reset request annotation (even if the reset request is not handled because +// the value of the meta.ReconcileRequestAnnotation annotation does not match). +func ShouldHandleResetRequest(obj *HelmRelease) bool { + return handleRequest(obj, ResetRequestAnnotation, &obj.Status.LastHandledResetAt) +} + +// ShouldHandleForceRequest returns true if the HelmRelease has a force request +// annotation, and the value of the annotation matches the value of the +// meta.ReconcileRequestAnnotation annotation. +// +// To ensure that the force request is handled only once, the value of +// HelmReleaseStatus.LastHandledForceAt is updated to match the value of the +// force request annotation (even if the force request is not handled because +// the value of the meta.ReconcileRequestAnnotation annotation does not match). +func ShouldHandleForceRequest(obj *HelmRelease) bool { + return handleRequest(obj, ForceRequestAnnotation, &obj.Status.LastHandledForceAt) +} + +// handleRequest returns true if the HelmRelease has a request annotation, and +// the value of the annotation matches the value of the meta.ReconcileRequestAnnotation +// annotation. +// +// The lastHandled argument is used to ensure that the request is handled only +// once, and is updated to match the value of the request annotation (even if +// the request is not handled because the value of the meta.ReconcileRequestAnnotation +// annotation does not match). +func handleRequest(obj *HelmRelease, annotation string, lastHandled *string) bool { + requestAt, requestOk := obj.GetAnnotations()[annotation] + reconcileAt, reconcileOk := meta.ReconcileAnnotationValue(obj.GetAnnotations()) + + var lastHandledRequest string + if requestOk { + lastHandledRequest = *lastHandled + *lastHandled = requestAt + } + + if requestOk && reconcileOk && requestAt == reconcileAt { + lastHandledReconcile := obj.Status.GetLastHandledReconcileRequest() + if lastHandledReconcile != reconcileAt && lastHandledRequest != requestAt { + return true + } + } + return false +} diff --git a/api/v2/annotations_test.go b/api/v2/annotations_test.go new file mode 100644 index 000000000..f164c8e59 --- /dev/null +++ b/api/v2/annotations_test.go @@ -0,0 +1,165 @@ +/* +Copyright 2024 The Flux 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 v2 + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/fluxcd/pkg/apis/meta" +) + +func TestShouldHandleResetRequest(t *testing.T) { + t.Run("should handle reset request", func(t *testing.T) { + obj := &HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + meta.ReconcileRequestAnnotation: "b", + ResetRequestAnnotation: "b", + }, + }, + Status: HelmReleaseStatus{ + LastHandledResetAt: "a", + ReconcileRequestStatus: meta.ReconcileRequestStatus{ + LastHandledReconcileAt: "a", + }, + }, + } + + if !ShouldHandleResetRequest(obj) { + t.Error("ShouldHandleResetRequest() = false") + } + + if obj.Status.LastHandledResetAt != "b" { + t.Error("ShouldHandleResetRequest did not update LastHandledResetAt") + } + }) +} + +func TestShouldHandleForceRequest(t *testing.T) { + t.Run("should handle force request", func(t *testing.T) { + obj := &HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + meta.ReconcileRequestAnnotation: "b", + ForceRequestAnnotation: "b", + }, + }, + Status: HelmReleaseStatus{ + LastHandledForceAt: "a", + ReconcileRequestStatus: meta.ReconcileRequestStatus{ + LastHandledReconcileAt: "a", + }, + }, + } + + if !ShouldHandleForceRequest(obj) { + t.Error("ShouldHandleForceRequest() = false") + } + + if obj.Status.LastHandledForceAt != "b" { + t.Error("ShouldHandleForceRequest did not update LastHandledForceAt") + } + }) +} + +func Test_handleRequest(t *testing.T) { + const requestAnnotation = "requestAnnotation" + + tests := []struct { + name string + annotations map[string]string + lastHandledReconcile string + lastHandledRequest string + want bool + expectLastHandledRequest string + }{ + { + name: "valid request and reconcile annotations", + annotations: map[string]string{ + meta.ReconcileRequestAnnotation: "b", + requestAnnotation: "b", + }, + want: true, + expectLastHandledRequest: "b", + }, + { + name: "mismatched annotations", + annotations: map[string]string{ + meta.ReconcileRequestAnnotation: "b", + requestAnnotation: "c", + }, + want: false, + expectLastHandledRequest: "c", + }, + { + name: "reconcile matches previous request", + annotations: map[string]string{ + meta.ReconcileRequestAnnotation: "b", + requestAnnotation: "b", + }, + lastHandledReconcile: "a", + lastHandledRequest: "b", + want: false, + expectLastHandledRequest: "b", + }, + { + name: "request matches previous reconcile", + annotations: map[string]string{ + meta.ReconcileRequestAnnotation: "b", + requestAnnotation: "b", + }, + lastHandledReconcile: "b", + lastHandledRequest: "a", + want: false, + expectLastHandledRequest: "b", + }, + { + name: "missing annotations", + annotations: map[string]string{}, + lastHandledRequest: "a", + want: false, + expectLastHandledRequest: "a", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + obj := &HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: tt.annotations, + }, + Status: HelmReleaseStatus{ + ReconcileRequestStatus: meta.ReconcileRequestStatus{ + LastHandledReconcileAt: tt.lastHandledReconcile, + }, + }, + } + + lastHandled := tt.lastHandledRequest + result := handleRequest(obj, requestAnnotation, &lastHandled) + + if result != tt.want { + t.Errorf("handleRequest() = %v, want %v", result, tt.want) + } + if lastHandled != tt.expectLastHandledRequest { + t.Errorf("lastHandledRequest = %v, want %v", lastHandled, tt.expectLastHandledRequest) + } + }) + } +} diff --git a/api/v2/condition_types.go b/api/v2/condition_types.go new file mode 100644 index 000000000..fb4c6d65f --- /dev/null +++ b/api/v2/condition_types.go @@ -0,0 +1,82 @@ +/* +Copyright 2024 The Flux 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 v2 + +const ( + // ReleasedCondition represents the status of the last release attempt + // (install/upgrade/test) against the latest desired state. + ReleasedCondition string = "Released" + + // TestSuccessCondition represents the status of the last test attempt against + // the latest desired state. + TestSuccessCondition string = "TestSuccess" + + // RemediatedCondition represents the status of the last remediation attempt + // (uninstall/rollback) due to a failure of the last release attempt against the + // latest desired state. + RemediatedCondition string = "Remediated" +) + +const ( + // InstallSucceededReason represents the fact that the Helm install for the + // HelmRelease succeeded. + InstallSucceededReason string = "InstallSucceeded" + + // InstallFailedReason represents the fact that the Helm install for the + // HelmRelease failed. + InstallFailedReason string = "InstallFailed" + + // UpgradeSucceededReason represents the fact that the Helm upgrade for the + // HelmRelease succeeded. + UpgradeSucceededReason string = "UpgradeSucceeded" + + // UpgradeFailedReason represents the fact that the Helm upgrade for the + // HelmRelease failed. + UpgradeFailedReason string = "UpgradeFailed" + + // TestSucceededReason represents the fact that the Helm tests for the + // HelmRelease succeeded. + TestSucceededReason string = "TestSucceeded" + + // TestFailedReason represents the fact that the Helm tests for the HelmRelease + // failed. + TestFailedReason string = "TestFailed" + + // RollbackSucceededReason represents the fact that the Helm rollback for the + // HelmRelease succeeded. + RollbackSucceededReason string = "RollbackSucceeded" + + // RollbackFailedReason represents the fact that the Helm test for the + // HelmRelease failed. + RollbackFailedReason string = "RollbackFailed" + + // UninstallSucceededReason represents the fact that the Helm uninstall for the + // HelmRelease succeeded. + UninstallSucceededReason string = "UninstallSucceeded" + + // UninstallFailedReason represents the fact that the Helm uninstall for the + // HelmRelease failed. + UninstallFailedReason string = "UninstallFailed" + + // ArtifactFailedReason represents the fact that the artifact download for the + // HelmRelease failed. + ArtifactFailedReason string = "ArtifactFailed" + + // DependencyNotReadyReason represents the fact that + // one of the dependencies is not ready. + DependencyNotReadyReason string = "DependencyNotReady" +) diff --git a/api/v2/doc.go b/api/v2/doc.go new file mode 100644 index 000000000..e04280738 --- /dev/null +++ b/api/v2/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2024 The Flux 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 v2 contains API Schema definitions for the helm v2 API group +// +kubebuilder:object:generate=true +// +groupName=helm.toolkit.fluxcd.io +package v2 diff --git a/api/v2/groupversion_info.go b/api/v2/groupversion_info.go new file mode 100644 index 000000000..352331bd9 --- /dev/null +++ b/api/v2/groupversion_info.go @@ -0,0 +1,33 @@ +/* +Copyright 2024 The Flux 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 v2 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "helm.toolkit.fluxcd.io", Version: "v2"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/api/v2/helmrelease_types.go b/api/v2/helmrelease_types.go new file mode 100644 index 000000000..688db28d0 --- /dev/null +++ b/api/v2/helmrelease_types.go @@ -0,0 +1,1255 @@ +/* +Copyright 2024 The Flux 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 v2 + +import ( + "strings" + "time" + + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/yaml" + + "github.com/fluxcd/pkg/apis/kustomize" + "github.com/fluxcd/pkg/apis/meta" +) + +const ( + // HelmReleaseKind is the kind in string format. + HelmReleaseKind = "HelmRelease" + // HelmReleaseFinalizer is set on a HelmRelease when it is first handled by + // the controller, and removed when this object is deleted. + HelmReleaseFinalizer = "finalizers.fluxcd.io" +) + +const ( + // defaultMaxHistory is the default number of Helm release versions to keep. + defaultMaxHistory = 5 +) + +// Kustomize Helm PostRenderer specification. +type Kustomize struct { + // Strategic merge and JSON patches, defined as inline YAML objects, + // capable of targeting objects based on kind, label and annotation selectors. + // +optional + Patches []kustomize.Patch `json:"patches,omitempty"` + + // Images is a list of (image name, new name, new tag or digest) + // for changing image names, tags or digests. This can also be achieved with a + // patch, but this operator is simpler to specify. + // +optional + Images []kustomize.Image `json:"images,omitempty" json:"images,omitempty"` +} + +// PostRenderer contains a Helm PostRenderer specification. +type PostRenderer struct { + // Kustomization to apply as PostRenderer. + // +optional + Kustomize *Kustomize `json:"kustomize,omitempty"` +} + +// HelmReleaseSpec defines the desired state of a Helm release. +// +kubebuilder:validation:XValidation:rule="(has(self.chart) && !has(self.chartRef)) || (!has(self.chart) && has(self.chartRef))", message="either chart or chartRef must be set" +type HelmReleaseSpec struct { + // Chart defines the template of the v1beta2.HelmChart that should be created + // for this HelmRelease. + // +optional + Chart HelmChartTemplate `json:"chart,omitempty"` + + // ChartRef holds a reference to a source controller resource containing the + // Helm chart artifact. + // +optional + ChartRef *CrossNamespaceSourceReference `json:"chartRef,omitempty"` + + // Interval at which to reconcile the Helm release. + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + // +required + Interval metav1.Duration `json:"interval"` + + // KubeConfig for reconciling the HelmRelease on a remote cluster. + // When used in combination with HelmReleaseSpec.ServiceAccountName, + // forces the controller to act on behalf of that Service Account at the + // target cluster. + // If the --default-service-account flag is set, its value will be used as + // a controller level fallback for when HelmReleaseSpec.ServiceAccountName + // is empty. + // +optional + KubeConfig *meta.KubeConfigReference `json:"kubeConfig,omitempty"` + + // Suspend tells the controller to suspend reconciliation for this HelmRelease, + // it does not apply to already started reconciliations. Defaults to false. + // +optional + Suspend bool `json:"suspend,omitempty"` + + // ReleaseName used for the Helm release. Defaults to a composition of + // '[TargetNamespace-]Name'. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=53 + // +kubebuilder:validation:Optional + // +optional + ReleaseName string `json:"releaseName,omitempty"` + + // TargetNamespace to target when performing operations for the HelmRelease. + // Defaults to the namespace of the HelmRelease. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + // +kubebuilder:validation:Optional + // +optional + TargetNamespace string `json:"targetNamespace,omitempty"` + + // StorageNamespace used for the Helm storage. + // Defaults to the namespace of the HelmRelease. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + // +kubebuilder:validation:Optional + // +optional + StorageNamespace string `json:"storageNamespace,omitempty"` + + // DependsOn may contain a meta.NamespacedObjectReference slice with + // references to HelmRelease resources that must be ready before this HelmRelease + // can be reconciled. + // +optional + DependsOn []meta.NamespacedObjectReference `json:"dependsOn,omitempty"` + + // Timeout is the time to wait for any individual Kubernetes operation (like Jobs + // for hooks) during the performance of a Helm action. Defaults to '5m0s'. + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // MaxHistory is the number of revisions saved by Helm for this HelmRelease. + // Use '0' for an unlimited number of revisions; defaults to '5'. + // +optional + MaxHistory *int `json:"maxHistory,omitempty"` + + // The name of the Kubernetes service account to impersonate + // when reconciling this HelmRelease. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` + + // PersistentClient tells the controller to use a persistent Kubernetes + // client for this release. When enabled, the client will be reused for the + // duration of the reconciliation, instead of being created and destroyed + // for each (step of a) Helm action. + // + // This can improve performance, but may cause issues with some Helm charts + // that for example do create Custom Resource Definitions during installation + // outside Helm's CRD lifecycle hooks, which are then not observed to be + // available by e.g. post-install hooks. + // + // If not set, it defaults to true. + // + // +optional + PersistentClient *bool `json:"persistentClient,omitempty"` + + // DriftDetection holds the configuration for detecting and handling + // differences between the manifest in the Helm storage and the resources + // currently existing in the cluster. + // +optional + DriftDetection *DriftDetection `json:"driftDetection,omitempty"` + + // Install holds the configuration for Helm install actions for this HelmRelease. + // +optional + Install *Install `json:"install,omitempty"` + + // Upgrade holds the configuration for Helm upgrade actions for this HelmRelease. + // +optional + Upgrade *Upgrade `json:"upgrade,omitempty"` + + // Test holds the configuration for Helm test actions for this HelmRelease. + // +optional + Test *Test `json:"test,omitempty"` + + // Rollback holds the configuration for Helm rollback actions for this HelmRelease. + // +optional + Rollback *Rollback `json:"rollback,omitempty"` + + // Uninstall holds the configuration for Helm uninstall actions for this HelmRelease. + // +optional + Uninstall *Uninstall `json:"uninstall,omitempty"` + + // ValuesFrom holds references to resources containing Helm values for this HelmRelease, + // and information about how they should be merged. + ValuesFrom []ValuesReference `json:"valuesFrom,omitempty"` + + // Values holds the values for this Helm release. + // +optional + Values *apiextensionsv1.JSON `json:"values,omitempty"` + + // PostRenderers holds an array of Helm PostRenderers, which will be applied in order + // of their definition. + // +optional + PostRenderers []PostRenderer `json:"postRenderers,omitempty"` +} + +// DriftDetectionMode represents the modes in which a controller can detect and +// handle differences between the manifest in the Helm storage and the resources +// currently existing in the cluster. +type DriftDetectionMode string + +var ( + // DriftDetectionEnabled instructs the controller to actively detect any + // changes between the manifest in the Helm storage and the resources + // currently existing in the cluster. + // If any differences are detected, the controller will automatically + // correct the cluster state by performing a Helm upgrade. + DriftDetectionEnabled DriftDetectionMode = "enabled" + + // DriftDetectionWarn instructs the controller to actively detect any + // changes between the manifest in the Helm storage and the resources + // currently existing in the cluster. + // If any differences are detected, the controller will emit a warning + // without automatically correcting the cluster state. + DriftDetectionWarn DriftDetectionMode = "warn" + + // DriftDetectionDisabled instructs the controller to skip detection of + // differences entirely. + // This is the default behavior, and the controller will not actively + // detect or respond to differences between the manifest in the Helm + // storage and the resources currently existing in the cluster. + DriftDetectionDisabled DriftDetectionMode = "disabled" +) + +var ( + // DriftDetectionMetadataKey is the label or annotation key used to disable + // the diffing of an object. + DriftDetectionMetadataKey = GroupVersion.Group + "/driftDetection" + // DriftDetectionDisabledValue is the value used to disable the diffing of + // an object using DriftDetectionMetadataKey. + DriftDetectionDisabledValue = "disabled" +) + +// IgnoreRule defines a rule to selectively disregard specific changes during +// the drift detection process. +type IgnoreRule struct { + // Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from + // consideration in a Kubernetes object. + // +required + Paths []string `json:"paths"` + + // Target is a selector for specifying Kubernetes objects to which this + // rule applies. + // If Target is not set, the Paths will be ignored for all Kubernetes + // objects within the manifest of the Helm release. + // +optional + Target *kustomize.Selector `json:"target,omitempty"` +} + +// DriftDetection defines the strategy for performing differential analysis and +// provides a way to define rules for ignoring specific changes during this +// process. +type DriftDetection struct { + // Mode defines how differences should be handled between the Helm manifest + // and the manifest currently applied to the cluster. + // If not explicitly set, it defaults to DiffModeDisabled. + // +kubebuilder:validation:Enum=enabled;warn;disabled + // +optional + Mode DriftDetectionMode `json:"mode,omitempty"` + + // Ignore contains a list of rules for specifying which changes to ignore + // during diffing. + // +optional + Ignore []IgnoreRule `json:"ignore,omitempty"` +} + +// GetMode returns the DiffMode set on the Diff, or DiffModeDisabled if not +// set. +func (d DriftDetection) GetMode() DriftDetectionMode { + if d.Mode == "" { + return DriftDetectionDisabled + } + return d.Mode +} + +// MustDetectChanges returns true if the DiffMode is set to DiffModeEnabled or +// DiffModeWarn. +func (d DriftDetection) MustDetectChanges() bool { + return d.GetMode() == DriftDetectionEnabled || d.GetMode() == DriftDetectionWarn +} + +// HelmChartTemplate defines the template from which the controller will +// generate a v1beta2.HelmChart object in the same namespace as the referenced +// v1.Source. +type HelmChartTemplate struct { + // ObjectMeta holds the template for metadata like labels and annotations. + // +optional + ObjectMeta *HelmChartTemplateObjectMeta `json:"metadata,omitempty"` + + // Spec holds the template for the v1beta2.HelmChartSpec for this HelmRelease. + // +required + Spec HelmChartTemplateSpec `json:"spec"` +} + +// HelmChartTemplateObjectMeta defines the template for the ObjectMeta of a +// v1beta2.HelmChart. +type HelmChartTemplateObjectMeta struct { + // Map of string keys and values that can be used to organize and categorize + // (scope and select) objects. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // +optional + Labels map[string]string `json:"labels,omitempty"` + + // Annotations is an unstructured key value map stored with a resource that may be + // set by external tools to store and retrieve arbitrary metadata. They are not + // queryable and should be preserved when modifying objects. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + // +optional + Annotations map[string]string `json:"annotations,omitempty"` +} + +// HelmChartTemplateSpec defines the template from which the controller will +// generate a v1beta2.HelmChartSpec object. +type HelmChartTemplateSpec struct { + // The name or path the Helm chart is available at in the SourceRef. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=2048 + // +required + Chart string `json:"chart"` + + // Version semver expression, ignored for charts from v1beta2.GitRepository and + // v1beta2.Bucket sources. Defaults to latest when omitted. + // +kubebuilder:default:=* + // +optional + Version string `json:"version,omitempty"` + + // The name and namespace of the v1.Source the chart is available at. + // +required + SourceRef CrossNamespaceObjectReference `json:"sourceRef"` + + // Interval at which to check the v1.Source for updates. Defaults to + // 'HelmReleaseSpec.Interval'. + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + // +optional + Interval *metav1.Duration `json:"interval,omitempty"` + + // Determines what enables the creation of a new artifact. Valid values are + // ('ChartVersion', 'Revision'). + // See the documentation of the values for an explanation on their behavior. + // Defaults to ChartVersion when omitted. + // +kubebuilder:validation:Enum=ChartVersion;Revision + // +kubebuilder:default:=ChartVersion + // +optional + ReconcileStrategy string `json:"reconcileStrategy,omitempty"` + + // Alternative list of values files to use as the chart values (values.yaml + // is not included by default), expected to be a relative path in the SourceRef. + // Values files are merged in the order of this list with the last file overriding + // the first. Ignored when omitted. + // +optional + ValuesFiles []string `json:"valuesFiles,omitempty"` + + // IgnoreMissingValuesFiles controls whether to silently ignore missing values files rather than failing. + // +optional + IgnoreMissingValuesFiles bool `json:"ignoreMissingValuesFiles,omitempty"` + + // Verify contains the secret name containing the trusted public keys + // used to verify the signature and specifies which provider to use to check + // whether OCI image is authentic. + // This field is only supported for OCI sources. + // Chart dependencies, which are not bundled in the umbrella chart artifact, + // are not verified. + // +optional + Verify *HelmChartTemplateVerification `json:"verify,omitempty"` +} + +// GetInterval returns the configured interval for the v1beta2.HelmChart, +// or the given default. +func (in HelmChartTemplate) GetInterval(defaultInterval metav1.Duration) metav1.Duration { + if in.Spec.Interval == nil { + return defaultInterval + } + return *in.Spec.Interval +} + +// GetNamespace returns the namespace targeted namespace for the +// v1beta2.HelmChart, or the given default. +func (in HelmChartTemplate) GetNamespace(defaultNamespace string) string { + if in.Spec.SourceRef.Namespace == "" { + return defaultNamespace + } + return in.Spec.SourceRef.Namespace +} + +// HelmChartTemplateVerification verifies the authenticity of an OCI Helm chart. +type HelmChartTemplateVerification struct { + // Provider specifies the technology used to sign the OCI Helm chart. + // +kubebuilder:validation:Enum=cosign;notation + // +kubebuilder:default:=cosign + Provider string `json:"provider"` + + // SecretRef specifies the Kubernetes Secret containing the + // trusted public keys. + // +optional + SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"` +} + +// Remediation defines a consistent interface for InstallRemediation and +// UpgradeRemediation. +// +kubebuilder:object:generate=false +type Remediation interface { + GetRetries() int + MustIgnoreTestFailures(bool) bool + MustRemediateLastFailure() bool + GetStrategy() RemediationStrategy + GetFailureCount(hr *HelmRelease) int64 + IncrementFailureCount(hr *HelmRelease) + RetriesExhausted(hr *HelmRelease) bool +} + +// Install holds the configuration for Helm install actions performed for this +// HelmRelease. +type Install struct { + // Timeout is the time to wait for any individual Kubernetes operation (like + // Jobs for hooks) during the performance of a Helm install action. Defaults to + // 'HelmReleaseSpec.Timeout'. + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // Remediation holds the remediation configuration for when the Helm install + // action for the HelmRelease fails. The default is to not perform any action. + // +optional + Remediation *InstallRemediation `json:"remediation,omitempty"` + + // DisableWait disables the waiting for resources to be ready after a Helm + // install has been performed. + // +optional + DisableWait bool `json:"disableWait,omitempty"` + + // DisableWaitForJobs disables waiting for jobs to complete after a Helm + // install has been performed. + // +optional + DisableWaitForJobs bool `json:"disableWaitForJobs,omitempty"` + + // DisableHooks prevents hooks from running during the Helm install action. + // +optional + DisableHooks bool `json:"disableHooks,omitempty"` + + // DisableOpenAPIValidation prevents the Helm install action from validating + // rendered templates against the Kubernetes OpenAPI Schema. + // +optional + DisableOpenAPIValidation bool `json:"disableOpenAPIValidation,omitempty"` + + // Replace tells the Helm install action to re-use the 'ReleaseName', but only + // if that name is a deleted release which remains in the history. + // +optional + Replace bool `json:"replace,omitempty"` + + // SkipCRDs tells the Helm install action to not install any CRDs. By default, + // CRDs are installed if not already present. + // + // Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. + // + // +deprecated + // +optional + SkipCRDs bool `json:"skipCRDs,omitempty"` + + // CRDs upgrade CRDs from the Helm Chart's crds directory according + // to the CRD upgrade policy provided here. Valid values are `Skip`, + // `Create` or `CreateReplace`. Default is `Create` and if omitted + // CRDs are installed but not updated. + // + // Skip: do neither install nor replace (update) any CRDs. + // + // Create: new CRDs are created, existing CRDs are neither updated nor deleted. + // + // CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + // but not deleted. + // + // By default, CRDs are applied (installed) during Helm install action. + // With this option users can opt in to CRD replace existing CRDs on Helm + // install actions, which is not (yet) natively supported by Helm. + // https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + // + // +kubebuilder:validation:Enum=Skip;Create;CreateReplace + // +optional + CRDs CRDsPolicy `json:"crds,omitempty"` + + // CreateNamespace tells the Helm install action to create the + // HelmReleaseSpec.TargetNamespace if it does not exist yet. + // On uninstall, the namespace will not be garbage collected. + // +optional + CreateNamespace bool `json:"createNamespace,omitempty"` +} + +// GetTimeout returns the configured timeout for the Helm install action, +// or the given default. +func (in Install) GetTimeout(defaultTimeout metav1.Duration) metav1.Duration { + if in.Timeout == nil { + return defaultTimeout + } + return *in.Timeout +} + +// GetRemediation returns the configured Remediation for the Helm install action. +func (in Install) GetRemediation() Remediation { + if in.Remediation == nil { + return InstallRemediation{} + } + return *in.Remediation +} + +// InstallRemediation holds the configuration for Helm install remediation. +type InstallRemediation struct { + // Retries is the number of retries that should be attempted on failures before + // bailing. Remediation, using an uninstall, is performed between each attempt. + // Defaults to '0', a negative integer equals to unlimited retries. + // +optional + Retries int `json:"retries,omitempty"` + + // IgnoreTestFailures tells the controller to skip remediation when the Helm + // tests are run after an install action but fail. Defaults to + // 'Test.IgnoreFailures'. + // +optional + IgnoreTestFailures *bool `json:"ignoreTestFailures,omitempty"` + + // RemediateLastFailure tells the controller to remediate the last failure, when + // no retries remain. Defaults to 'false'. + // +optional + RemediateLastFailure *bool `json:"remediateLastFailure,omitempty"` +} + +// GetRetries returns the number of retries that should be attempted on +// failures. +func (in InstallRemediation) GetRetries() int { + return in.Retries +} + +// MustIgnoreTestFailures returns the configured IgnoreTestFailures or the given +// default. +func (in InstallRemediation) MustIgnoreTestFailures(def bool) bool { + if in.IgnoreTestFailures == nil { + return def + } + return *in.IgnoreTestFailures +} + +// MustRemediateLastFailure returns whether to remediate the last failure when +// no retries remain. +func (in InstallRemediation) MustRemediateLastFailure() bool { + if in.RemediateLastFailure == nil { + return false + } + return *in.RemediateLastFailure +} + +// GetStrategy returns the strategy to use for failure remediation. +func (in InstallRemediation) GetStrategy() RemediationStrategy { + return UninstallRemediationStrategy +} + +// GetFailureCount gets the failure count. +func (in InstallRemediation) GetFailureCount(hr *HelmRelease) int64 { + return hr.Status.InstallFailures +} + +// IncrementFailureCount increments the failure count. +func (in InstallRemediation) IncrementFailureCount(hr *HelmRelease) { + hr.Status.InstallFailures++ +} + +// RetriesExhausted returns true if there are no remaining retries. +func (in InstallRemediation) RetriesExhausted(hr *HelmRelease) bool { + return in.Retries >= 0 && in.GetFailureCount(hr) > int64(in.Retries) +} + +// CRDsPolicy defines the install/upgrade approach to use for CRDs when +// installing or upgrading a HelmRelease. +type CRDsPolicy string + +const ( + // Skip CRDs do neither install nor replace (update) any CRDs. + Skip CRDsPolicy = "Skip" + // Create CRDs which do not already exist, do not replace (update) already existing + // CRDs and keep (do not delete) CRDs which no longer exist in the current release. + Create CRDsPolicy = "Create" + // Create CRDs which do not already exist, Replace (update) already existing CRDs + // and keep (do not delete) CRDs which no longer exist in the current release. + CreateReplace CRDsPolicy = "CreateReplace" +) + +// Upgrade holds the configuration for Helm upgrade actions for this +// HelmRelease. +type Upgrade struct { + // Timeout is the time to wait for any individual Kubernetes operation (like + // Jobs for hooks) during the performance of a Helm upgrade action. Defaults to + // 'HelmReleaseSpec.Timeout'. + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // Remediation holds the remediation configuration for when the Helm upgrade + // action for the HelmRelease fails. The default is to not perform any action. + // +optional + Remediation *UpgradeRemediation `json:"remediation,omitempty"` + + // DisableWait disables the waiting for resources to be ready after a Helm + // upgrade has been performed. + // +optional + DisableWait bool `json:"disableWait,omitempty"` + + // DisableWaitForJobs disables waiting for jobs to complete after a Helm + // upgrade has been performed. + // +optional + DisableWaitForJobs bool `json:"disableWaitForJobs,omitempty"` + + // DisableHooks prevents hooks from running during the Helm upgrade action. + // +optional + DisableHooks bool `json:"disableHooks,omitempty"` + + // DisableOpenAPIValidation prevents the Helm upgrade action from validating + // rendered templates against the Kubernetes OpenAPI Schema. + // +optional + DisableOpenAPIValidation bool `json:"disableOpenAPIValidation,omitempty"` + + // Force forces resource updates through a replacement strategy. + // +optional + Force bool `json:"force,omitempty"` + + // PreserveValues will make Helm reuse the last release's values and merge in + // overrides from 'Values'. Setting this flag makes the HelmRelease + // non-declarative. + // +optional + PreserveValues bool `json:"preserveValues,omitempty"` + + // CleanupOnFail allows deletion of new resources created during the Helm + // upgrade action when it fails. + // +optional + CleanupOnFail bool `json:"cleanupOnFail,omitempty"` + + // CRDs upgrade CRDs from the Helm Chart's crds directory according + // to the CRD upgrade policy provided here. Valid values are `Skip`, + // `Create` or `CreateReplace`. Default is `Skip` and if omitted + // CRDs are neither installed nor upgraded. + // + // Skip: do neither install nor replace (update) any CRDs. + // + // Create: new CRDs are created, existing CRDs are neither updated nor deleted. + // + // CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + // but not deleted. + // + // By default, CRDs are not applied during Helm upgrade action. With this + // option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. + // https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + // + // +kubebuilder:validation:Enum=Skip;Create;CreateReplace + // +optional + CRDs CRDsPolicy `json:"crds,omitempty"` +} + +// GetTimeout returns the configured timeout for the Helm upgrade action, or the +// given default. +func (in Upgrade) GetTimeout(defaultTimeout metav1.Duration) metav1.Duration { + if in.Timeout == nil { + return defaultTimeout + } + return *in.Timeout +} + +// GetRemediation returns the configured Remediation for the Helm upgrade +// action. +func (in Upgrade) GetRemediation() Remediation { + if in.Remediation == nil { + return UpgradeRemediation{} + } + return *in.Remediation +} + +// UpgradeRemediation holds the configuration for Helm upgrade remediation. +type UpgradeRemediation struct { + // Retries is the number of retries that should be attempted on failures before + // bailing. Remediation, using 'Strategy', is performed between each attempt. + // Defaults to '0', a negative integer equals to unlimited retries. + // +optional + Retries int `json:"retries,omitempty"` + + // IgnoreTestFailures tells the controller to skip remediation when the Helm + // tests are run after an upgrade action but fail. + // Defaults to 'Test.IgnoreFailures'. + // +optional + IgnoreTestFailures *bool `json:"ignoreTestFailures,omitempty"` + + // RemediateLastFailure tells the controller to remediate the last failure, when + // no retries remain. Defaults to 'false' unless 'Retries' is greater than 0. + // +optional + RemediateLastFailure *bool `json:"remediateLastFailure,omitempty"` + + // Strategy to use for failure remediation. Defaults to 'rollback'. + // +kubebuilder:validation:Enum=rollback;uninstall + // +optional + Strategy *RemediationStrategy `json:"strategy,omitempty"` +} + +// GetRetries returns the number of retries that should be attempted on +// failures. +func (in UpgradeRemediation) GetRetries() int { + return in.Retries +} + +// MustIgnoreTestFailures returns the configured IgnoreTestFailures or the given +// default. +func (in UpgradeRemediation) MustIgnoreTestFailures(def bool) bool { + if in.IgnoreTestFailures == nil { + return def + } + return *in.IgnoreTestFailures +} + +// MustRemediateLastFailure returns whether to remediate the last failure when +// no retries remain. +func (in UpgradeRemediation) MustRemediateLastFailure() bool { + if in.RemediateLastFailure == nil { + return in.Retries > 0 + } + return *in.RemediateLastFailure +} + +// GetStrategy returns the strategy to use for failure remediation. +func (in UpgradeRemediation) GetStrategy() RemediationStrategy { + if in.Strategy == nil { + return RollbackRemediationStrategy + } + return *in.Strategy +} + +// GetFailureCount gets the failure count. +func (in UpgradeRemediation) GetFailureCount(hr *HelmRelease) int64 { + return hr.Status.UpgradeFailures +} + +// IncrementFailureCount increments the failure count. +func (in UpgradeRemediation) IncrementFailureCount(hr *HelmRelease) { + hr.Status.UpgradeFailures++ +} + +// RetriesExhausted returns true if there are no remaining retries. +func (in UpgradeRemediation) RetriesExhausted(hr *HelmRelease) bool { + return in.Retries >= 0 && in.GetFailureCount(hr) > int64(in.Retries) +} + +// RemediationStrategy returns the strategy to use to remediate a failed install +// or upgrade. +type RemediationStrategy string + +const ( + // RollbackRemediationStrategy represents a Helm remediation strategy of Helm + // rollback. + RollbackRemediationStrategy RemediationStrategy = "rollback" + + // UninstallRemediationStrategy represents a Helm remediation strategy of Helm + // uninstall. + UninstallRemediationStrategy RemediationStrategy = "uninstall" +) + +// Test holds the configuration for Helm test actions for this HelmRelease. +type Test struct { + // Enable enables Helm test actions for this HelmRelease after an Helm install + // or upgrade action has been performed. + // +optional + Enable bool `json:"enable,omitempty"` + + // Timeout is the time to wait for any individual Kubernetes operation during + // the performance of a Helm test action. Defaults to 'HelmReleaseSpec.Timeout'. + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // IgnoreFailures tells the controller to skip remediation when the Helm tests + // are run but fail. Can be overwritten for tests run after install or upgrade + // actions in 'Install.IgnoreTestFailures' and 'Upgrade.IgnoreTestFailures'. + // +optional + IgnoreFailures bool `json:"ignoreFailures,omitempty"` + + // Filters is a list of tests to run or exclude from running. + Filters *[]Filter `json:"filters,omitempty"` +} + +// GetTimeout returns the configured timeout for the Helm test action, +// or the given default. +func (in Test) GetTimeout(defaultTimeout metav1.Duration) metav1.Duration { + if in.Timeout == nil { + return defaultTimeout + } + return *in.Timeout +} + +// Filter holds the configuration for individual Helm test filters. +type Filter struct { + // Name is the name of the test. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +required + Name string `json:"name"` + // Exclude specifies whether the named test should be excluded. + // +optional + Exclude bool `json:"exclude,omitempty"` +} + +// GetFilters returns the configured filters for the Helm test action/ +func (in Test) GetFilters() []Filter { + if in.Filters == nil { + var filters []Filter + return filters + } + return *in.Filters +} + +// Rollback holds the configuration for Helm rollback actions for this +// HelmRelease. +type Rollback struct { + // Timeout is the time to wait for any individual Kubernetes operation (like + // Jobs for hooks) during the performance of a Helm rollback action. Defaults to + // 'HelmReleaseSpec.Timeout'. + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // DisableWait disables the waiting for resources to be ready after a Helm + // rollback has been performed. + // +optional + DisableWait bool `json:"disableWait,omitempty"` + + // DisableWaitForJobs disables waiting for jobs to complete after a Helm + // rollback has been performed. + // +optional + DisableWaitForJobs bool `json:"disableWaitForJobs,omitempty"` + + // DisableHooks prevents hooks from running during the Helm rollback action. + // +optional + DisableHooks bool `json:"disableHooks,omitempty"` + + // Recreate performs pod restarts for the resource if applicable. + // +optional + Recreate bool `json:"recreate,omitempty"` + + // Force forces resource updates through a replacement strategy. + // +optional + Force bool `json:"force,omitempty"` + + // CleanupOnFail allows deletion of new resources created during the Helm + // rollback action when it fails. + // +optional + CleanupOnFail bool `json:"cleanupOnFail,omitempty"` +} + +// GetTimeout returns the configured timeout for the Helm rollback action, or +// the given default. +func (in Rollback) GetTimeout(defaultTimeout metav1.Duration) metav1.Duration { + if in.Timeout == nil { + return defaultTimeout + } + return *in.Timeout +} + +// Uninstall holds the configuration for Helm uninstall actions for this +// HelmRelease. +type Uninstall struct { + // Timeout is the time to wait for any individual Kubernetes operation (like + // Jobs for hooks) during the performance of a Helm uninstall action. Defaults + // to 'HelmReleaseSpec.Timeout'. + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // DisableHooks prevents hooks from running during the Helm rollback action. + // +optional + DisableHooks bool `json:"disableHooks,omitempty"` + + // KeepHistory tells Helm to remove all associated resources and mark the + // release as deleted, but retain the release history. + // +optional + KeepHistory bool `json:"keepHistory,omitempty"` + + // DisableWait disables waiting for all the resources to be deleted after + // a Helm uninstall is performed. + // +optional + DisableWait bool `json:"disableWait,omitempty"` + + // DeletionPropagation specifies the deletion propagation policy when + // a Helm uninstall is performed. + // +kubebuilder:default=background + // +kubebuilder:validation:Enum=background;foreground;orphan + // +optional + DeletionPropagation *string `json:"deletionPropagation,omitempty"` +} + +// GetTimeout returns the configured timeout for the Helm uninstall action, or +// the given default. +func (in Uninstall) GetTimeout(defaultTimeout metav1.Duration) metav1.Duration { + if in.Timeout == nil { + return defaultTimeout + } + return *in.Timeout +} + +// GetDeletionPropagation returns the configured deletion propagation policy +// for the Helm uninstall action, or 'background'. +func (in Uninstall) GetDeletionPropagation() string { + if in.DeletionPropagation == nil { + return "background" + } + return *in.DeletionPropagation +} + +// ReleaseAction is the action to perform a Helm release. +type ReleaseAction string + +const ( + // ReleaseActionInstall represents a Helm install action. + ReleaseActionInstall ReleaseAction = "install" + // ReleaseActionUpgrade represents a Helm upgrade action. + ReleaseActionUpgrade ReleaseAction = "upgrade" +) + +// HelmReleaseStatus defines the observed state of a HelmRelease. +type HelmReleaseStatus struct { + // ObservedGeneration is the last observed generation. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // LastAttemptedGeneration is the last generation the controller attempted + // to reconcile. + // +optional + LastAttemptedGeneration int64 `json:"lastAttemptedGeneration,omitempty"` + + // Conditions holds the conditions for the HelmRelease. + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // HelmChart is the namespaced name of the HelmChart resource created by + // the controller for the HelmRelease. + // +optional + HelmChart string `json:"helmChart,omitempty"` + + // StorageNamespace is the namespace of the Helm release storage for the + // current release. + // +kubebuilder:validation:MaxLength=63 + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:Optional + // +optional + StorageNamespace string `json:"storageNamespace,omitempty"` + + // History holds the history of Helm releases performed for this HelmRelease + // up to the last successfully completed release. + // +optional + History Snapshots `json:"history,omitempty"` + + // LastAttemptedReleaseAction is the last release action performed for this + // HelmRelease. It is used to determine the active remediation strategy. + // +kubebuilder:validation:Enum=install;upgrade + // +optional + LastAttemptedReleaseAction ReleaseAction `json:"lastAttemptedReleaseAction,omitempty"` + + // Failures is the reconciliation failure count against the latest desired + // state. It is reset after a successful reconciliation. + // +optional + Failures int64 `json:"failures,omitempty"` + + // InstallFailures is the install failure count against the latest desired + // state. It is reset after a successful reconciliation. + // +optional + InstallFailures int64 `json:"installFailures,omitempty"` + + // UpgradeFailures is the upgrade failure count against the latest desired + // state. It is reset after a successful reconciliation. + // +optional + UpgradeFailures int64 `json:"upgradeFailures,omitempty"` + + // LastAttemptedRevision is the Source revision of the last reconciliation + // attempt. For OCIRepository sources, the 12 first characters of the digest are + // appended to the chart version e.g. "1.2.3+1234567890ab". + // +optional + LastAttemptedRevision string `json:"lastAttemptedRevision,omitempty"` + + // LastAttemptedRevisionDigest is the digest of the last reconciliation attempt. + // This is only set for OCIRepository sources. + // +optional + LastAttemptedRevisionDigest string `json:"lastAttemptedRevisionDigest,omitempty"` + + // LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last + // reconciliation attempt. + // Deprecated: Use LastAttemptedConfigDigest instead. + // +optional + LastAttemptedValuesChecksum string `json:"lastAttemptedValuesChecksum,omitempty"` + + // LastReleaseRevision is the revision of the last successful Helm release. + // Deprecated: Use History instead. + // +optional + LastReleaseRevision int `json:"lastReleaseRevision,omitempty"` + + // LastAttemptedConfigDigest is the digest for the config (better known as + // "values") of the last reconciliation attempt. + // +optional + LastAttemptedConfigDigest string `json:"lastAttemptedConfigDigest,omitempty"` + + // LastHandledForceAt holds the value of the most recent force request + // value, so a change of the annotation value can be detected. + // +optional + LastHandledForceAt string `json:"lastHandledForceAt,omitempty"` + + // LastHandledResetAt holds the value of the most recent reset request + // value, so a change of the annotation value can be detected. + // +optional + LastHandledResetAt string `json:"lastHandledResetAt,omitempty"` + + meta.ReconcileRequestStatus `json:",inline"` +} + +// ClearHistory clears the History. +func (in *HelmReleaseStatus) ClearHistory() { + in.History = nil +} + +// ClearFailures clears the failure counters. +func (in *HelmReleaseStatus) ClearFailures() { + in.Failures = 0 + in.InstallFailures = 0 + in.UpgradeFailures = 0 +} + +// GetHelmChart returns the namespace and name of the HelmChart. +func (in HelmReleaseStatus) GetHelmChart() (string, string) { + if in.HelmChart == "" { + return "", "" + } + if split := strings.Split(in.HelmChart, string(types.Separator)); len(split) > 1 { + return split[0], split[1] + } + return "", "" +} + +func (in *HelmReleaseStatus) GetLastAttemptedRevision() string { + return in.LastAttemptedRevision +} + +const ( + // SourceIndexKey is the key used for indexing HelmReleases based on + // their sources. + SourceIndexKey string = ".metadata.source" +) + +// +genclient +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=hr +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="" +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="" + +// HelmRelease is the Schema for the helmreleases API +type HelmRelease struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec HelmReleaseSpec `json:"spec,omitempty"` + // +kubebuilder:default:={"observedGeneration":-1} + Status HelmReleaseStatus `json:"status,omitempty"` +} + +// GetDriftDetection returns the configuration for detecting and handling +// differences between the manifest in the Helm storage and the resources +// currently existing in the cluster. +func (in *HelmRelease) GetDriftDetection() DriftDetection { + if in.Spec.DriftDetection == nil { + return DriftDetection{} + } + return *in.Spec.DriftDetection +} + +// GetInstall returns the configuration for Helm install actions for the +// HelmRelease. +func (in *HelmRelease) GetInstall() Install { + if in.Spec.Install == nil { + return Install{} + } + return *in.Spec.Install +} + +// GetUpgrade returns the configuration for Helm upgrade actions for this +// HelmRelease. +func (in *HelmRelease) GetUpgrade() Upgrade { + if in.Spec.Upgrade == nil { + return Upgrade{} + } + return *in.Spec.Upgrade +} + +// GetTest returns the configuration for Helm test actions for this HelmRelease. +func (in *HelmRelease) GetTest() Test { + if in.Spec.Test == nil { + return Test{} + } + return *in.Spec.Test +} + +// GetRollback returns the configuration for Helm rollback actions for this +// HelmRelease. +func (in *HelmRelease) GetRollback() Rollback { + if in.Spec.Rollback == nil { + return Rollback{} + } + return *in.Spec.Rollback +} + +// GetUninstall returns the configuration for Helm uninstall actions for this +// HelmRelease. +func (in *HelmRelease) GetUninstall() Uninstall { + if in.Spec.Uninstall == nil { + return Uninstall{} + } + return *in.Spec.Uninstall +} + +// GetActiveRemediation returns the active Remediation configuration for the +// HelmRelease. +func (in HelmRelease) GetActiveRemediation() Remediation { + switch in.Status.LastAttemptedReleaseAction { + case ReleaseActionInstall: + return in.GetInstall().GetRemediation() + case ReleaseActionUpgrade: + return in.GetUpgrade().GetRemediation() + default: + return nil + } +} + +// GetRequeueAfter returns the duration after which the HelmRelease +// must be reconciled again. +func (in HelmRelease) GetRequeueAfter() time.Duration { + return in.Spec.Interval.Duration +} + +// GetValues unmarshals the raw values to a map[string]interface{} and returns +// the result. +func (in HelmRelease) GetValues() map[string]interface{} { + var values map[string]interface{} + if in.Spec.Values != nil { + _ = yaml.Unmarshal(in.Spec.Values.Raw, &values) + } + return values +} + +// GetReleaseName returns the configured release name, or a composition of +// '[TargetNamespace-]Name'. +func (in HelmRelease) GetReleaseName() string { + if in.Spec.ReleaseName != "" { + return in.Spec.ReleaseName + } + if in.Spec.TargetNamespace != "" { + return strings.Join([]string{in.Spec.TargetNamespace, in.Name}, "-") + } + return in.Name +} + +// GetReleaseNamespace returns the configured TargetNamespace, or the namespace +// of the HelmRelease. +func (in HelmRelease) GetReleaseNamespace() string { + if in.Spec.TargetNamespace != "" { + return in.Spec.TargetNamespace + } + return in.Namespace +} + +// GetStorageNamespace returns the configured StorageNamespace for helm, or the namespace +// of the HelmRelease. +func (in HelmRelease) GetStorageNamespace() string { + if in.Spec.StorageNamespace != "" { + return in.Spec.StorageNamespace + } + return in.Namespace +} + +// GetHelmChartName returns the name used by the controller for the HelmChart creation. +func (in HelmRelease) GetHelmChartName() string { + return strings.Join([]string{in.Namespace, in.Name}, "-") +} + +// GetTimeout returns the configured Timeout, or the default of 300s. +func (in HelmRelease) GetTimeout() metav1.Duration { + if in.Spec.Timeout == nil { + return metav1.Duration{Duration: 300 * time.Second} + } + return *in.Spec.Timeout +} + +// GetMaxHistory returns the configured MaxHistory, or the default of 5. +func (in HelmRelease) GetMaxHistory() int { + if in.Spec.MaxHistory == nil { + return defaultMaxHistory + } + return *in.Spec.MaxHistory +} + +// UsePersistentClient returns the configured PersistentClient, or the default +// of true. +func (in HelmRelease) UsePersistentClient() bool { + if in.Spec.PersistentClient == nil { + return true + } + return *in.Spec.PersistentClient +} + +// GetDependsOn returns the list of dependencies across-namespaces. +func (in HelmRelease) GetDependsOn() []meta.NamespacedObjectReference { + return in.Spec.DependsOn +} + +// GetConditions returns the status conditions of the object. +func (in HelmRelease) GetConditions() []metav1.Condition { + return in.Status.Conditions +} + +// SetConditions sets the status conditions on the object. +func (in *HelmRelease) SetConditions(conditions []metav1.Condition) { + in.Status.Conditions = conditions +} + +// HasChartRef returns true if the HelmRelease has a ChartRef. +func (in *HelmRelease) HasChartRef() bool { + return in.Spec.ChartRef != nil +} + +// HasChartTemplate returns true if the HelmRelease has a ChartTemplate. +func (in *HelmRelease) HasChartTemplate() bool { + return in.Spec.Chart.Spec.Chart != "" +} + +// +kubebuilder:object:root=true + +// HelmReleaseList contains a list of HelmRelease objects. +type HelmReleaseList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []HelmRelease `json:"items"` +} + +func init() { + SchemeBuilder.Register(&HelmRelease{}, &HelmReleaseList{}) +} diff --git a/api/v2/reference_types.go b/api/v2/reference_types.go new file mode 100644 index 000000000..fe7003e34 --- /dev/null +++ b/api/v2/reference_types.go @@ -0,0 +1,115 @@ +/* +Copyright 2024 The Flux 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 v2 + +// CrossNamespaceObjectReference contains enough information to let you locate +// the typed referenced object at cluster level. +type CrossNamespaceObjectReference struct { + // APIVersion of the referent. + // +optional + APIVersion string `json:"apiVersion,omitempty"` + + // Kind of the referent. + // +kubebuilder:validation:Enum=HelmRepository;GitRepository;Bucket + // +required + Kind string `json:"kind,omitempty"` + + // Name of the referent. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +required + Name string `json:"name"` + + // Namespace of the referent. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + // +kubebuilder:validation:Optional + // +optional + Namespace string `json:"namespace,omitempty"` +} + +// CrossNamespaceSourceReference contains enough information to let you locate +// the typed referenced object at cluster level. +type CrossNamespaceSourceReference struct { + // APIVersion of the referent. + // +optional + APIVersion string `json:"apiVersion,omitempty"` + + // Kind of the referent. + // +kubebuilder:validation:Enum=OCIRepository;HelmChart + // +required + Kind string `json:"kind"` + + // Name of the referent. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +required + Name string `json:"name"` + + // Namespace of the referent, defaults to the namespace of the Kubernetes + // resource object that contains the reference. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + // +kubebuilder:validation:Optional + // +optional + Namespace string `json:"namespace,omitempty"` +} + +// ValuesReference contains a reference to a resource containing Helm values, +// and optionally the key they can be found at. +type ValuesReference struct { + // Kind of the values referent, valid values are ('Secret', 'ConfigMap'). + // +kubebuilder:validation:Enum=Secret;ConfigMap + // +required + Kind string `json:"kind"` + + // Name of the values referent. Should reside in the same namespace as the + // referring resource. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +required + Name string `json:"name"` + + // ValuesKey is the data key where the values.yaml or a specific value can be + // found at. Defaults to 'values.yaml'. + // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:validation:Pattern=`^[\-._a-zA-Z0-9]+$` + // +optional + ValuesKey string `json:"valuesKey,omitempty"` + + // TargetPath is the YAML dot notation path the value should be merged at. When + // set, the ValuesKey is expected to be a single flat value. Defaults to 'None', + // which results in the values getting merged at the root. + // +kubebuilder:validation:MaxLength=250 + // +kubebuilder:validation:Pattern=`^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$` + // +optional + TargetPath string `json:"targetPath,omitempty"` + + // Optional marks this ValuesReference as optional. When set, a not found error + // for the values reference is ignored, but any ValuesKey, TargetPath or + // transient error will still result in a reconciliation failure. + // +optional + Optional bool `json:"optional,omitempty"` +} + +// GetValuesKey returns the defined ValuesKey, or the default ('values.yaml'). +func (in ValuesReference) GetValuesKey() string { + if in.ValuesKey == "" { + return "values.yaml" + } + return in.ValuesKey +} diff --git a/api/v2/snapshot_types.go b/api/v2/snapshot_types.go new file mode 100644 index 000000000..cd254e99a --- /dev/null +++ b/api/v2/snapshot_types.go @@ -0,0 +1,236 @@ +/* +Copyright 2024 The Flux 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 v2 + +import ( + "fmt" + "sort" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // snapshotStatusDeployed indicates that the release the snapshot was taken + // from is currently deployed. + snapshotStatusDeployed = "deployed" + // snapshotStatusSuperseded indicates that the release the snapshot was taken + // from has been superseded by a newer release. + snapshotStatusSuperseded = "superseded" + + // snapshotTestPhaseFailed indicates that the test of the release the snapshot + // was taken from has failed. + snapshotTestPhaseFailed = "Failed" +) + +// Snapshots is a list of Snapshot objects. +type Snapshots []*Snapshot + +// Len returns the number of Snapshots. +func (in Snapshots) Len() int { + return len(in) +} + +// SortByVersion sorts the Snapshots by version, in descending order. +func (in Snapshots) SortByVersion() { + sort.Slice(in, func(i, j int) bool { + return in[i].Version > in[j].Version + }) +} + +// Latest returns the most recent Snapshot. +func (in Snapshots) Latest() *Snapshot { + if len(in) == 0 { + return nil + } + in.SortByVersion() + return in[0] +} + +// Previous returns the most recent Snapshot before the Latest that has a +// status of "deployed" or "superseded", or nil if there is no such Snapshot. +// Unless ignoreTests is true, Snapshots with a test in the "Failed" phase are +// ignored. +func (in Snapshots) Previous(ignoreTests bool) *Snapshot { + if len(in) < 2 { + return nil + } + in.SortByVersion() + for i := range in[1:] { + s := in[i+1] + if s.Status == snapshotStatusDeployed || s.Status == snapshotStatusSuperseded { + if ignoreTests || !s.HasTestInPhase(snapshotTestPhaseFailed) { + return s + } + } + } + return nil +} + +// Truncate removes all Snapshots up to the Previous deployed Snapshot. +// If there is no previous-deployed Snapshot, the most recent 5 Snapshots are +// retained. +func (in *Snapshots) Truncate(ignoreTests bool) { + if in.Len() < 2 { + return + } + + in.SortByVersion() + for i := range (*in)[1:] { + s := (*in)[i+1] + if s.Status == snapshotStatusDeployed || s.Status == snapshotStatusSuperseded { + if ignoreTests || !s.HasTestInPhase(snapshotTestPhaseFailed) { + *in = (*in)[:i+2] + return + } + } + } + + if in.Len() > defaultMaxHistory { + // If none of the Snapshots are deployed or superseded, and there + // are more than the defaultMaxHistory, truncate to the most recent + // Snapshots. + *in = (*in)[:defaultMaxHistory] + } +} + +// Snapshot captures a point-in-time copy of the status information for a Helm release, +// as managed by the controller. +type Snapshot struct { + // APIVersion is the API version of the Snapshot. + // Provisional: when the calculation method of the Digest field is changed, + // this field will be used to distinguish between the old and new methods. + // +optional + APIVersion string `json:"apiVersion,omitempty"` + // Digest is the checksum of the release object in storage. + // It has the format of `:`. + // +required + Digest string `json:"digest"` + // Name is the name of the release. + // +required + Name string `json:"name"` + // Namespace is the namespace the release is deployed to. + // +required + Namespace string `json:"namespace"` + // Version is the version of the release object in storage. + // +required + Version int `json:"version"` + // Status is the current state of the release. + // +required + Status string `json:"status"` + // ChartName is the chart name of the release object in storage. + // +required + ChartName string `json:"chartName"` + // ChartVersion is the chart version of the release object in + // storage. + // +required + ChartVersion string `json:"chartVersion"` + // ConfigDigest is the checksum of the config (better known as + // "values") of the release object in storage. + // It has the format of `:`. + // +required + ConfigDigest string `json:"configDigest"` + // FirstDeployed is when the release was first deployed. + // +required + FirstDeployed metav1.Time `json:"firstDeployed"` + // LastDeployed is when the release was last deployed. + // +required + LastDeployed metav1.Time `json:"lastDeployed"` + // Deleted is when the release was deleted. + // +optional + Deleted metav1.Time `json:"deleted,omitempty"` + // TestHooks is the list of test hooks for the release as observed to be + // run by the controller. + // +optional + TestHooks *map[string]*TestHookStatus `json:"testHooks,omitempty"` + // OCIDigest is the digest of the OCI artifact associated with the release. + // +optional + OCIDigest string `json:"ociDigest,omitempty"` +} + +// FullReleaseName returns the full name of the release in the format +// of '/. +func (in *Snapshot) FullReleaseName() string { + if in == nil { + return "" + } + return fmt.Sprintf("%s/%s.v%d", in.Namespace, in.Name, in.Version) +} + +// VersionedChartName returns the full name of the chart in the format of +// '@'. +func (in *Snapshot) VersionedChartName() string { + if in == nil { + return "" + } + return fmt.Sprintf("%s@%s", in.ChartName, in.ChartVersion) +} + +// HasBeenTested returns true if TestHooks is not nil. This includes an empty +// map, which indicates the chart has no tests. +func (in *Snapshot) HasBeenTested() bool { + return in != nil && in.TestHooks != nil +} + +// GetTestHooks returns the TestHooks for the release if not nil. +func (in *Snapshot) GetTestHooks() map[string]*TestHookStatus { + if in == nil || in.TestHooks == nil { + return nil + } + return *in.TestHooks +} + +// HasTestInPhase returns true if any of the TestHooks is in the given phase. +func (in *Snapshot) HasTestInPhase(phase string) bool { + if in != nil { + for _, h := range in.GetTestHooks() { + if h.Phase == phase { + return true + } + } + } + return false +} + +// SetTestHooks sets the TestHooks for the release. +func (in *Snapshot) SetTestHooks(hooks map[string]*TestHookStatus) { + if in == nil || hooks == nil { + return + } + in.TestHooks = &hooks +} + +// Targets returns true if the Snapshot targets the given release data. +func (in *Snapshot) Targets(name, namespace string, version int) bool { + if in != nil { + return in.Name == name && in.Namespace == namespace && in.Version == version + } + return false +} + +// TestHookStatus holds the status information for a test hook as observed +// to be run by the controller. +type TestHookStatus struct { + // LastStarted is the time the test hook was last started. + // +optional + LastStarted metav1.Time `json:"lastStarted,omitempty"` + // LastCompleted is the time the test hook last completed. + // +optional + LastCompleted metav1.Time `json:"lastCompleted,omitempty"` + // Phase the test hook was observed to be in. + // +optional + Phase string `json:"phase,omitempty"` +} diff --git a/api/v2/snapshot_types_test.go b/api/v2/snapshot_types_test.go new file mode 100644 index 000000000..5c95c39b5 --- /dev/null +++ b/api/v2/snapshot_types_test.go @@ -0,0 +1,298 @@ +/* +Copyright 2024 The Flux 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 v2 + +import ( + "reflect" + "testing" +) + +func TestSnapshots_Sort(t *testing.T) { + tests := []struct { + name string + in Snapshots + want Snapshots + }{ + { + name: "sorts by descending version", + in: Snapshots{ + {Version: 1}, + {Version: 3}, + {Version: 2}, + }, + want: Snapshots{ + {Version: 3}, + {Version: 2}, + {Version: 1}, + }, + }, + { + name: "already sorted", + in: Snapshots{ + {Version: 3}, + {Version: 2}, + {Version: 1}, + }, + want: Snapshots{ + {Version: 3}, + {Version: 2}, + {Version: 1}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.in.SortByVersion() + + if !reflect.DeepEqual(tt.in, tt.want) { + t.Errorf("SortByVersion() got %v, want %v", tt.in, tt.want) + } + }) + } +} + +func TestSnapshots_Latest(t *testing.T) { + tests := []struct { + name string + in Snapshots + want *Snapshot + }{ + { + name: "returns most recent snapshot", + in: Snapshots{ + {Version: 1}, + {Version: 3}, + {Version: 2}, + }, + want: &Snapshot{Version: 3}, + }, + { + name: "returns nil if empty", + in: Snapshots{}, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.in.Latest(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Latest() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSnapshots_Previous(t *testing.T) { + tests := []struct { + name string + in Snapshots + ignoreTests bool + want *Snapshot + }{ + { + name: "returns previous snapshot", + in: Snapshots{ + {Version: 2, Status: "deployed"}, + {Version: 3, Status: "failed"}, + {Version: 1, Status: "superseded"}, + }, + want: &Snapshot{Version: 2, Status: "deployed"}, + }, + { + name: "includes snapshots with failed tests", + in: Snapshots{ + {Version: 4, Status: "deployed"}, + {Version: 1, Status: "superseded"}, + {Version: 2, Status: "superseded"}, + {Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "test": {Phase: "Failed"}, + }}, + }, + ignoreTests: true, + want: &Snapshot{Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "test": {Phase: "Failed"}, + }}, + }, + { + name: "ignores snapshots with failed tests", + in: Snapshots{ + {Version: 4, Status: "deployed"}, + {Version: 1, Status: "superseded"}, + {Version: 2, Status: "superseded"}, + {Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "test": {Phase: "Failed"}, + }}, + }, + ignoreTests: false, + want: &Snapshot{Version: 2, Status: "superseded"}, + }, + { + name: "returns nil without previous snapshot", + in: Snapshots{ + {Version: 1, Status: "deployed"}, + }, + want: nil, + }, + { + name: "returns nil without snapshot matching criteria", + in: Snapshots{ + {Version: 4, Status: "deployed"}, + {Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "test": {Phase: "Failed"}, + }}, + }, + ignoreTests: false, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.in.Previous(tt.ignoreTests); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Previous() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSnapshots_Truncate(t *testing.T) { + tests := []struct { + name string + in Snapshots + ignoreTests bool + want Snapshots + }{ + { + name: "keeps previous snapshot", + in: Snapshots{ + {Version: 1, Status: "superseded"}, + {Version: 3, Status: "failed"}, + {Version: 2, Status: "superseded"}, + {Version: 4, Status: "deployed"}, + }, + want: Snapshots{ + {Version: 4, Status: "deployed"}, + {Version: 3, Status: "failed"}, + {Version: 2, Status: "superseded"}, + }, + }, + { + name: "ignores snapshots with failed tests", + in: Snapshots{ + {Version: 4, Status: "deployed"}, + {Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"}, + "upgrade-test-fail-podinfo-grpc-test-gddcw": {}, + }}, + {Version: 2, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "upgrade-test-fail-podinfo-grpc-test-h0tc2": { + Phase: "Succeeded", + }, + "upgrade-test-fail-podinfo-jwt-test-vzusa": { + Phase: "Succeeded", + }, + "upgrade-test-fail-podinfo-service-test-b647e": { + Phase: "Succeeded", + }, + }}, + }, + ignoreTests: false, + want: Snapshots{ + {Version: 4, Status: "deployed"}, + {Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"}, + "upgrade-test-fail-podinfo-grpc-test-gddcw": {}, + }}, + {Version: 2, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "upgrade-test-fail-podinfo-grpc-test-h0tc2": { + Phase: "Succeeded", + }, + "upgrade-test-fail-podinfo-jwt-test-vzusa": { + Phase: "Succeeded", + }, + "upgrade-test-fail-podinfo-service-test-b647e": { + Phase: "Succeeded", + }, + }}, + }, + }, + { + name: "keeps previous snapshot with failed tests", + in: Snapshots{ + {Version: 4, Status: "deployed"}, + {Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"}, + "upgrade-test-fail-podinfo-grpc-test-gddcw": {}, + }}, + {Version: 2, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "upgrade-test-fail-podinfo-grpc-test-h0tc2": { + Phase: "Succeeded", + }, + "upgrade-test-fail-podinfo-jwt-test-vzusa": { + Phase: "Succeeded", + }, + "upgrade-test-fail-podinfo-service-test-b647e": { + Phase: "Succeeded", + }, + }}, + {Version: 1, Status: "superseded"}, + }, + ignoreTests: true, + want: Snapshots{ + {Version: 4, Status: "deployed"}, + {Version: 3, Status: "superseded", TestHooks: &map[string]*TestHookStatus{ + "upgrade-test-fail-podinfo-fault-test-tiz9x": {Phase: "Failed"}, + "upgrade-test-fail-podinfo-grpc-test-gddcw": {}, + }}, + }, + }, + { + name: "retains most recent snapshots when all have failed", + in: Snapshots{ + {Version: 6, Status: "deployed"}, + {Version: 5, Status: "failed"}, + {Version: 4, Status: "failed"}, + {Version: 3, Status: "failed"}, + {Version: 2, Status: "failed"}, + {Version: 1, Status: "failed"}, + }, + want: Snapshots{ + {Version: 6, Status: "deployed"}, + {Version: 5, Status: "failed"}, + {Version: 4, Status: "failed"}, + {Version: 3, Status: "failed"}, + {Version: 2, Status: "failed"}, + }, + }, + { + name: "without previous snapshot", + in: Snapshots{ + {Version: 1, Status: "deployed"}, + }, + want: Snapshots{ + {Version: 1, Status: "deployed"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.in.Truncate(tt.ignoreTests) + + if !reflect.DeepEqual(tt.in, tt.want) { + t.Errorf("Truncate() got %v, want %v", tt.in, tt.want) + } + }) + } +} diff --git a/api/v2/zz_generated.deepcopy.go b/api/v2/zz_generated.deepcopy.go new file mode 100644 index 000000000..57e62b402 --- /dev/null +++ b/api/v2/zz_generated.deepcopy.go @@ -0,0 +1,730 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2024 The Flux 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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v2 + +import ( + "github.com/fluxcd/pkg/apis/kustomize" + "github.com/fluxcd/pkg/apis/meta" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CrossNamespaceObjectReference) DeepCopyInto(out *CrossNamespaceObjectReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossNamespaceObjectReference. +func (in *CrossNamespaceObjectReference) DeepCopy() *CrossNamespaceObjectReference { + if in == nil { + return nil + } + out := new(CrossNamespaceObjectReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CrossNamespaceSourceReference) DeepCopyInto(out *CrossNamespaceSourceReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossNamespaceSourceReference. +func (in *CrossNamespaceSourceReference) DeepCopy() *CrossNamespaceSourceReference { + if in == nil { + return nil + } + out := new(CrossNamespaceSourceReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DriftDetection) DeepCopyInto(out *DriftDetection) { + *out = *in + if in.Ignore != nil { + in, out := &in.Ignore, &out.Ignore + *out = make([]IgnoreRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DriftDetection. +func (in *DriftDetection) DeepCopy() *DriftDetection { + if in == nil { + return nil + } + out := new(DriftDetection) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Filter) DeepCopyInto(out *Filter) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Filter. +func (in *Filter) DeepCopy() *Filter { + if in == nil { + return nil + } + out := new(Filter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmChartTemplate) DeepCopyInto(out *HelmChartTemplate) { + *out = *in + if in.ObjectMeta != nil { + in, out := &in.ObjectMeta, &out.ObjectMeta + *out = new(HelmChartTemplateObjectMeta) + (*in).DeepCopyInto(*out) + } + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplate. +func (in *HelmChartTemplate) DeepCopy() *HelmChartTemplate { + if in == nil { + return nil + } + out := new(HelmChartTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmChartTemplateObjectMeta) DeepCopyInto(out *HelmChartTemplateObjectMeta) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplateObjectMeta. +func (in *HelmChartTemplateObjectMeta) DeepCopy() *HelmChartTemplateObjectMeta { + if in == nil { + return nil + } + out := new(HelmChartTemplateObjectMeta) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmChartTemplateSpec) DeepCopyInto(out *HelmChartTemplateSpec) { + *out = *in + out.SourceRef = in.SourceRef + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(v1.Duration) + **out = **in + } + if in.ValuesFiles != nil { + in, out := &in.ValuesFiles, &out.ValuesFiles + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Verify != nil { + in, out := &in.Verify, &out.Verify + *out = new(HelmChartTemplateVerification) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplateSpec. +func (in *HelmChartTemplateSpec) DeepCopy() *HelmChartTemplateSpec { + if in == nil { + return nil + } + out := new(HelmChartTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmChartTemplateVerification) DeepCopyInto(out *HelmChartTemplateVerification) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(meta.LocalObjectReference) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartTemplateVerification. +func (in *HelmChartTemplateVerification) DeepCopy() *HelmChartTemplateVerification { + if in == nil { + return nil + } + out := new(HelmChartTemplateVerification) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmRelease) DeepCopyInto(out *HelmRelease) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmRelease. +func (in *HelmRelease) DeepCopy() *HelmRelease { + if in == nil { + return nil + } + out := new(HelmRelease) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HelmRelease) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmReleaseList) DeepCopyInto(out *HelmReleaseList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]HelmRelease, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseList. +func (in *HelmReleaseList) DeepCopy() *HelmReleaseList { + if in == nil { + return nil + } + out := new(HelmReleaseList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HelmReleaseList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmReleaseSpec) DeepCopyInto(out *HelmReleaseSpec) { + *out = *in + in.Chart.DeepCopyInto(&out.Chart) + if in.ChartRef != nil { + in, out := &in.ChartRef, &out.ChartRef + *out = new(CrossNamespaceSourceReference) + **out = **in + } + out.Interval = in.Interval + if in.KubeConfig != nil { + in, out := &in.KubeConfig, &out.KubeConfig + *out = new(meta.KubeConfigReference) + **out = **in + } + if in.DependsOn != nil { + in, out := &in.DependsOn, &out.DependsOn + *out = make([]meta.NamespacedObjectReference, len(*in)) + copy(*out, *in) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.MaxHistory != nil { + in, out := &in.MaxHistory, &out.MaxHistory + *out = new(int) + **out = **in + } + if in.PersistentClient != nil { + in, out := &in.PersistentClient, &out.PersistentClient + *out = new(bool) + **out = **in + } + if in.DriftDetection != nil { + in, out := &in.DriftDetection, &out.DriftDetection + *out = new(DriftDetection) + (*in).DeepCopyInto(*out) + } + if in.Install != nil { + in, out := &in.Install, &out.Install + *out = new(Install) + (*in).DeepCopyInto(*out) + } + if in.Upgrade != nil { + in, out := &in.Upgrade, &out.Upgrade + *out = new(Upgrade) + (*in).DeepCopyInto(*out) + } + if in.Test != nil { + in, out := &in.Test, &out.Test + *out = new(Test) + (*in).DeepCopyInto(*out) + } + if in.Rollback != nil { + in, out := &in.Rollback, &out.Rollback + *out = new(Rollback) + (*in).DeepCopyInto(*out) + } + if in.Uninstall != nil { + in, out := &in.Uninstall, &out.Uninstall + *out = new(Uninstall) + (*in).DeepCopyInto(*out) + } + if in.ValuesFrom != nil { + in, out := &in.ValuesFrom, &out.ValuesFrom + *out = make([]ValuesReference, len(*in)) + copy(*out, *in) + } + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = new(apiextensionsv1.JSON) + (*in).DeepCopyInto(*out) + } + if in.PostRenderers != nil { + in, out := &in.PostRenderers, &out.PostRenderers + *out = make([]PostRenderer, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseSpec. +func (in *HelmReleaseSpec) DeepCopy() *HelmReleaseSpec { + if in == nil { + return nil + } + out := new(HelmReleaseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmReleaseStatus) DeepCopyInto(out *HelmReleaseStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.History != nil { + in, out := &in.History, &out.History + *out = make(Snapshots, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Snapshot) + (*in).DeepCopyInto(*out) + } + } + } + out.ReconcileRequestStatus = in.ReconcileRequestStatus +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseStatus. +func (in *HelmReleaseStatus) DeepCopy() *HelmReleaseStatus { + if in == nil { + return nil + } + out := new(HelmReleaseStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IgnoreRule) DeepCopyInto(out *IgnoreRule) { + *out = *in + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(kustomize.Selector) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IgnoreRule. +func (in *IgnoreRule) DeepCopy() *IgnoreRule { + if in == nil { + return nil + } + out := new(IgnoreRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Install) DeepCopyInto(out *Install) { + *out = *in + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.Remediation != nil { + in, out := &in.Remediation, &out.Remediation + *out = new(InstallRemediation) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Install. +func (in *Install) DeepCopy() *Install { + if in == nil { + return nil + } + out := new(Install) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstallRemediation) DeepCopyInto(out *InstallRemediation) { + *out = *in + if in.IgnoreTestFailures != nil { + in, out := &in.IgnoreTestFailures, &out.IgnoreTestFailures + *out = new(bool) + **out = **in + } + if in.RemediateLastFailure != nil { + in, out := &in.RemediateLastFailure, &out.RemediateLastFailure + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstallRemediation. +func (in *InstallRemediation) DeepCopy() *InstallRemediation { + if in == nil { + return nil + } + out := new(InstallRemediation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Kustomize) DeepCopyInto(out *Kustomize) { + *out = *in + if in.Patches != nil { + in, out := &in.Patches, &out.Patches + *out = make([]kustomize.Patch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Images != nil { + in, out := &in.Images, &out.Images + *out = make([]kustomize.Image, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Kustomize. +func (in *Kustomize) DeepCopy() *Kustomize { + if in == nil { + return nil + } + out := new(Kustomize) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostRenderer) DeepCopyInto(out *PostRenderer) { + *out = *in + if in.Kustomize != nil { + in, out := &in.Kustomize, &out.Kustomize + *out = new(Kustomize) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostRenderer. +func (in *PostRenderer) DeepCopy() *PostRenderer { + if in == nil { + return nil + } + out := new(PostRenderer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Rollback) DeepCopyInto(out *Rollback) { + *out = *in + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rollback. +func (in *Rollback) DeepCopy() *Rollback { + if in == nil { + return nil + } + out := new(Rollback) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Snapshot) DeepCopyInto(out *Snapshot) { + *out = *in + in.FirstDeployed.DeepCopyInto(&out.FirstDeployed) + in.LastDeployed.DeepCopyInto(&out.LastDeployed) + in.Deleted.DeepCopyInto(&out.Deleted) + if in.TestHooks != nil { + in, out := &in.TestHooks, &out.TestHooks + *out = new(map[string]*TestHookStatus) + if **in != nil { + in, out := *in, *out + *out = make(map[string]*TestHookStatus, len(*in)) + for key, val := range *in { + var outVal *TestHookStatus + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = new(TestHookStatus) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Snapshot. +func (in *Snapshot) DeepCopy() *Snapshot { + if in == nil { + return nil + } + out := new(Snapshot) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Snapshots) DeepCopyInto(out *Snapshots) { + { + in := &in + *out = make(Snapshots, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Snapshot) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Snapshots. +func (in Snapshots) DeepCopy() Snapshots { + if in == nil { + return nil + } + out := new(Snapshots) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Test) DeepCopyInto(out *Test) { + *out = *in + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.Filters != nil { + in, out := &in.Filters, &out.Filters + *out = new([]Filter) + if **in != nil { + in, out := *in, *out + *out = make([]Filter, len(*in)) + copy(*out, *in) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Test. +func (in *Test) DeepCopy() *Test { + if in == nil { + return nil + } + out := new(Test) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TestHookStatus) DeepCopyInto(out *TestHookStatus) { + *out = *in + in.LastStarted.DeepCopyInto(&out.LastStarted) + in.LastCompleted.DeepCopyInto(&out.LastCompleted) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TestHookStatus. +func (in *TestHookStatus) DeepCopy() *TestHookStatus { + if in == nil { + return nil + } + out := new(TestHookStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Uninstall) DeepCopyInto(out *Uninstall) { + *out = *in + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.DeletionPropagation != nil { + in, out := &in.DeletionPropagation, &out.DeletionPropagation + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Uninstall. +func (in *Uninstall) DeepCopy() *Uninstall { + if in == nil { + return nil + } + out := new(Uninstall) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Upgrade) DeepCopyInto(out *Upgrade) { + *out = *in + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.Remediation != nil { + in, out := &in.Remediation, &out.Remediation + *out = new(UpgradeRemediation) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Upgrade. +func (in *Upgrade) DeepCopy() *Upgrade { + if in == nil { + return nil + } + out := new(Upgrade) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpgradeRemediation) DeepCopyInto(out *UpgradeRemediation) { + *out = *in + if in.IgnoreTestFailures != nil { + in, out := &in.IgnoreTestFailures, &out.IgnoreTestFailures + *out = new(bool) + **out = **in + } + if in.RemediateLastFailure != nil { + in, out := &in.RemediateLastFailure, &out.RemediateLastFailure + *out = new(bool) + **out = **in + } + if in.Strategy != nil { + in, out := &in.Strategy, &out.Strategy + *out = new(RemediationStrategy) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeRemediation. +func (in *UpgradeRemediation) DeepCopy() *UpgradeRemediation { + if in == nil { + return nil + } + out := new(UpgradeRemediation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValuesReference) DeepCopyInto(out *ValuesReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValuesReference. +func (in *ValuesReference) DeepCopy() *ValuesReference { + if in == nil { + return nil + } + out := new(ValuesReference) + in.DeepCopyInto(out) + return out +} diff --git a/api/v2beta1/helmrelease_types.go b/api/v2beta1/helmrelease_types.go index 697e4668b..f857baac0 100644 --- a/api/v2beta1/helmrelease_types.go +++ b/api/v2beta1/helmrelease_types.go @@ -1087,7 +1087,7 @@ const ( // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" // +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="" // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="" -// +kubebuilder:deprecatedversion:warning="v2beta1 HelmRelease is deprecated, upgrade to v2beta2" +// +kubebuilder:deprecatedversion:warning="v2beta1 HelmRelease is deprecated, upgrade to v2" // HelmRelease is the Schema for the helmreleases API type HelmRelease struct { diff --git a/api/v2beta1/zz_generated.deepcopy.go b/api/v2beta1/zz_generated.deepcopy.go index 9afc3f776..69eea9950 100644 --- a/api/v2beta1/zz_generated.deepcopy.go +++ b/api/v2beta1/zz_generated.deepcopy.go @@ -1,7 +1,7 @@ //go:build !ignore_autogenerated /* -Copyright 2022 The Flux authors +Copyright 2024 The Flux authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v2beta2/helmrelease_types.go b/api/v2beta2/helmrelease_types.go index 05c88b8fc..4f2121dad 100644 --- a/api/v2beta2/helmrelease_types.go +++ b/api/v2beta2/helmrelease_types.go @@ -1081,11 +1081,11 @@ const ( // +genclient // +kubebuilder:object:root=true // +kubebuilder:resource:shortName=hr -// +kubebuilder:storageversion // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" // +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="" // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="" +// +kubebuilder:deprecatedversion:warning="v2beta2 HelmRelease is deprecated, upgrade to v2" // HelmRelease is the Schema for the helmreleases API type HelmRelease struct { diff --git a/api/v2beta2/zz_generated.deepcopy.go b/api/v2beta2/zz_generated.deepcopy.go index 7d7f3200a..2e5711694 100644 --- a/api/v2beta2/zz_generated.deepcopy.go +++ b/api/v2beta2/zz_generated.deepcopy.go @@ -1,7 +1,7 @@ //go:build !ignore_autogenerated /* -Copyright 2022 The Flux authors +Copyright 2024 The Flux authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml b/config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml index 94900003b..f60447460 100644 --- a/config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml +++ b/config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml @@ -16,6 +16,1165 @@ spec: singular: helmrelease scope: Namespaced versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v2 + schema: + openAPIV3Schema: + description: HelmRelease is the Schema for the helmreleases API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmReleaseSpec defines the desired state of a Helm release. + properties: + chart: + description: |- + Chart defines the template of the v1beta2.HelmChart that should be created + for this HelmRelease. + properties: + metadata: + description: ObjectMeta holds the template for metadata like labels + and annotations. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + type: object + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + type: object + type: object + spec: + description: Spec holds the template for the v1beta2.HelmChartSpec + for this HelmRelease. + properties: + chart: + description: The name or path the Helm chart is available + at in the SourceRef. + maxLength: 2048 + minLength: 1 + type: string + ignoreMissingValuesFiles: + description: IgnoreMissingValuesFiles controls whether to + silently ignore missing values files rather than failing. + type: boolean + interval: + description: |- + Interval at which to check the v1.Source for updates. Defaults to + 'HelmReleaseSpec.Interval'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + reconcileStrategy: + default: ChartVersion + description: |- + Determines what enables the creation of a new artifact. Valid values are + ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: The name and namespace of the v1.Source the chart + is available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace of the referent. + maxLength: 63 + minLength: 1 + type: string + required: + - name + type: object + valuesFiles: + description: |- + Alternative list of values files to use as the chart values (values.yaml + is not included by default), expected to be a relative path in the SourceRef. + Values files are merged in the order of this list with the last file overriding + the first. Ignored when omitted. + items: + type: string + type: array + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + This field is only supported for OCI sources. + Chart dependencies, which are not bundled in the umbrella chart artifact, + are not verified. + properties: + provider: + default: cosign + description: Provider specifies the technology used to + sign the OCI Helm chart. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + version: + default: '*' + description: |- + Version semver expression, ignored for charts from v1beta2.GitRepository and + v1beta2.Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - sourceRef + type: object + required: + - spec + type: object + chartRef: + description: |- + ChartRef holds a reference to a source controller resource containing the + Helm chart artifact. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - OCIRepository + - HelmChart + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace of the referent, defaults to the namespace of the Kubernetes + resource object that contains the reference. + maxLength: 63 + minLength: 1 + type: string + required: + - kind + - name + type: object + dependsOn: + description: |- + DependsOn may contain a meta.NamespacedObjectReference slice with + references to HelmRelease resources that must be ready before this HelmRelease + can be reconciled. + items: + description: |- + NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any + namespace. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. + type: string + required: + - name + type: object + type: array + driftDetection: + description: |- + DriftDetection holds the configuration for detecting and handling + differences between the manifest in the Helm storage and the resources + currently existing in the cluster. + properties: + ignore: + description: |- + Ignore contains a list of rules for specifying which changes to ignore + during diffing. + items: + description: |- + IgnoreRule defines a rule to selectively disregard specific changes during + the drift detection process. + properties: + paths: + description: |- + Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from + consideration in a Kubernetes object. + items: + type: string + type: array + target: + description: |- + Target is a selector for specifying Kubernetes objects to which this + rule applies. + If Target is not set, the Paths will be ignored for all Kubernetes + objects within the manifest of the Helm release. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - paths + type: object + type: array + mode: + description: |- + Mode defines how differences should be handled between the Helm manifest + and the manifest currently applied to the cluster. + If not explicitly set, it defaults to DiffModeDisabled. + enum: + - enabled + - warn + - disabled + type: string + type: object + install: + description: Install holds the configuration for Helm install actions + for this HelmRelease. + properties: + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Create` and if omitted + CRDs are installed but not updated. + + + Skip: do neither install nor replace (update) any CRDs. + + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + + By default, CRDs are applied (installed) during Helm install action. + With this option users can opt in to CRD replace existing CRDs on Helm + install actions, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + createNamespace: + description: |- + CreateNamespace tells the Helm install action to create the + HelmReleaseSpec.TargetNamespace if it does not exist yet. + On uninstall, the namespace will not be garbage collected. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm install action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm install action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + install has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + install has been performed. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm install + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an install action but fail. Defaults to + 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false'. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using an uninstall, is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + type: object + replace: + description: |- + Replace tells the Helm install action to re-use the 'ReleaseName', but only + if that name is a deleted release which remains in the history. + type: boolean + skipCRDs: + description: |- + SkipCRDs tells the Helm install action to not install any CRDs. By default, + CRDs are installed if not already present. + + + Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm install action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + interval: + description: Interval at which to reconcile the Helm release. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: |- + KubeConfig for reconciling the HelmRelease on a remote cluster. + When used in combination with HelmReleaseSpec.ServiceAccountName, + forces the controller to act on behalf of that Service Account at the + target cluster. + If the --default-service-account flag is set, its value will be used as + a controller level fallback for when HelmReleaseSpec.ServiceAccountName + is empty. + properties: + secretRef: + description: |- + SecretRef holds the name of a secret that contains a key with + the kubeconfig file as the value. If no key is set, the key will default + to 'value'. + It is recommended that the kubeconfig is self-contained, and the secret + is regularly updated if credentials such as a cloud-access-token expire. + Cloud specific `cmd-path` auth helpers will not function without adding + binaries and credentials to the Pod that is responsible for reconciling + Kubernetes resources. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific + default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + required: + - secretRef + type: object + maxHistory: + description: |- + MaxHistory is the number of revisions saved by Helm for this HelmRelease. + Use '0' for an unlimited number of revisions; defaults to '5'. + type: integer + persistentClient: + description: |- + PersistentClient tells the controller to use a persistent Kubernetes + client for this release. When enabled, the client will be reused for the + duration of the reconciliation, instead of being created and destroyed + for each (step of a) Helm action. + + + This can improve performance, but may cause issues with some Helm charts + that for example do create Custom Resource Definitions during installation + outside Helm's CRD lifecycle hooks, which are then not observed to be + available by e.g. post-install hooks. + + + If not set, it defaults to true. + type: boolean + postRenderers: + description: |- + PostRenderers holds an array of Helm PostRenderers, which will be applied in order + of their definition. + items: + description: PostRenderer contains a Helm PostRenderer specification. + properties: + kustomize: + description: Kustomization to apply as PostRenderer. + properties: + images: + description: |- + Images is a list of (image name, new name, new tag or digest) + for changing image names, tags or digests. This can also be achieved with a + patch, but this operator is simpler to specify. + items: + description: Image contains an image name, a new name, + a new tag or digest, which will replace the original + name and tag. + properties: + digest: + description: |- + Digest is the value used to replace the original image tag. + If digest is present NewTag value is ignored. + type: string + name: + description: Name is a tag-less image name. + type: string + newName: + description: NewName is the value used to replace + the original name. + type: string + newTag: + description: NewTag is the value used to replace the + original tag. + type: string + required: + - name + type: object + type: array + patches: + description: |- + Strategic merge and JSON patches, defined as inline YAML objects, + capable of targeting objects based on kind, label and annotation selectors. + items: + description: |- + Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should + be applied to. + properties: + patch: + description: |- + Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with + an array of operation objects. + type: string + target: + description: Target points to the resources that the + patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + type: object + type: array + type: object + type: object + type: array + releaseName: + description: |- + ReleaseName used for the Helm release. Defaults to a composition of + '[TargetNamespace-]Name'. + maxLength: 53 + minLength: 1 + type: string + rollback: + description: Rollback holds the configuration for Helm rollback actions + for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + rollback action when it fails. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + rollback has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + rollback has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement + strategy. + type: boolean + recreate: + description: Recreate performs pod restarts for the resource if + applicable. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm rollback action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + serviceAccountName: + description: |- + The name of the Kubernetes service account to impersonate + when reconciling this HelmRelease. + maxLength: 253 + minLength: 1 + type: string + storageNamespace: + description: |- + StorageNamespace used for the Helm storage. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + suspend: + description: |- + Suspend tells the controller to suspend reconciliation for this HelmRelease, + it does not apply to already started reconciliations. Defaults to false. + type: boolean + targetNamespace: + description: |- + TargetNamespace to target when performing operations for the HelmRelease. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + test: + description: Test holds the configuration for Helm test actions for + this HelmRelease. + properties: + enable: + description: |- + Enable enables Helm test actions for this HelmRelease after an Helm install + or upgrade action has been performed. + type: boolean + filters: + description: Filters is a list of tests to run or exclude from + running. + items: + description: Filter holds the configuration for individual Helm + test filters. + properties: + exclude: + description: Exclude specifies whether the named test should + be excluded. + type: boolean + name: + description: Name is the name of the test. + maxLength: 253 + minLength: 1 + type: string + required: + - name + type: object + type: array + ignoreFailures: + description: |- + IgnoreFailures tells the controller to skip remediation when the Helm tests + are run but fail. Can be overwritten for tests run after install or upgrade + actions in 'Install.IgnoreTestFailures' and 'Upgrade.IgnoreTestFailures'. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation during + the performance of a Helm test action. Defaults to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like Jobs + for hooks) during the performance of a Helm action. Defaults to '5m0s'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + uninstall: + description: Uninstall holds the configuration for Helm uninstall + actions for this HelmRelease. + properties: + deletionPropagation: + default: background + description: |- + DeletionPropagation specifies the deletion propagation policy when + a Helm uninstall is performed. + enum: + - background + - foreground + - orphan + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables waiting for all the resources to be deleted after + a Helm uninstall is performed. + type: boolean + keepHistory: + description: |- + KeepHistory tells Helm to remove all associated resources and mark the + release as deleted, but retain the release history. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm uninstall action. Defaults + to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + upgrade: + description: Upgrade holds the configuration for Helm upgrade actions + for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + upgrade action when it fails. + type: boolean + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Skip` and if omitted + CRDs are neither installed nor upgraded. + + + Skip: do neither install nor replace (update) any CRDs. + + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + + By default, CRDs are not applied during Helm upgrade action. With this + option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm upgrade action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm upgrade action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + upgrade has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + upgrade has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement + strategy. + type: boolean + preserveValues: + description: |- + PreserveValues will make Helm reuse the last release's values and merge in + overrides from 'Values'. Setting this flag makes the HelmRelease + non-declarative. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm upgrade + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an upgrade action but fail. + Defaults to 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false' unless 'Retries' is greater than 0. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using 'Strategy', is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + strategy: + description: Strategy to use for failure remediation. Defaults + to 'rollback'. + enum: + - rollback + - uninstall + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm upgrade action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + values: + description: Values holds the values for this Helm release. + x-kubernetes-preserve-unknown-fields: true + valuesFrom: + description: |- + ValuesFrom holds references to resources containing Helm values for this HelmRelease, + and information about how they should be merged. + items: + description: |- + ValuesReference contains a reference to a resource containing Helm values, + and optionally the key they can be found at. + properties: + kind: + description: Kind of the values referent, valid values are ('Secret', + 'ConfigMap'). + enum: + - Secret + - ConfigMap + type: string + name: + description: |- + Name of the values referent. Should reside in the same namespace as the + referring resource. + maxLength: 253 + minLength: 1 + type: string + optional: + description: |- + Optional marks this ValuesReference as optional. When set, a not found error + for the values reference is ignored, but any ValuesKey, TargetPath or + transient error will still result in a reconciliation failure. + type: boolean + targetPath: + description: |- + TargetPath is the YAML dot notation path the value should be merged at. When + set, the ValuesKey is expected to be a single flat value. Defaults to 'None', + which results in the values getting merged at the root. + maxLength: 250 + pattern: ^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$ + type: string + valuesKey: + description: |- + ValuesKey is the data key where the values.yaml or a specific value can be + found at. Defaults to 'values.yaml'. + maxLength: 253 + pattern: ^[\-._a-zA-Z0-9]+$ + type: string + required: + - kind + - name + type: object + type: array + required: + - interval + type: object + x-kubernetes-validations: + - message: either chart or chartRef must be set + rule: (has(self.chart) && !has(self.chartRef)) || (!has(self.chart) + && has(self.chartRef)) + status: + default: + observedGeneration: -1 + description: HelmReleaseStatus defines the observed state of a HelmRelease. + properties: + conditions: + description: Conditions holds the conditions for the HelmRelease. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + failures: + description: |- + Failures is the reconciliation failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + helmChart: + description: |- + HelmChart is the namespaced name of the HelmChart resource created by + the controller for the HelmRelease. + type: string + history: + description: |- + History holds the history of Helm releases performed for this HelmRelease + up to the last successfully completed release. + items: + description: |- + Snapshot captures a point-in-time copy of the status information for a Helm release, + as managed by the controller. + properties: + apiVersion: + description: |- + APIVersion is the API version of the Snapshot. + Provisional: when the calculation method of the Digest field is changed, + this field will be used to distinguish between the old and new methods. + type: string + chartName: + description: ChartName is the chart name of the release object + in storage. + type: string + chartVersion: + description: |- + ChartVersion is the chart version of the release object in + storage. + type: string + configDigest: + description: |- + ConfigDigest is the checksum of the config (better known as + "values") of the release object in storage. + It has the format of `:`. + type: string + deleted: + description: Deleted is when the release was deleted. + format: date-time + type: string + digest: + description: |- + Digest is the checksum of the release object in storage. + It has the format of `:`. + type: string + firstDeployed: + description: FirstDeployed is when the release was first deployed. + format: date-time + type: string + lastDeployed: + description: LastDeployed is when the release was last deployed. + format: date-time + type: string + name: + description: Name is the name of the release. + type: string + namespace: + description: Namespace is the namespace the release is deployed + to. + type: string + ociDigest: + description: OCIDigest is the digest of the OCI artifact associated + with the release. + type: string + status: + description: Status is the current state of the release. + type: string + testHooks: + additionalProperties: + description: |- + TestHookStatus holds the status information for a test hook as observed + to be run by the controller. + properties: + lastCompleted: + description: LastCompleted is the time the test hook last + completed. + format: date-time + type: string + lastStarted: + description: LastStarted is the time the test hook was + last started. + format: date-time + type: string + phase: + description: Phase the test hook was observed to be in. + type: string + type: object + description: |- + TestHooks is the list of test hooks for the release as observed to be + run by the controller. + type: object + version: + description: Version is the version of the release object in + storage. + type: integer + required: + - chartName + - chartVersion + - configDigest + - digest + - firstDeployed + - lastDeployed + - name + - namespace + - status + - version + type: object + type: array + installFailures: + description: |- + InstallFailures is the install failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + lastAttemptedConfigDigest: + description: |- + LastAttemptedConfigDigest is the digest for the config (better known as + "values") of the last reconciliation attempt. + type: string + lastAttemptedGeneration: + description: |- + LastAttemptedGeneration is the last generation the controller attempted + to reconcile. + format: int64 + type: integer + lastAttemptedReleaseAction: + description: |- + LastAttemptedReleaseAction is the last release action performed for this + HelmRelease. It is used to determine the active remediation strategy. + enum: + - install + - upgrade + type: string + lastAttemptedRevision: + description: |- + LastAttemptedRevision is the Source revision of the last reconciliation + attempt. For OCIRepository sources, the 12 first characters of the digest are + appended to the chart version e.g. "1.2.3+1234567890ab". + type: string + lastAttemptedRevisionDigest: + description: |- + LastAttemptedRevisionDigest is the digest of the last reconciliation attempt. + This is only set for OCIRepository sources. + type: string + lastAttemptedValuesChecksum: + description: |- + LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last + reconciliation attempt. + Deprecated: Use LastAttemptedConfigDigest instead. + type: string + lastHandledForceAt: + description: |- + LastHandledForceAt holds the value of the most recent force request + value, so a change of the annotation value can be detected. + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastHandledResetAt: + description: |- + LastHandledResetAt holds the value of the most recent reset request + value, so a change of the annotation value can be detected. + type: string + lastReleaseRevision: + description: |- + LastReleaseRevision is the revision of the last successful Helm release. + Deprecated: Use History instead. + type: integer + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + storageNamespace: + description: |- + StorageNamespace is the namespace of the Helm release storage for the + current release. + maxLength: 63 + minLength: 1 + type: string + upgradeFailures: + description: |- + UpgradeFailures is the upgrade failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age @@ -27,7 +1186,7 @@ spec: name: Status type: string deprecated: true - deprecationWarning: v2beta1 HelmRelease is deprecated, upgrade to v2beta2 + deprecationWarning: v2beta1 HelmRelease is deprecated, upgrade to v2 name: v2beta1 schema: openAPIV3Schema: @@ -1258,6 +2417,8 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].message name: Status type: string + deprecated: true + deprecationWarning: v2beta2 HelmRelease is deprecated, upgrade to v2 name: v2beta2 schema: openAPIV3Schema: @@ -2520,6 +3681,6 @@ spec: type: object type: object served: true - storage: true + storage: false subresources: status: {} diff --git a/config/samples/helm_v2beta2_helmrelease_gitrepository.yaml b/config/samples/helm_v2_helmrelease_gitrepository.yaml similarity index 56% rename from config/samples/helm_v2beta2_helmrelease_gitrepository.yaml rename to config/samples/helm_v2_helmrelease_gitrepository.yaml index 0f8d46335..a46a742b2 100644 --- a/config/samples/helm_v2beta2_helmrelease_gitrepository.yaml +++ b/config/samples/helm_v2_helmrelease_gitrepository.yaml @@ -1,4 +1,4 @@ -apiVersion: helm.toolkit.fluxcd.io/v2beta2 +apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: podinfo-gitrepository @@ -10,9 +10,3 @@ spec: sourceRef: kind: GitRepository name: podinfo - interval: 1m - upgrade: - remediation: - remediateLastFailure: true - test: - enable: true diff --git a/config/samples/helm_v2beta2_helmrelease_helmrepository.yaml b/config/samples/helm_v2_helmrelease_helmrepository.yaml similarity index 59% rename from config/samples/helm_v2beta2_helmrelease_helmrepository.yaml rename to config/samples/helm_v2_helmrelease_helmrepository.yaml index 06461c1b1..c4c4d1a5d 100644 --- a/config/samples/helm_v2beta2_helmrelease_helmrepository.yaml +++ b/config/samples/helm_v2_helmrelease_helmrepository.yaml @@ -1,4 +1,4 @@ -apiVersion: helm.toolkit.fluxcd.io/v2beta2 +apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: podinfo-helmrepository @@ -11,9 +11,4 @@ spec: sourceRef: kind: HelmRepository name: podinfo - interval: 1m - upgrade: - remediation: - remediateLastFailure: true - test: - enable: true + interval: 10m diff --git a/config/samples/helm_v2_helmrelease_ocirepository.yaml b/config/samples/helm_v2_helmrelease_ocirepository.yaml new file mode 100644 index 000000000..aafef9c0b --- /dev/null +++ b/config/samples/helm_v2_helmrelease_ocirepository.yaml @@ -0,0 +1,13 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: podinfo-ocirepository +spec: + interval: 5m + chartRef: + kind: OCIRepository + name: podinfo + test: + enable: true + values: + replicaCount: 2 diff --git a/config/samples/source_v1beta2_helmrepository.yaml b/config/samples/source_v1_helmrepository.yaml similarity index 71% rename from config/samples/source_v1beta2_helmrepository.yaml rename to config/samples/source_v1_helmrepository.yaml index c83ef482b..f4f6e3216 100644 --- a/config/samples/source_v1beta2_helmrepository.yaml +++ b/config/samples/source_v1_helmrepository.yaml @@ -1,4 +1,4 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 +apiVersion: source.toolkit.fluxcd.io/v1 kind: HelmRepository metadata: name: podinfo diff --git a/config/samples/source_v1beta2_ocirepository.yaml b/config/samples/source_v1beta2_ocirepository.yaml new file mode 100644 index 000000000..948512217 --- /dev/null +++ b/config/samples/source_v1beta2_ocirepository.yaml @@ -0,0 +1,9 @@ +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: OCIRepository +metadata: + name: podinfo +spec: + interval: 1m + url: oci://ghcr.io/stefanprodan/charts/podinfo + ref: + semver: 6.x diff --git a/config/testdata/post-renderer-kustomize/helmrelease.yaml b/config/testdata/post-renderer-kustomize/helmrelease.yaml index 9c5707604..e4f839e5b 100644 --- a/config/testdata/post-renderer-kustomize/helmrelease.yaml +++ b/config/testdata/post-renderer-kustomize/helmrelease.yaml @@ -16,20 +16,20 @@ spec: fullnameOverride: mypodinfo postRenderers: - kustomize: - patchesStrategicMerge: - - kind: Deployment - apiVersion: apps/v1 - metadata: - name: mypodinfo - labels: - xxxx: yyyy - patchesJson6902: + patches: + - patch: | + kind: Deployment + apiVersion: apps/v1 + metadata: + name: mypodinfo + labels: + xxxx: yyyy - target: group: apps version: v1 kind: Deployment name: mypodinfo - patch: - - op: add - path: /metadata/labels/yyyy - value: xxxx + patch: | + - op: add + path: /metadata/labels/yyyy + value: xxxx diff --git a/docs/api/v2/helm.md b/docs/api/v2/helm.md new file mode 100644 index 000000000..08472f1b8 --- /dev/null +++ b/docs/api/v2/helm.md @@ -0,0 +1,2927 @@ +

Helm API reference v2

+

Packages:

+ +

helm.toolkit.fluxcd.io/v2

+

Package v2 contains API Schema definitions for the helm v2 API group

+Resource Types: + +

HelmRelease +

+

HelmRelease is the Schema for the helmreleases API

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+apiVersion
+string
+helm.toolkit.fluxcd.io/v2 +
+kind
+string +
+HelmRelease +
+metadata
+ + +Kubernetes meta/v1.ObjectMeta + + +
+Refer to the Kubernetes API documentation for the fields of the +metadata field. +
+spec
+ + +HelmReleaseSpec + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+chart
+ + +HelmChartTemplate + + +
+(Optional) +

Chart defines the template of the v1beta2.HelmChart that should be created +for this HelmRelease.

+
+chartRef
+ + +CrossNamespaceSourceReference + + +
+(Optional) +

ChartRef holds a reference to a source controller resource containing the +Helm chart artifact.

+
+interval
+ + +Kubernetes meta/v1.Duration + + +
+

Interval at which to reconcile the Helm release.

+
+kubeConfig
+ + +github.com/fluxcd/pkg/apis/meta.KubeConfigReference + + +
+(Optional) +

KubeConfig for reconciling the HelmRelease on a remote cluster. +When used in combination with HelmReleaseSpec.ServiceAccountName, +forces the controller to act on behalf of that Service Account at the +target cluster. +If the –default-service-account flag is set, its value will be used as +a controller level fallback for when HelmReleaseSpec.ServiceAccountName +is empty.

+
+suspend
+ +bool + +
+(Optional) +

Suspend tells the controller to suspend reconciliation for this HelmRelease, +it does not apply to already started reconciliations. Defaults to false.

+
+releaseName
+ +string + +
+(Optional) +

ReleaseName used for the Helm release. Defaults to a composition of +‘[TargetNamespace-]Name’.

+
+targetNamespace
+ +string + +
+(Optional) +

TargetNamespace to target when performing operations for the HelmRelease. +Defaults to the namespace of the HelmRelease.

+
+storageNamespace
+ +string + +
+(Optional) +

StorageNamespace used for the Helm storage. +Defaults to the namespace of the HelmRelease.

+
+dependsOn
+ + +[]github.com/fluxcd/pkg/apis/meta.NamespacedObjectReference + + +
+(Optional) +

DependsOn may contain a meta.NamespacedObjectReference slice with +references to HelmRelease resources that must be ready before this HelmRelease +can be reconciled.

+
+timeout
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

Timeout is the time to wait for any individual Kubernetes operation (like Jobs +for hooks) during the performance of a Helm action. Defaults to ‘5m0s’.

+
+maxHistory
+ +int + +
+(Optional) +

MaxHistory is the number of revisions saved by Helm for this HelmRelease. +Use ‘0’ for an unlimited number of revisions; defaults to ‘5’.

+
+serviceAccountName
+ +string + +
+(Optional) +

The name of the Kubernetes service account to impersonate +when reconciling this HelmRelease.

+
+persistentClient
+ +bool + +
+(Optional) +

PersistentClient tells the controller to use a persistent Kubernetes +client for this release. When enabled, the client will be reused for the +duration of the reconciliation, instead of being created and destroyed +for each (step of a) Helm action.

+

This can improve performance, but may cause issues with some Helm charts +that for example do create Custom Resource Definitions during installation +outside Helm’s CRD lifecycle hooks, which are then not observed to be +available by e.g. post-install hooks.

+

If not set, it defaults to true.

+
+driftDetection
+ + +DriftDetection + + +
+(Optional) +

DriftDetection holds the configuration for detecting and handling +differences between the manifest in the Helm storage and the resources +currently existing in the cluster.

+
+install
+ + +Install + + +
+(Optional) +

Install holds the configuration for Helm install actions for this HelmRelease.

+
+upgrade
+ + +Upgrade + + +
+(Optional) +

Upgrade holds the configuration for Helm upgrade actions for this HelmRelease.

+
+test
+ + +Test + + +
+(Optional) +

Test holds the configuration for Helm test actions for this HelmRelease.

+
+rollback
+ + +Rollback + + +
+(Optional) +

Rollback holds the configuration for Helm rollback actions for this HelmRelease.

+
+uninstall
+ + +Uninstall + + +
+(Optional) +

Uninstall holds the configuration for Helm uninstall actions for this HelmRelease.

+
+valuesFrom
+ + +[]ValuesReference + + +
+

ValuesFrom holds references to resources containing Helm values for this HelmRelease, +and information about how they should be merged.

+
+values
+ + +Kubernetes pkg/apis/apiextensions/v1.JSON + + +
+(Optional) +

Values holds the values for this Helm release.

+
+postRenderers
+ + +[]PostRenderer + + +
+(Optional) +

PostRenderers holds an array of Helm PostRenderers, which will be applied in order +of their definition.

+
+
+status
+ + +HelmReleaseStatus + + +
+
+
+
+

CRDsPolicy +(string alias)

+

+(Appears on: +Install, +Upgrade) +

+

CRDsPolicy defines the install/upgrade approach to use for CRDs when +installing or upgrading a HelmRelease.

+

CrossNamespaceObjectReference +

+

+(Appears on: +HelmChartTemplateSpec) +

+

CrossNamespaceObjectReference contains enough information to let you locate +the typed referenced object at cluster level.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+apiVersion
+ +string + +
+(Optional) +

APIVersion of the referent.

+
+kind
+ +string + +
+

Kind of the referent.

+
+name
+ +string + +
+

Name of the referent.

+
+namespace
+ +string + +
+(Optional) +

Namespace of the referent.

+
+
+
+

CrossNamespaceSourceReference +

+

+(Appears on: +HelmReleaseSpec) +

+

CrossNamespaceSourceReference contains enough information to let you locate +the typed referenced object at cluster level.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+apiVersion
+ +string + +
+(Optional) +

APIVersion of the referent.

+
+kind
+ +string + +
+

Kind of the referent.

+
+name
+ +string + +
+

Name of the referent.

+
+namespace
+ +string + +
+(Optional) +

Namespace of the referent, defaults to the namespace of the Kubernetes +resource object that contains the reference.

+
+
+
+

DriftDetection +

+

+(Appears on: +HelmReleaseSpec) +

+

DriftDetection defines the strategy for performing differential analysis and +provides a way to define rules for ignoring specific changes during this +process.

+
+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+mode
+ + +DriftDetectionMode + + +
+(Optional) +

Mode defines how differences should be handled between the Helm manifest +and the manifest currently applied to the cluster. +If not explicitly set, it defaults to DiffModeDisabled.

+
+ignore
+ + +[]IgnoreRule + + +
+(Optional) +

Ignore contains a list of rules for specifying which changes to ignore +during diffing.

+
+
+
+

DriftDetectionMode +(string alias)

+

+(Appears on: +DriftDetection) +

+

DriftDetectionMode represents the modes in which a controller can detect and +handle differences between the manifest in the Helm storage and the resources +currently existing in the cluster.

+

Filter +

+

+(Appears on: +Test) +

+

Filter holds the configuration for individual Helm test filters.

+
+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+name
+ +string + +
+

Name is the name of the test.

+
+exclude
+ +bool + +
+(Optional) +

Exclude specifies whether the named test should be excluded.

+
+
+
+

HelmChartTemplate +

+

+(Appears on: +HelmReleaseSpec) +

+

HelmChartTemplate defines the template from which the controller will +generate a v1beta2.HelmChart object in the same namespace as the referenced +v1.Source.

+
+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+metadata
+ + +HelmChartTemplateObjectMeta + + +
+(Optional) +

ObjectMeta holds the template for metadata like labels and annotations.

+
+spec
+ + +HelmChartTemplateSpec + + +
+

Spec holds the template for the v1beta2.HelmChartSpec for this HelmRelease.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+chart
+ +string + +
+

The name or path the Helm chart is available at in the SourceRef.

+
+version
+ +string + +
+(Optional) +

Version semver expression, ignored for charts from v1beta2.GitRepository and +v1beta2.Bucket sources. Defaults to latest when omitted.

+
+sourceRef
+ + +CrossNamespaceObjectReference + + +
+

The name and namespace of the v1.Source the chart is available at.

+
+interval
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

Interval at which to check the v1.Source for updates. Defaults to +‘HelmReleaseSpec.Interval’.

+
+reconcileStrategy
+ +string + +
+(Optional) +

Determines what enables the creation of a new artifact. Valid values are +(‘ChartVersion’, ‘Revision’). +See the documentation of the values for an explanation on their behavior. +Defaults to ChartVersion when omitted.

+
+valuesFiles
+ +[]string + +
+(Optional) +

Alternative list of values files to use as the chart values (values.yaml +is not included by default), expected to be a relative path in the SourceRef. +Values files are merged in the order of this list with the last file overriding +the first. Ignored when omitted.

+
+ignoreMissingValuesFiles
+ +bool + +
+(Optional) +

IgnoreMissingValuesFiles controls whether to silently ignore missing values files rather than failing.

+
+verify
+ + +HelmChartTemplateVerification + + +
+(Optional) +

Verify contains the secret name containing the trusted public keys +used to verify the signature and specifies which provider to use to check +whether OCI image is authentic. +This field is only supported for OCI sources. +Chart dependencies, which are not bundled in the umbrella chart artifact, +are not verified.

+
+
+
+
+

HelmChartTemplateObjectMeta +

+

+(Appears on: +HelmChartTemplate) +

+

HelmChartTemplateObjectMeta defines the template for the ObjectMeta of a +v1beta2.HelmChart.

+
+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+labels
+ +map[string]string + +
+(Optional) +

Map of string keys and values that can be used to organize and categorize +(scope and select) objects. +More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/

+
+annotations
+ +map[string]string + +
+(Optional) +

Annotations is an unstructured key value map stored with a resource that may be +set by external tools to store and retrieve arbitrary metadata. They are not +queryable and should be preserved when modifying objects. +More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/

+
+
+
+

HelmChartTemplateSpec +

+

+(Appears on: +HelmChartTemplate) +

+

HelmChartTemplateSpec defines the template from which the controller will +generate a v1beta2.HelmChartSpec object.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+chart
+ +string + +
+

The name or path the Helm chart is available at in the SourceRef.

+
+version
+ +string + +
+(Optional) +

Version semver expression, ignored for charts from v1beta2.GitRepository and +v1beta2.Bucket sources. Defaults to latest when omitted.

+
+sourceRef
+ + +CrossNamespaceObjectReference + + +
+

The name and namespace of the v1.Source the chart is available at.

+
+interval
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

Interval at which to check the v1.Source for updates. Defaults to +‘HelmReleaseSpec.Interval’.

+
+reconcileStrategy
+ +string + +
+(Optional) +

Determines what enables the creation of a new artifact. Valid values are +(‘ChartVersion’, ‘Revision’). +See the documentation of the values for an explanation on their behavior. +Defaults to ChartVersion when omitted.

+
+valuesFiles
+ +[]string + +
+(Optional) +

Alternative list of values files to use as the chart values (values.yaml +is not included by default), expected to be a relative path in the SourceRef. +Values files are merged in the order of this list with the last file overriding +the first. Ignored when omitted.

+
+ignoreMissingValuesFiles
+ +bool + +
+(Optional) +

IgnoreMissingValuesFiles controls whether to silently ignore missing values files rather than failing.

+
+verify
+ + +HelmChartTemplateVerification + + +
+(Optional) +

Verify contains the secret name containing the trusted public keys +used to verify the signature and specifies which provider to use to check +whether OCI image is authentic. +This field is only supported for OCI sources. +Chart dependencies, which are not bundled in the umbrella chart artifact, +are not verified.

+
+
+
+

HelmChartTemplateVerification +

+

+(Appears on: +HelmChartTemplateSpec) +

+

HelmChartTemplateVerification verifies the authenticity of an OCI Helm chart.

+
+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+provider
+ +string + +
+

Provider specifies the technology used to sign the OCI Helm chart.

+
+secretRef
+ + +github.com/fluxcd/pkg/apis/meta.LocalObjectReference + + +
+(Optional) +

SecretRef specifies the Kubernetes Secret containing the +trusted public keys.

+
+
+
+

HelmReleaseSpec +

+

+(Appears on: +HelmRelease) +

+

HelmReleaseSpec defines the desired state of a Helm release.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+chart
+ + +HelmChartTemplate + + +
+(Optional) +

Chart defines the template of the v1beta2.HelmChart that should be created +for this HelmRelease.

+
+chartRef
+ + +CrossNamespaceSourceReference + + +
+(Optional) +

ChartRef holds a reference to a source controller resource containing the +Helm chart artifact.

+
+interval
+ + +Kubernetes meta/v1.Duration + + +
+

Interval at which to reconcile the Helm release.

+
+kubeConfig
+ + +github.com/fluxcd/pkg/apis/meta.KubeConfigReference + + +
+(Optional) +

KubeConfig for reconciling the HelmRelease on a remote cluster. +When used in combination with HelmReleaseSpec.ServiceAccountName, +forces the controller to act on behalf of that Service Account at the +target cluster. +If the –default-service-account flag is set, its value will be used as +a controller level fallback for when HelmReleaseSpec.ServiceAccountName +is empty.

+
+suspend
+ +bool + +
+(Optional) +

Suspend tells the controller to suspend reconciliation for this HelmRelease, +it does not apply to already started reconciliations. Defaults to false.

+
+releaseName
+ +string + +
+(Optional) +

ReleaseName used for the Helm release. Defaults to a composition of +‘[TargetNamespace-]Name’.

+
+targetNamespace
+ +string + +
+(Optional) +

TargetNamespace to target when performing operations for the HelmRelease. +Defaults to the namespace of the HelmRelease.

+
+storageNamespace
+ +string + +
+(Optional) +

StorageNamespace used for the Helm storage. +Defaults to the namespace of the HelmRelease.

+
+dependsOn
+ + +[]github.com/fluxcd/pkg/apis/meta.NamespacedObjectReference + + +
+(Optional) +

DependsOn may contain a meta.NamespacedObjectReference slice with +references to HelmRelease resources that must be ready before this HelmRelease +can be reconciled.

+
+timeout
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

Timeout is the time to wait for any individual Kubernetes operation (like Jobs +for hooks) during the performance of a Helm action. Defaults to ‘5m0s’.

+
+maxHistory
+ +int + +
+(Optional) +

MaxHistory is the number of revisions saved by Helm for this HelmRelease. +Use ‘0’ for an unlimited number of revisions; defaults to ‘5’.

+
+serviceAccountName
+ +string + +
+(Optional) +

The name of the Kubernetes service account to impersonate +when reconciling this HelmRelease.

+
+persistentClient
+ +bool + +
+(Optional) +

PersistentClient tells the controller to use a persistent Kubernetes +client for this release. When enabled, the client will be reused for the +duration of the reconciliation, instead of being created and destroyed +for each (step of a) Helm action.

+

This can improve performance, but may cause issues with some Helm charts +that for example do create Custom Resource Definitions during installation +outside Helm’s CRD lifecycle hooks, which are then not observed to be +available by e.g. post-install hooks.

+

If not set, it defaults to true.

+
+driftDetection
+ + +DriftDetection + + +
+(Optional) +

DriftDetection holds the configuration for detecting and handling +differences between the manifest in the Helm storage and the resources +currently existing in the cluster.

+
+install
+ + +Install + + +
+(Optional) +

Install holds the configuration for Helm install actions for this HelmRelease.

+
+upgrade
+ + +Upgrade + + +
+(Optional) +

Upgrade holds the configuration for Helm upgrade actions for this HelmRelease.

+
+test
+ + +Test + + +
+(Optional) +

Test holds the configuration for Helm test actions for this HelmRelease.

+
+rollback
+ + +Rollback + + +
+(Optional) +

Rollback holds the configuration for Helm rollback actions for this HelmRelease.

+
+uninstall
+ + +Uninstall + + +
+(Optional) +

Uninstall holds the configuration for Helm uninstall actions for this HelmRelease.

+
+valuesFrom
+ + +[]ValuesReference + + +
+

ValuesFrom holds references to resources containing Helm values for this HelmRelease, +and information about how they should be merged.

+
+values
+ + +Kubernetes pkg/apis/apiextensions/v1.JSON + + +
+(Optional) +

Values holds the values for this Helm release.

+
+postRenderers
+ + +[]PostRenderer + + +
+(Optional) +

PostRenderers holds an array of Helm PostRenderers, which will be applied in order +of their definition.

+
+
+
+

HelmReleaseStatus +

+

+(Appears on: +HelmRelease) +

+

HelmReleaseStatus defines the observed state of a HelmRelease.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+observedGeneration
+ +int64 + +
+(Optional) +

ObservedGeneration is the last observed generation.

+
+lastAttemptedGeneration
+ +int64 + +
+(Optional) +

LastAttemptedGeneration is the last generation the controller attempted +to reconcile.

+
+conditions
+ + +[]Kubernetes meta/v1.Condition + + +
+(Optional) +

Conditions holds the conditions for the HelmRelease.

+
+helmChart
+ +string + +
+(Optional) +

HelmChart is the namespaced name of the HelmChart resource created by +the controller for the HelmRelease.

+
+storageNamespace
+ +string + +
+(Optional) +

StorageNamespace is the namespace of the Helm release storage for the +current release.

+
+history
+ + +Snapshots + + +
+(Optional) +

History holds the history of Helm releases performed for this HelmRelease +up to the last successfully completed release.

+
+lastAttemptedReleaseAction
+ + +ReleaseAction + + +
+(Optional) +

LastAttemptedReleaseAction is the last release action performed for this +HelmRelease. It is used to determine the active remediation strategy.

+
+failures
+ +int64 + +
+(Optional) +

Failures is the reconciliation failure count against the latest desired +state. It is reset after a successful reconciliation.

+
+installFailures
+ +int64 + +
+(Optional) +

InstallFailures is the install failure count against the latest desired +state. It is reset after a successful reconciliation.

+
+upgradeFailures
+ +int64 + +
+(Optional) +

UpgradeFailures is the upgrade failure count against the latest desired +state. It is reset after a successful reconciliation.

+
+lastAttemptedRevision
+ +string + +
+(Optional) +

LastAttemptedRevision is the Source revision of the last reconciliation +attempt. For OCIRepository sources, the 12 first characters of the digest are +appended to the chart version e.g. “1.2.3+1234567890ab”.

+
+lastAttemptedRevisionDigest
+ +string + +
+(Optional) +

LastAttemptedRevisionDigest is the digest of the last reconciliation attempt. +This is only set for OCIRepository sources.

+
+lastAttemptedValuesChecksum
+ +string + +
+(Optional) +

LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last +reconciliation attempt. +Deprecated: Use LastAttemptedConfigDigest instead.

+
+lastReleaseRevision
+ +int + +
+(Optional) +

LastReleaseRevision is the revision of the last successful Helm release. +Deprecated: Use History instead.

+
+lastAttemptedConfigDigest
+ +string + +
+(Optional) +

LastAttemptedConfigDigest is the digest for the config (better known as +“values”) of the last reconciliation attempt.

+
+lastHandledForceAt
+ +string + +
+(Optional) +

LastHandledForceAt holds the value of the most recent force request +value, so a change of the annotation value can be detected.

+
+lastHandledResetAt
+ +string + +
+(Optional) +

LastHandledResetAt holds the value of the most recent reset request +value, so a change of the annotation value can be detected.

+
+ReconcileRequestStatus
+ + +github.com/fluxcd/pkg/apis/meta.ReconcileRequestStatus + + +
+

+(Members of ReconcileRequestStatus are embedded into this type.) +

+
+
+
+

IgnoreRule +

+

+(Appears on: +DriftDetection) +

+

IgnoreRule defines a rule to selectively disregard specific changes during +the drift detection process.

+
+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+paths
+ +[]string + +
+

Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from +consideration in a Kubernetes object.

+
+target
+ + +github.com/fluxcd/pkg/apis/kustomize.Selector + + +
+(Optional) +

Target is a selector for specifying Kubernetes objects to which this +rule applies. +If Target is not set, the Paths will be ignored for all Kubernetes +objects within the manifest of the Helm release.

+
+
+
+

Install +

+

+(Appears on: +HelmReleaseSpec) +

+

Install holds the configuration for Helm install actions performed for this +HelmRelease.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+timeout
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

Timeout is the time to wait for any individual Kubernetes operation (like +Jobs for hooks) during the performance of a Helm install action. Defaults to +‘HelmReleaseSpec.Timeout’.

+
+remediation
+ + +InstallRemediation + + +
+(Optional) +

Remediation holds the remediation configuration for when the Helm install +action for the HelmRelease fails. The default is to not perform any action.

+
+disableWait
+ +bool + +
+(Optional) +

DisableWait disables the waiting for resources to be ready after a Helm +install has been performed.

+
+disableWaitForJobs
+ +bool + +
+(Optional) +

DisableWaitForJobs disables waiting for jobs to complete after a Helm +install has been performed.

+
+disableHooks
+ +bool + +
+(Optional) +

DisableHooks prevents hooks from running during the Helm install action.

+
+disableOpenAPIValidation
+ +bool + +
+(Optional) +

DisableOpenAPIValidation prevents the Helm install action from validating +rendered templates against the Kubernetes OpenAPI Schema.

+
+replace
+ +bool + +
+(Optional) +

Replace tells the Helm install action to re-use the ‘ReleaseName’, but only +if that name is a deleted release which remains in the history.

+
+skipCRDs
+ +bool + +
+(Optional) +

SkipCRDs tells the Helm install action to not install any CRDs. By default, +CRDs are installed if not already present.

+

Deprecated use CRD policy (crds) attribute with value Skip instead.

+
+crds
+ + +CRDsPolicy + + +
+(Optional) +

CRDs upgrade CRDs from the Helm Chart’s crds directory according +to the CRD upgrade policy provided here. Valid values are Skip, +Create or CreateReplace. Default is Create and if omitted +CRDs are installed but not updated.

+

Skip: do neither install nor replace (update) any CRDs.

+

Create: new CRDs are created, existing CRDs are neither updated nor deleted.

+

CreateReplace: new CRDs are created, existing CRDs are updated (replaced) +but not deleted.

+

By default, CRDs are applied (installed) during Helm install action. +With this option users can opt in to CRD replace existing CRDs on Helm +install actions, which is not (yet) natively supported by Helm. +https://helm.sh/docs/chart_best_practices/custom_resource_definitions.

+
+createNamespace
+ +bool + +
+(Optional) +

CreateNamespace tells the Helm install action to create the +HelmReleaseSpec.TargetNamespace if it does not exist yet. +On uninstall, the namespace will not be garbage collected.

+
+
+
+

InstallRemediation +

+

+(Appears on: +Install) +

+

InstallRemediation holds the configuration for Helm install remediation.

+
+
+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+retries
+ +int + +
+(Optional) +

Retries is the number of retries that should be attempted on failures before +bailing. Remediation, using an uninstall, is performed between each attempt. +Defaults to ‘0’, a negative integer equals to unlimited retries.

+
+ignoreTestFailures
+ +bool + +
+(Optional) +

IgnoreTestFailures tells the controller to skip remediation when the Helm +tests are run after an install action but fail. Defaults to +‘Test.IgnoreFailures’.

+
+remediateLastFailure
+ +bool + +
+(Optional) +

RemediateLastFailure tells the controller to remediate the last failure, when +no retries remain. Defaults to ‘false’.

+
+
+
+

Kustomize +

+

+(Appears on: +PostRenderer) +

+

Kustomize Helm PostRenderer specification.

+
+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+patches
+ + +[]github.com/fluxcd/pkg/apis/kustomize.Patch + + +
+(Optional) +

Strategic merge and JSON patches, defined as inline YAML objects, +capable of targeting objects based on kind, label and annotation selectors.

+
+images
+ + +[]github.com/fluxcd/pkg/apis/kustomize.Image + + +
+(Optional) +

Images is a list of (image name, new name, new tag or digest) +for changing image names, tags or digests. This can also be achieved with a +patch, but this operator is simpler to specify.

+
+
+
+

PostRenderer +

+

+(Appears on: +HelmReleaseSpec) +

+

PostRenderer contains a Helm PostRenderer specification.

+
+
+ + + + + + + + + + + + + +
FieldDescription
+kustomize
+ + +Kustomize + + +
+(Optional) +

Kustomization to apply as PostRenderer.

+
+
+
+

ReleaseAction +(string alias)

+

+(Appears on: +HelmReleaseStatus) +

+

ReleaseAction is the action to perform a Helm release.

+

Remediation +

+

Remediation defines a consistent interface for InstallRemediation and +UpgradeRemediation.

+

RemediationStrategy +(string alias)

+

+(Appears on: +UpgradeRemediation) +

+

RemediationStrategy returns the strategy to use to remediate a failed install +or upgrade.

+

Rollback +

+

+(Appears on: +HelmReleaseSpec) +

+

Rollback holds the configuration for Helm rollback actions for this +HelmRelease.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+timeout
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

Timeout is the time to wait for any individual Kubernetes operation (like +Jobs for hooks) during the performance of a Helm rollback action. Defaults to +‘HelmReleaseSpec.Timeout’.

+
+disableWait
+ +bool + +
+(Optional) +

DisableWait disables the waiting for resources to be ready after a Helm +rollback has been performed.

+
+disableWaitForJobs
+ +bool + +
+(Optional) +

DisableWaitForJobs disables waiting for jobs to complete after a Helm +rollback has been performed.

+
+disableHooks
+ +bool + +
+(Optional) +

DisableHooks prevents hooks from running during the Helm rollback action.

+
+recreate
+ +bool + +
+(Optional) +

Recreate performs pod restarts for the resource if applicable.

+
+force
+ +bool + +
+(Optional) +

Force forces resource updates through a replacement strategy.

+
+cleanupOnFail
+ +bool + +
+(Optional) +

CleanupOnFail allows deletion of new resources created during the Helm +rollback action when it fails.

+
+
+
+

Snapshot +

+

Snapshot captures a point-in-time copy of the status information for a Helm release, +as managed by the controller.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+apiVersion
+ +string + +
+(Optional) +

APIVersion is the API version of the Snapshot. +Provisional: when the calculation method of the Digest field is changed, +this field will be used to distinguish between the old and new methods.

+
+digest
+ +string + +
+

Digest is the checksum of the release object in storage. +It has the format of <algo>:<checksum>.

+
+name
+ +string + +
+

Name is the name of the release.

+
+namespace
+ +string + +
+

Namespace is the namespace the release is deployed to.

+
+version
+ +int + +
+

Version is the version of the release object in storage.

+
+status
+ +string + +
+

Status is the current state of the release.

+
+chartName
+ +string + +
+

ChartName is the chart name of the release object in storage.

+
+chartVersion
+ +string + +
+

ChartVersion is the chart version of the release object in +storage.

+
+configDigest
+ +string + +
+

ConfigDigest is the checksum of the config (better known as +“values”) of the release object in storage. +It has the format of <algo>:<checksum>.

+
+firstDeployed
+ + +Kubernetes meta/v1.Time + + +
+

FirstDeployed is when the release was first deployed.

+
+lastDeployed
+ + +Kubernetes meta/v1.Time + + +
+

LastDeployed is when the release was last deployed.

+
+deleted
+ + +Kubernetes meta/v1.Time + + +
+(Optional) +

Deleted is when the release was deleted.

+
+testHooks
+ + +TestHookStatus + + +
+(Optional) +

TestHooks is the list of test hooks for the release as observed to be +run by the controller.

+
+ociDigest
+ +string + +
+(Optional) +

OCIDigest is the digest of the OCI artifact associated with the release.

+
+
+
+

Snapshots +([]*./api/v2.Snapshot alias)

+

+(Appears on: +HelmReleaseStatus) +

+

Snapshots is a list of Snapshot objects.

+

Test +

+

+(Appears on: +HelmReleaseSpec) +

+

Test holds the configuration for Helm test actions for this HelmRelease.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+enable
+ +bool + +
+(Optional) +

Enable enables Helm test actions for this HelmRelease after an Helm install +or upgrade action has been performed.

+
+timeout
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

Timeout is the time to wait for any individual Kubernetes operation during +the performance of a Helm test action. Defaults to ‘HelmReleaseSpec.Timeout’.

+
+ignoreFailures
+ +bool + +
+(Optional) +

IgnoreFailures tells the controller to skip remediation when the Helm tests +are run but fail. Can be overwritten for tests run after install or upgrade +actions in ‘Install.IgnoreTestFailures’ and ‘Upgrade.IgnoreTestFailures’.

+
+filters
+ + +Filter + + +
+

Filters is a list of tests to run or exclude from running.

+
+
+
+

TestHookStatus +

+

+(Appears on: +Snapshot) +

+

TestHookStatus holds the status information for a test hook as observed +to be run by the controller.

+
+
+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+lastStarted
+ + +Kubernetes meta/v1.Time + + +
+(Optional) +

LastStarted is the time the test hook was last started.

+
+lastCompleted
+ + +Kubernetes meta/v1.Time + + +
+(Optional) +

LastCompleted is the time the test hook last completed.

+
+phase
+ +string + +
+(Optional) +

Phase the test hook was observed to be in.

+
+
+
+

Uninstall +

+

+(Appears on: +HelmReleaseSpec) +

+

Uninstall holds the configuration for Helm uninstall actions for this +HelmRelease.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+timeout
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

Timeout is the time to wait for any individual Kubernetes operation (like +Jobs for hooks) during the performance of a Helm uninstall action. Defaults +to ‘HelmReleaseSpec.Timeout’.

+
+disableHooks
+ +bool + +
+(Optional) +

DisableHooks prevents hooks from running during the Helm rollback action.

+
+keepHistory
+ +bool + +
+(Optional) +

KeepHistory tells Helm to remove all associated resources and mark the +release as deleted, but retain the release history.

+
+disableWait
+ +bool + +
+(Optional) +

DisableWait disables waiting for all the resources to be deleted after +a Helm uninstall is performed.

+
+deletionPropagation
+ +string + +
+(Optional) +

DeletionPropagation specifies the deletion propagation policy when +a Helm uninstall is performed.

+
+
+
+

Upgrade +

+

+(Appears on: +HelmReleaseSpec) +

+

Upgrade holds the configuration for Helm upgrade actions for this +HelmRelease.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+timeout
+ + +Kubernetes meta/v1.Duration + + +
+(Optional) +

Timeout is the time to wait for any individual Kubernetes operation (like +Jobs for hooks) during the performance of a Helm upgrade action. Defaults to +‘HelmReleaseSpec.Timeout’.

+
+remediation
+ + +UpgradeRemediation + + +
+(Optional) +

Remediation holds the remediation configuration for when the Helm upgrade +action for the HelmRelease fails. The default is to not perform any action.

+
+disableWait
+ +bool + +
+(Optional) +

DisableWait disables the waiting for resources to be ready after a Helm +upgrade has been performed.

+
+disableWaitForJobs
+ +bool + +
+(Optional) +

DisableWaitForJobs disables waiting for jobs to complete after a Helm +upgrade has been performed.

+
+disableHooks
+ +bool + +
+(Optional) +

DisableHooks prevents hooks from running during the Helm upgrade action.

+
+disableOpenAPIValidation
+ +bool + +
+(Optional) +

DisableOpenAPIValidation prevents the Helm upgrade action from validating +rendered templates against the Kubernetes OpenAPI Schema.

+
+force
+ +bool + +
+(Optional) +

Force forces resource updates through a replacement strategy.

+
+preserveValues
+ +bool + +
+(Optional) +

PreserveValues will make Helm reuse the last release’s values and merge in +overrides from ‘Values’. Setting this flag makes the HelmRelease +non-declarative.

+
+cleanupOnFail
+ +bool + +
+(Optional) +

CleanupOnFail allows deletion of new resources created during the Helm +upgrade action when it fails.

+
+crds
+ + +CRDsPolicy + + +
+(Optional) +

CRDs upgrade CRDs from the Helm Chart’s crds directory according +to the CRD upgrade policy provided here. Valid values are Skip, +Create or CreateReplace. Default is Skip and if omitted +CRDs are neither installed nor upgraded.

+

Skip: do neither install nor replace (update) any CRDs.

+

Create: new CRDs are created, existing CRDs are neither updated nor deleted.

+

CreateReplace: new CRDs are created, existing CRDs are updated (replaced) +but not deleted.

+

By default, CRDs are not applied during Helm upgrade action. With this +option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. +https://helm.sh/docs/chart_best_practices/custom_resource_definitions.

+
+
+
+

UpgradeRemediation +

+

+(Appears on: +Upgrade) +

+

UpgradeRemediation holds the configuration for Helm upgrade remediation.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+retries
+ +int + +
+(Optional) +

Retries is the number of retries that should be attempted on failures before +bailing. Remediation, using ‘Strategy’, is performed between each attempt. +Defaults to ‘0’, a negative integer equals to unlimited retries.

+
+ignoreTestFailures
+ +bool + +
+(Optional) +

IgnoreTestFailures tells the controller to skip remediation when the Helm +tests are run after an upgrade action but fail. +Defaults to ‘Test.IgnoreFailures’.

+
+remediateLastFailure
+ +bool + +
+(Optional) +

RemediateLastFailure tells the controller to remediate the last failure, when +no retries remain. Defaults to ‘false’ unless ‘Retries’ is greater than 0.

+
+strategy
+ + +RemediationStrategy + + +
+(Optional) +

Strategy to use for failure remediation. Defaults to ‘rollback’.

+
+
+
+

ValuesReference +

+

+(Appears on: +HelmReleaseSpec) +

+

ValuesReference contains a reference to a resource containing Helm values, +and optionally the key they can be found at.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+kind
+ +string + +
+

Kind of the values referent, valid values are (‘Secret’, ‘ConfigMap’).

+
+name
+ +string + +
+

Name of the values referent. Should reside in the same namespace as the +referring resource.

+
+valuesKey
+ +string + +
+(Optional) +

ValuesKey is the data key where the values.yaml or a specific value can be +found at. Defaults to ‘values.yaml’.

+
+targetPath
+ +string + +
+(Optional) +

TargetPath is the YAML dot notation path the value should be merged at. When +set, the ValuesKey is expected to be a single flat value. Defaults to ‘None’, +which results in the values getting merged at the root.

+
+optional
+ +bool + +
+(Optional) +

Optional marks this ValuesReference as optional. When set, a not found error +for the values reference is ignored, but any ValuesKey, TargetPath or +transient error will still result in a reconciliation failure.

+
+
+
+
+

This page was automatically generated with gen-crd-api-reference-docs

+
diff --git a/docs/spec/README.md b/docs/spec/README.md index 826f2817a..3c481b43d 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -51,7 +51,7 @@ trigger a Helm uninstall. Alerting can be configured with a Kubernetes custom resource that specifies a webhook address, and a group of `HelmRelease` resources to be monitored using the [notification-controller](https://github.com/fluxcd/notification-controller). -The API design of the controller can be found at [helm.toolkit.fluxcd.io/v2beta2](./v2beta2/helmreleases.md). +The API design of the controller can be found at [helm.toolkit.fluxcd.io/v2](./v2/helmreleases.md). ## Backward compatibility diff --git a/docs/spec/v2/README.md b/docs/spec/v2/README.md new file mode 100644 index 000000000..826a91650 --- /dev/null +++ b/docs/spec/v2/README.md @@ -0,0 +1,16 @@ +# helm.toolkit.fluxcd.io/v2 + +This is the v2 API specification for declaratively managing Helm chart +releases with Kubernetes manifests. + +## Specification + +- [HelmRelease CRD](helmreleases.md) + + [Example](helmreleases.md#example) + + [Writing a HelmRelease spec](helmreleases.md#writing-a-helmrelease-spec) + + [Working with HelmReleases](helmreleases.md#working-with-helmreleases) + + [HelmRelease Status](helmreleases.md#helmrelease-status) + +## Implementation + +* [helm-controller](https://github.com/fluxcd/helm-controller/) diff --git a/docs/spec/v2/helmreleases.md b/docs/spec/v2/helmreleases.md new file mode 100644 index 000000000..d437ef905 --- /dev/null +++ b/docs/spec/v2/helmreleases.md @@ -0,0 +1,1692 @@ +# Helm Releases + + + +The `HelmRelease` API allows for controller-driven reconciliation of Helm +releases via Helm actions such as install, upgrade, test, uninstall, and +rollback. In addition to this, it detects and corrects cluster state drift +from the desired release state. + +## Example + +The following is an example of a HelmRelease which installs the +[podinfo Helm chart](https://github.com/stefanprodan/podinfo/tree/master/charts/podinfo). + +```yaml +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: podinfo + namespace: default +spec: + interval: 5m + url: https://stefanprodan.github.io/podinfo +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: podinfo + namespace: default +spec: + interval: 10m + timeout: 5m + chart: + spec: + chart: podinfo + version: '6.5.*' + sourceRef: + kind: HelmRepository + name: podinfo + interval: 5m + releaseName: podinfo + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + test: + enable: true + driftDetection: + mode: enabled + ignore: + - paths: ["/spec/replicas"] + target: + kind: Deployment + values: + replicaCount: 2 +``` + +In the above example: + +- A [HelmRepository](https://fluxcd.io/flux/components/source/helmrepositories/) + named `podinfo` is created, pointing to the Helm repository from which the + podinfo chart can be installed. +- A HelmRelease named `podinfo` is created, that will create a [HelmChart](https://fluxcd.io/flux/components/source/helmcharts/) object + from [the `.spec.chart`](#chart-template) and watch it for Artifact changes. +- The controller will fetch the chart from the HelmChart's Artifact and use it + together with the `.spec.releaseName` and `.spec.values` to confirm if the + Helm release exists and is up-to-date. +- If the Helm release does not exist, is not up-to-date, or has not observed to + be made by the controller based on [the HelmRelease's history](#history), then + the controller will install or upgrade the release. If this fails, it is + allowed to retry the operation a number of times while requeueing between + attempts, as defined by the respective [remediation configurations](#configuring-failure-handling). +- If the [Helm tests](#test-configuration) for the release have not been run + before for this release, the HelmRelease will run them. +- When the Helm release in storage is up-to-date, the controller will check if + the release in the cluster has drifted from the desired state, as defined by + the [drift detection configuration](#drift-detection). If it has, the + controller will [correct the drift](#drift-correction) by re-applying the + desired state. +- The controller will repeat the above steps at the interval defined by + `.spec.interval`, or when the configuration changes in a way that affects the + desired state of the Helm release (e.g. a new chart version or values). + +You can run this example by saving the manifest into `podinfo.yaml`. + +1. Apply the resource on the cluster: + + ```sh + kubectl apply -f podinfo.yaml + ``` + +2. Run `kubectl get helmrelease` to see the HelmRelease: + + ```console + NAME AGE READY STATUS + podinfo 15s True Helm test succeeded for release default/podinfo.v1 with chart podinfo@6.5.3: 3 test hooks completed successfully + ``` + +3. Run `kubectl describe helmrelease podinfo` to see the [Conditions](#conditions) + and [History](#history) in the HelmRelease's Status: + + ```console + ... + Status: + Conditions: + Last Transition Time: 2023-12-04T14:17:47Z + Message: Helm test succeeded for release default/podinfo.v1 with chart podinfo@6.5.3: 3 test hooks completed successfully + Observed Generation: 1 + Reason: TestSucceeded + Status: True + Type: Ready + Last Transition Time: 2023-12-04T14:17:39Z + Message: Helm install succeeded for release default/podinfo.v1 with chart podinfo@6.5.3 + Observed Generation: 1 + Reason: InstallSucceeded + Status: True + Type: Released + Last Transition Time: 2023-12-04T14:17:47Z + Message: Helm test succeeded for release default/podinfo.v1 with chart podinfo@6.5.3: 3 test hooks completed successfully + Observed Generation: 1 + Reason: TestSucceeded + Status: True + Type: TestSuccess + Helm Chart: default/default-podinfo + History: + Chart Name: podinfo + Chart Version: 6.5.3 + Config Digest: sha256:e15c415d62760896bd8bec192a44c5716dc224db9e0fc609b9ac14718f8f9e56 + Digest: sha256:e59aeb8b854f42e44756c2ef552a073051f1fc4f90e68aacbae7f824139580bc + First Deployed: 2023-12-04T14:17:35Z + Last Deployed: 2023-12-04T14:17:35Z + Name: podinfo + Namespace: default + Status: deployed + Test Hooks: + Podinfo - Grpc - Test - Scyhk: + Last Completed: 2023-12-04T14:17:42Z + Last Started: 2023-12-04T14:17:39Z + Phase: Succeeded + Podinfo - Jwt - Test - Scddu: + Last Completed: 2023-12-04T14:17:45Z + Last Started: 2023-12-04T14:17:42Z + Phase: Succeeded + Podinfo - Service - Test - Uibss: + Last Completed: 2023-12-04T14:17:47Z + Last Started: 2023-12-04T14:17:45Z + Phase: Succeeded + Version: 1 + Last Applied Revision: 6.5.3 + Last Attempted Config Digest: sha256:e15c415d62760896bd8bec192a44c5716dc224db9e0fc609b9ac14718f8f9e56 + Last Attempted Generation: 1 + Last Attempted Release Action: install + Last Attempted Revision: 6.5.3 + Observed Generation: 1 + Storage Namespace: default + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal HelmChartCreated 23s helm-controller Created HelmChart/default/default-podinfo with SourceRef 'HelmRepository/default/podinfo' + Normal HelmChartInSync 22s helm-controller HelmChart/default/default-podinfo with SourceRef 'HelmRepository/default/podinfo' is in-sync + Normal InstallSucceeded 18s helm-controller Helm install succeeded for release default/podinfo.v1 with chart podinfo@6.5.3 + Normal TestSucceeded 10s helm-controller Helm test succeeded for release default/podinfo.v1 with chart podinfo@6.5.3: 3 test hooks completed successfully + ``` + +## Writing a HelmRelease spec + +As with all other Kubernetes config, a HelmRelease needs `apiVersion`, +`kind`, and `metadata` fields. The name of a HelmRelease object must be a +valid [DNS subdomain name](https://kubernetes.io/docs/concepts/overview/working-with-objects/names#dns-subdomain-names). + +A HelmRelease also needs a +[`.spec` section](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status). + +### Chart template + +`.spec.chart` is an optional field used by the helm-controller as a template to +create a new [HelmChart resource](https://fluxcd.io/flux/components/source/helmcharts/). + +The spec for the HelmChart is provided via `.spec.chart.spec`, refer to +[writing a HelmChart spec](https://fluxcd.io/flux/components/source/helmcharts/#writing-a-helmchart-spec) +for in-depth information. + +Annotations and labels can be added by configuring the respective +`.spec.chart.metadata` fields. + +The HelmChart is created in the same namespace as the `.sourceRef`, with a name +matching the HelmRelease's `<.metadata.namespace>-<.metadata.name>`, and will +be reported in `.status.helmChart`. + +The chart version of the last release attempt is reported in +`.status.lastAttemptedRevision`. The controller will automatically perform a +Helm release when the HelmChart produces a new chart (version). + +**Warning:** Changing the `.spec.chart` to a Helm chart with a different name +(as specified in the chart's `Chart.yaml`) will cause the controller to +uninstall any previous release before installing the new one. + +**Note:** On multi-tenant clusters, platform admins can disable cross-namespace +references with the `--no-cross-namespace-refs=true` flag. When this flag is +set, the HelmRelease can only refer to Sources in the same namespace as the +HelmRelease object. + +### Chart reference + +`.spec.chartRef` is an optional field used to refer to an [OCIRepository resource](https://fluxcd.io/flux/components/source/ocirepositories/) or a [HelmChart resource](https://fluxcd.io/flux/components/source/helmcharts/) +from which to fetch the Helm chart. The chart is fetched by the controller with the +information provided by `.status.artifact` of the referenced resource. + +For a referenced resource of `kind OCIRepository`, the chart version of the last +release attempt is reported in `.status.lastAttemptedRevision`. The version is in +the format `+`. The digest of the OCI artifact is appended +to the version to ensure that a change in the artifact content triggers a new release. +The controller will automatically perform a Helm upgrade when the `OCIRepository` +detects a new digest in the OCI artifact stored in registry, even if the version +inside `Chart.yaml` is unchanged. + +**Warning:** One of `.spec.chart` or `.spec.chartRef` must be set, but not both. +When switching from `.spec.chart` to `.spec.chartRef`, the controller will perform +an Helm upgrade and will garbage collect the old HelmChart object. + +**Note:** On multi-tenant clusters, platform admins can disable cross-namespace +references with the `--no-cross-namespace-refs=true` controller flag. When this flag is +set, the HelmRelease can only refer to OCIRepositories in the same namespace as the +HelmRelease object. + +#### OCIRepository reference example + +```yaml +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: OCIRepository +metadata: + name: podinfo + namespace: default +spec: + interval: 10m + layerSelector: + mediaType: "application/vnd.cncf.helm.chart.content.v1.tar+gzip" + operation: copy + url: oci://ghcr.io/stefanprodan/charts/podinfo + ref: + semver: ">= 6.0.0" +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: podinfo + namespace: default +spec: + interval: 10m + chartRef: + kind: OCIRepository + name: podinfo + namespace: default + values: + replicaCount: 2 +``` + +#### HelmChart reference example + +```yaml +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmChart +metadata: + name: podinfo + namespace: default +spec: + interval: 10m + chart: podinfo + sourceRef: + kind: HelmRepository + name: podinfo + version: "6.x" + valuesFiles: + - values-prod.yaml +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: podinfo + namespace: default +spec: + interval: 10m + chartRef: + kind: HelmChart + name: podinfo + namespace: default + values: + replicaCount: 2 +``` + +### Release name + +`.spec.releaseName` is an optional field used to specify the name of the Helm +release. It defaults to a composition of `[-]`. + +**Warning:** Changing the release name of a HelmRelease which has already been +installed will not rename the release. Instead, the existing release will be +uninstalled before installing a new release with the new name. + +**Note:** When the composition exceeds the maximum length of 53 characters, the +name is shortened by hashing the release name with SHA-256. The resulting name +is then composed of the first 40 characters of the release name, followed by a +dash (`-`), followed by the first 12 characters of the hash. For example, +`a-very-lengthy-target-namespace-with-a-nice-object-name` becomes +`a-very-lengthy-target-namespace-with-a-nic-97af5d7f41f3`. + +### Target namespace + +`.spec.targetNamespace` is an optional field used to specify the namespace to +which the Helm release is made. It defaults to the namespace of the +HelmRelease. + +**Warning:** Changing the target namespace of a HelmRelease which has already +been installed will not move the release to the new namespace. Instead, the +existing release will be uninstalled before installing a new release in the new +target namespace. + +### Storage namespace + +`.spec.storageNamespace` is an optional field used to specify the namespace +in which Helm stores release information. It defaults to the namespace of the +HelmRelease. + +**Warning:** Changing the storage namespace of a HelmRelease which has already +been installed will not move the release to the new namespace. Instead, the +existing release will be uninstalled before installing a new release in the new +storage namespace. + +**Note:** When making use of the Helm CLI and attempting to make use of +`helm get` commands to inspect a release, the `-n` flag should target the +storage namespace of the HelmRelease. + +### Service Account reference + +`.spec.serviceAccountName` is an optional field used to specify the +Service Account to be impersonated while reconciling the HelmRelease. +For more information, refer to [Role-based access control](#role-based-access-control). + +### Persistent client + +`.spec.persistentClient` is an optional field to instruct the controller to use +a persistent Kubernetes client for this release. If specified, the client will +be reused for the duration of the reconciliation, instead of being created and +destroyed for each (step of a) Helm action. If not set, it defaults to `true.` + +**Note:** This method generally boosts performance but could potentially cause +complications with specific Helm charts. For instance, charts creating Custom +Resource Definitions outside Helm's CRD lifecycle hooks during installation +might face issues where these resources are not recognized as available, +especially by post-install hooks. + +### Max history + +`.spec.maxHistory` is an optional field to configure the number of release +revisions saved by Helm. If not set, it defaults to `5`. + +**Note:** Although setting this to `0` for an unlimited number of revisions is +permissible, it is advised against due to performance reasons. + +### Dependencies + +`.spec.dependsOn` is an optional list to refer to other HelmRelease objects +which the HelmRelease depends on. If specified, the HelmRelease is only allowed +to proceed after the referred HelmReleases are ready, i.e. have the `Ready` +condition marked as `True`. + +This is helpful when there is a need to make sure other resources exist before +the workloads defined in a HelmRelease are released. For example, before +installing objects of a certain Custom Resource kind, the Custom Resource +Defintions and the related controller must exist in the cluster. + +```yaml +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: backend + namespace: default +spec: + # ...omitted for brevity +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: frontend + namespace: default +spec: + # ...omitted for brevity + dependsOn: + - name: backend +``` + +**Note:** This does not account for upgrade ordering. Kubernetes only allows +applying one resource (HelmRelease in this case) at a time, so there is no +way for the controller to know when a dependency HelmRelease may be updated. +Also, circular dependencies between HelmRelease resources must be avoided, +otherwise the interdependent HelmRelease resources will never be reconciled. + +### Values + +The values for the Helm release can be specified in two ways: + +- [Values references](#values-references) +- [Inline values](#inline-values) + +Changes to the combined values will trigger a new Helm release. + +#### Values references + +`.spec.valuesFrom` is an optional list to refer to ConfigMap and Secret +resources from which to take values. The values are merged in the order given, +with the later values overwriting earlier, and then [inline values](#inline-values) +overwriting those. + +An item on the list offers the following subkeys: + +- `kind`: Kind of the values referent, supported values are `ConfigMap` and + `Secret`. +- `name`: The `.metadata.name` of the values referent, in the same namespace as + the HelmRelease. +- `valuesKey` (Optional): The `.data` key where the values.yaml or a specific + value can be found. Defaults to `values.yaml` when omitted. +- `targetPath` (Optional): The YAML dot notation path at which the value should + be merged. When set, the valuesKey is expected to be a single flat value. + Defaults to empty when omitted, which results in the values getting merged at + the root. +- `optional` (Optional): Whether this values reference is optional. When + `true`, a not found error for the values reference is ignored, but any + `valuesKey`, `targetPath` or transient error will still result in a + reconciliation failure. Defaults to `false` when omitted. + +```yaml +spec: + valuesFrom: + - kind: ConfigMap + name: prod-env-values + valuesKey: values-prod.yaml + - kind: Secret + name: prod-tls-values + valuesKey: crt + targetPath: tls.crt + optional: true +``` + +**Note:** The `targetPath` supports the same formatting as you would supply as +an argument to the `helm` binary using `--set [path]=[value]`. In addition to +this, the referred value can contain the same value formats (e.g. `{a,b,c}` for +a list). You can read more about the available formats and limitations in the +[Helm documentation](https://helm.sh/docs/intro/using_helm/#the-format-and-limitations-of---set). + +For JSON strings, the [limitations are the same as while using `helm`](https://github.com/helm/helm/issues/5618) +and require you to escape the full JSON string (including `=`, `[`, `,`, `.`). + +#### Inline values + +`.spec.values` is an optional field to inline values within a HelmRelease. When +[values references](#values-references) are defined, inline values are merged +with the values from these references, overwriting any existing ones. + +```yaml +spec: + values: + replicaCount: 2 +``` + +### Install configuration + +`.spec.install` is an optional field to specify the configuration for the +controller to use when running a [Helm install action](https://helm.sh/docs/helm/helm_install/). + +The field offers the following subfields: + +- `.timeout` (Optional): The time to wait for any individual Kubernetes + operation (like Jobs for hooks) during the installation of the chart. + Defaults to the [global timeout value](#timeout). +- `.crds` (Optional): The Custom Resource Definition install policy to use. + Valid values are `Skip`, `Create` and `CreateReplace`. Default is `Create`, + which will create Custom Resource Definitions when they do not exist. Refer + to [Custom Resource Definition lifecycle](#controlling-the-lifecycle-of-custom-resource-definitions) + for more information. +- `.replace` (Optional): Instructs Helm to re-use the [release name](#release-name), + but only if that name is a deleted release which remains in the history. + Defaults to `false`. +- `.createNamespace` (Optional): Instructs Helm to create the [target namespace](#target-namespace) + if it does not exist. On uninstall, the created namespace will not be garbage + collected. Defaults to `false`. +- `.disableHooks` (Optional): Prevents [chart hooks](https://helm.sh/docs/topics/charts_hooks/) + from running during the installation of the chart. Defaults to `false`. +- `.disableOpenAPIValidation` (Optional): Prevents Helm from validating the + rendered templates against the Kubernetes OpenAPI Schema. Defaults to `false`. +- `.disableWait` (Optional): Disables waiting for resources to be ready after + the installation of the chart. Defaults to `false`. +- `.disableWaitForJobs` (Optional): Disables waiting for any Jobs to complete + after the installation of the chart. Defaults to `false`. + +#### Install remediation + +`.spec.install.remediation` is an optional field to configure the remediation +strategy to use when the installation of a Helm chart fails. + +The field offers the following subfields: + +- `.retries` (Optional): The number of retries that should be attempted on + failures before bailing. Remediation, using an [uninstall](#uninstall-configuration), + is performed between each attempt. Defaults to `0`, a negative integer equals + to an infinite number of retries. +- `.ignoreTestFailures` (Optional): Instructs the controller to not remediate + when a [Helm test](#test-configuration) failure occurs. Defaults to + `.spec.test.ignoreFailures`. +- `.remediateLastFailure` (Optional): Instructs the controller to remediate the + last failure when no retries remain. Defaults to `false`. + +### Upgrade configuration + +`.spec.upgrade` is an optional field to specify the configuration for the +controller to use when running a [Helm upgrade action](https://helm.sh/docs/helm/helm_upgrade/). + +The field offers the following subfields: + +- `.timeout` (Optional): The time to wait for any individual Kubernetes + operation (like Jobs for hooks) during the upgrade of the release. + Defaults to the [global timeout value](#timeout). +- `.crds` (Optional): The Custom Resource Definition upgrade policy to use. + Valid values are `Skip`, `Create` and `CreateReplace`. Default is `None`. + Refer to [Custom Resource Definition lifecycle](#controlling-the-lifecycle-of-custom-resource-definitions) + for more information. +- `.cleanupOnFail` (Optional): Allows deletion of new resources created during + the upgrade of the release when it fails. Defaults to `false`. +- `.disableHooks` (Optional): Prevents [chart hooks](https://helm.sh/docs/topics/charts_hooks/) + from running during the upgrade of the release. Defaults to `false`. +- `.disableOpenAPIValidation` (Optional): Prevents Helm from validating the + rendered templates against the Kubernetes OpenAPI Schema. Defaults to `false`. +- `.disableWait` (Optional): Disables waiting for resources to be ready after + upgrading the release. Defaults to `false`. +- `.disableWaitForJobs` (Optional): Disables waiting for any Jobs to complete + after upgrading the release. Defaults to `false`. +- `.force` (Optional): Forces resource updates through a replacement strategy. + Defaults to `false`. +- `.preserveValues` (Optional): Instructs Helm to re-use the values from the + last release while merging in overrides from [values](#values). Setting + this flag makes the HelmRelease non-declarative. Defaults to `false`. + +#### Upgrade remediation + +`.spec.upgrade.remediation` is an optional field to configure the remediation +strategy to use when the upgrade of a Helm release fails. + +The field offers the following subfields: + +- `.retries` (Optional): The number of retries that should be attempted on + failures before bailing. Remediation, using the `.strategy`, is performed + between each attempt. Defaults to `0`, a negative integer equals to an + infinite number of retries. +- `.strategy` (Optional): The remediation strategy to use when a Helm upgrade + fails. Valid values are `rollback` and `uninstall`. Defaults to `rollback`. +- `.ignoreTestFailures` (Optional): Instructs the controller to not remediate + when a [Helm test](#test-configuration) failure occurs. Defaults to + `.spec.test.ignoreFailures`. +- `.remediateLastFailure` (Optional): Instructs the controller to remediate the + last failure when no retries remain. Defaults to `false` unless `.retries` is + greater than `0`. + +### Test configuration + +`.spec.test` is an optional field to specify the configuration values for the +[Helm test action](https://helm.sh/docs/helm/helm_test/). + +To make the controller run the [Helm tests available for the chart](https://helm.sh/docs/topics/chart_tests/) +after a successful Helm install or upgrade, `.spec.test.enable` can be set to +`true`. When enabled, the test results will be available in the +[`.status.history`](#history) field and emitted as a Kubernetes Event. + +By default, when tests are enabled, failures in tests are considered release +failures, and thus are subject to the triggering Helm action's remediation +configuration. However, test failures can be ignored by setting +`.spec.test.ignoreFailures` to `true`. In this case, no remediation action +will be taken, and the test failure will not affect the `Ready` status +condition. This can be overridden per Helm action by setting the respective +[install](#install-configuration) or [upgrade](#upgrade-configuration) +configuration option. + +```yaml +spec: + test: + enable: true + ignoreFailures: true +``` + +#### Filtering tests + +`.spec.test.filters` is an optional list to include or exclude specific tests +from being run. + +```yaml +spec: + test: + enable: true + filters: + - name: my-release-test-connection + exclude: false + - name: my-release-test-migration + exclude: true +``` + +### Rollback configuration + +`.spec.rollback` is an optional field to specify the configuration values for +a [Helm rollback action](https://helm.sh/docs/helm/helm_rollback/). This +configuration applies when the [upgrade remediation strategy](#upgrade-remediation) +is set to `rollback`. + +The field offers the following subfields: + +- `.timeout` (Optional): The time to wait for any individual Kubernetes + operation (like Jobs for hooks) during the rollback of the release. + Defaults to the [global timeout value](#timeout). +- `.cleanupOnFail` (Optional): Allows deletion of new resources created during + the rollback of the release when it fails. Defaults to `false`. +- `.disableHooks` (Optional): Prevents [chart hooks](https://helm.sh/docs/topics/charts_hooks/) + from running during the rollback of the release. Defaults to `false`. +- `.disableWait` (Optional): Disables waiting for resources to be ready after + rolling back the release. Defaults to `false`. +- `.disableWaitForJobs` (Optional): Disables waiting for any Jobs to complete + after rolling back the release. Defaults to `false`. +- `.force` (Optional): Forces resource updates through a replacement strategy. + Defaults to `false`. +- `.recreate` (Optional): Performs Pod restarts if applicable. Defaults to + `false`. + +### Uninstall configuration + +`.spec.uninstall` is an optional field to specify the configuration values for +a [Helm uninstall action](https://helm.sh/docs/helm/helm_uninstall/). This +configuration applies to the [install remediation](#install-remediation), and +when the [upgrade remediation strategy](#upgrade-remediation) is set to +`uninstall`. + +The field offers the following subfields: + +- `.timeout` (Optional): The time to wait for any individual Kubernetes + operation (like Jobs for hooks) during the uninstalltion of the release. + Defaults to the [global timeout value](#timeout). +- `.deletionPropagation` (Optional): The [deletion propagation policy](https://kubernetes.io/docs/tasks/administer-cluster/use-cascading-deletion/) + when a Helm uninstall is performed. Valid values are `background`, + `foreground` and `orphan`. Defaults to `background`. +- `.disableHooks` (Optional): Prevents [chart hooks](https://helm.sh/docs/topics/charts_hooks/) + from running during the uninstallation of the release. Defaults to `false`. +- `.disableWait` (Optional): Disables waiting for resources to be deleted after + uninstalling the release. Defaults to `false`. +- `.keepHistory` (Optional): Instructs Helm to remove all associated resources + and mark the release as deleted, but to retain the release history. Defaults + to `false`. + +### Drift detection + +`.spec.driftDetection` is an optional field to enable the detection (and +correction) of cluster-state drift compared to the manifest from the Helm +storage. + +When `.spec.driftDetection.mode` is set to `warn` or `enabled`, and the +desired state of the HelmRelease is in-sync with the Helm release object in +the storage, the controller will compare the manifest from the Helm storage +with the current state of the cluster using a +[server-side dry-run apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/). + +If this comparison detects a drift (either due to a resource being created +or modified during the dry-run), the controller will emit a Kubernetes Event +with a short summary of the detected changes. In addition, a more extensive +[JSON Patch](https://datatracker.ietf.org/doc/html/rfc6902) summary is logged +to the controller logs (with `--log-level=debug`). + +#### Drift correction + +Furthermore, when `.spec.driftDetection.mode` is set to `enabled`, the +controller will attempt to correct the drift by creating and patching the +resources based on the server-side dry-run apply result. + +At the end of the correction attempt, it will emit a Kubernetes Event with a +summary of the changes it made and any failures it encountered. In case of a +failure, it will continue to detect and correct drift until the desired state +has been reached, or a new Helm action is triggered (due to e.g. a change to +the spec). + +#### Ignore rules + +`.spec.driftDetection.ignore` is an optional field to provide +[JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901) to ignore while +detecting and correcting drift. This can for example be useful when Horizontal +Pod Autoscaling is enabled for a Deployment, or when a Helm chart has hooks +which mutate a resource. + +```yaml +spec: + driftDetection: + mode: enabled + ignore: + - paths: ["/spec/replicas"] +``` + +**Note:** It is possible to achieve a likewise behavior as using +[ignore annotations](#ignore-annotation) by configuring a JSON Pointer +targeting a whole document (`""`). + +To ignore `.paths` in a specific target resource, a `.target` selector can be +applied to the ignored paths. + +```yaml +spec: + driftDetection: + ignore: + - paths: ["/spec/replicas"] + target: + kind: Deployment +``` + +The following `.target` selectors are available, defining multiple fields +causes the selector to be more specific: + +- `group` (Optional): Matches the `.apiVersion` group of resources while + offering support for regular expressions. For example, `apps`, + `helm.toolkit.fluxcd.io` or `.*.toolkit.fluxcd.io`. +- `version` (Optional): Matches the `.apiVersion` version of resources while + offering support for regular expressions. For example, `v1`, `v2beta2` or + `v2beta[\d]`. +- `kind` (Optional): Matches the `.kind` of resources while offering support + for regular expressions. For example, `Deployment`, `HelmRelelease` or + `(HelmRelease|HelmChart)`. +- `name` (Optional): Matches the `.metadata.name` of resources while offering + support for regular expressions. For example, `podinfo` or `podinfo.*`. +- `namespace` (Optional): Matches the `.metadata.namespace` of resources while + offering support for regular expressions. For example, `my-release-ns` or + `.*-system`. +- `annotationSelector` (Optional): Matches the `.metadata.annotations` of + resources using a [label selector expression](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors). + For example, `environment = production` or `environment notin (staging)`. +- `labelSelector` (Optional): Matches the `.metadata.labels` of resources + using a [label selector expression](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors). + For example, `environment = production` or `environment notin (staging)`. + +#### Ignore annotation + +To exclude certain resources from the comparison, they can be labeled or +annotated with `helm.toolkit.fluxcd.io/driftDetection: disabled`. Using +[post-renderers](#post-renderers), this can be applied to any resource +rendered by Helm. + +```yaml +spec: + postRenderers: + - kustomize: + patches: + - target: + version: v1 + kind: Deployment + name: my-app + patch: | + - op: add + path: /metadata/annotations/helm.toolkit.fluxcd.io~1driftDetection + value: disabled +``` + +**Note:** In many cases, it may be better (and easier) to configure an [ignore +rule](#ignore-rules) to ignore (a portion of) a resource. + +### Post renderers + +`.spec.postRenderers` is an optional list to provide [post rendering](https://helm.sh/docs/topics/advanced/#post-rendering) +capabilities using the following built-in Kustomize directives: + +- [patches](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patches/) (`kustomize.patches`) +- [images](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/images/) (`kustomize.images`) + +Post renderers are applied in the order given, and persisted by Helm to the +manifest for the release in the storage. + +**Note:** [Helm has a limitation at present](https://github.com/helm/helm/issues/7891), +which prevents post renderers from being applied to chart hooks. + +```yaml +spec: + postRenderers: + - kustomize: + patches: + - target: + version: v1 + kind: Deployment + name: metrics-server + patch: | + - op: add + path: /metadata/labels/environment + value: production + images: + - name: docker.io/bitnami/metrics-server + newName: docker.io/bitnami/metrics-server + newTag: 0.4.1-debian-10-r54 +``` + +### KubeConfig reference + +`.spec.kubeConfig.secretRef.name` is an optional field to specify the name of +a Secret containing a KubeConfig. If specified, the Helm operations will be +targeted at the default cluster specified in this KubeConfig instead of using +the in-cluster Service Account. + +The Secret defined in the `.secretRef` must exist in the same namespace as the +HelmRelease. On every reconciliation, the KubeConfig bytes will be loaded from +the `.secretRef.key` (default: `value` or `value.yaml`) of the Secret's data, +and the Secret can thus be regularly updated if cluster access tokens have to +rotate due to expiration. + +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: prod-kubeconfig +type: Opaque +stringData: + value.yaml: | + apiVersion: v1 + kind: Config + # ...omitted for brevity +``` + +**Note:** The KubeConfig should be self-contained and not rely on binaries, the +environment, or credential files from the helm-controller Pod. This matches the +constraints of KubeConfigs from current Cluster API providers. KubeConfigs with +`cmd-path` in them likely won't work without a custom, per-provider installation +of helm-controller. + +When both `.spec.kubeConfig` and a [Service Account reference](#service-account-reference) +are specified, the controller will impersonate the Service Account on the +target cluster. + +The Helm storage is stored on the remote cluster in a namespace that equals to +the namespace of the HelmRelease, or the [configured storage namespace](#storage-namespace). +The release itself is made in a namespace that equals to the namespace of the +HelmRelease, or the [configured target namespace](#target-namespace). The +namespaces are expected to exist, with the exception that the target namespace +can be created on demand by Helm when namespace creation is [configured during +install](#install-configuration). + +Other references to Kubernetes resources in the HelmRelease, like [values +references](#values-references), are expected to exist on the reconciling +cluster. + +### Interval + +`.spec.interval` is a required field that specifies the interval at which the +HelmRelease is reconciled, i.e. the controller ensures the current Helm release +matches the desired state. + +After successfully reconciling the object, the controller requeues it for +inspection at the specified interval. The value must be in a [Go recognized +duration string format](https://pkg.go.dev/time#ParseDuration), e.g. `10m0s` +to reconcile the object every ten minutes. + +If the `.metadata.generation` of a resource changes (due to e.g. a change to +the spec) or the HelmChart revision changes (which generates a Kubernetes +Event), this is handled instantly outside the interval window. + +**Note:** The controller can be configured to apply a jitter to the interval in +order to distribute the load more evenly when multiple HelmRelease objects are +set up with the same interval. For more information, please refer to the +[helm-controller configuration options](https://fluxcd.io/flux/components/helm/options/). + +### Timeout + +`.spec.timeout` is an optional field to specify a timeout for a Helm action like +install, upgrade or rollback. The value must be in a +[Go recognized duration string format](https://pkg.go.dev/time#ParseDuration), +e.g. `5m30s` for a timeout of five minutes and thirty seconds. The default +value is `5m0s`. + +### Suspend + +`.spec.suspend` is an optional field to suspend the reconciliation of a +HelmRelease. When set to `true`, the controller will stop reconciling the +HelmRelease, and changes to the resource or the Helm chart will not result in +a new Helm release. When the field is set to `false` or removed, it will +resume. + +## Working with HelmReleases + +### Configuring failure handling + +From time to time, a Helm installation, upgrade, or accompanying [Helm test](#test-configuration) +may fail. When this happens, by default no action is taken, and the release is +left in a failed state. However, several automatic failure remediation options +can be set via [`.spec.install.remediation`](#install-remediation) and +[`.spec.upgrade.remediation`](#upgrade-remediation). + +By configuring the `.retries` field for the respective action, the controller +will first remediate the failure by performing a Helm rollback or uninstall, and +then reattempt the action. It will repeat this process until the `.retries` +are exhausted, or the action succeeds. + +Once the `.retries` are exhausted, the controller will stop attempting to +remediate the failure, and the Helm release will be left in a failed state. +To ensure the Helm release is brought back to the last known good state or +uninstalled, `.remediateLastFailure` can be set to `true`. +For Helm upgrades, this defaults to `true` if at least one retry is configured. + +When a new release configuration or Helm chart is detected, the controller will +reset the failure counters and attempt to install or upgrade the release again. + +**Note:** In addition to the automatic failure remediation options, the +controller can be instructed to [force a Helm release](#forcing-a-release) or +to [retry a failed Helm release](#resetting-remediation-retries) + +### Controlling the lifecycle of Custom Resource Definitions + +Helm does support [the installation of Custom Resource Definitions](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#method-1-let-helm-do-it-for-you) +(CRDs) as part of a chart. However, it has no native support for +[upgrading CRDs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#some-caveats-and-explanations): + +> There is no support at this time for upgrading or deleting CRDs using Helm. +> This was an explicit decision after much community discussion due to the +> danger for unintentional data loss. Furthermore, there is currently no +> community consensus around how to handle CRDs and their lifecycle. As this +> evolves, Helm will add support for those use cases. + +If you write your own Helm charts, you can work around this limitation by +putting your CRDs into the templates instead of the `crds/` directory, or by +factoring them out into a separate Helm chart as suggested by the [official Helm +documentation](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#method-2-separate-charts). + +However, if you use a third-party Helm chart that installs CRDs, not being able +to upgrade the CRDs via HelmRelease objects might become a cumbersome limitation +within your GitOps workflow. Therefore, Flux allows you to opt in to upgrading +CRDs by setting the `.crds` policy in the [`.spec.install`](#install-configuration) +and [`.spec.upgrade`](#upgrade-configuration) configurations. + +The following policy values are supported: + +- `Skip`: Skip the installation or upgrade of CRDs. This is the default value + for `.spec.upgrade.crds`. +- `Create`: Create CRDs if they do not exist, but do not upgrade or delete them. + This is the default value for `.spec.install.crds`. +- `CreateReplace`: Create new CRDs, update (replace) existing ones, but **do + not** delete CRDs which no longer exist in the current Helm chart. + +For example, if you want to update CRDs when installing and upgrading a Helm +chart, you can set the `.spec.install.crds` and `.spec.upgrade.crds` policies to +`CreateReplace`: + +```yaml +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: my-operator + namespace: default +spec: + interval: 10m + chart: + spec: + chart: my-operator + version: "1.0.1" + sourceRef: + kind: HelmRepository + name: my-operator-repo + interval: 5m + install: + crds: CreateReplace + upgrade: + crds: CreateReplace +``` + +### Role-based access control + +By default, a HelmRelease runs under the cluster admin account and can create, +modify, and delete cluster level objects (ClusterRoles, ClusterRoleBindings, +CustomResourceDefinitions, etc.) and namespaced objects (Deployments, Ingresses, +etc.) + +For certain HelmReleases, a cluster administrator may wish to restrict the +permissions of the HelmRelease to a specific namespace or to a specific set of +namespaced objects. To restrict a HelmRelease, one can assign a Service Account +under which the reconciliation is performed using +[`.spec.serviceAccountName`](#service-account-reference). + +Assuming you want to restrict a group of HelmReleases to a single namespace, +you can create a Service Account with a RoleBinding that grants access only to +that namespace. + +For example, the following Service Account and RoleBinding restricts the +HelmRelease to the `webapp` namespace: + +```yaml +--- +apiVersion: v1 +kind: Namespace +metadata: + name: webapp +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: webapp-reconciler + namespace: webapp +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: webapp-reconciler + namespace: webapp +rules: + - apiGroups: ["*"] + resources: ["*"] + verbs: ["*"] +--- + apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: webapp-reconciler + namespace: webapp + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: webapp-reconciler + subjects: + - kind: ServiceAccount + name: webapp-reconciler + namespace: webapp +``` + +**Note:** The above resources are not created by the helm-controller, but should +be created by a cluster administrator and preferably be managed by a +[Kustomization](https://fluxcd.io/flux/components/kustomize/kustomizations/). + +The Service Account can then be referenced in the HelmRelease: + +```yaml +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: podinfo + namespace: webapp +spec: + serviceAccountName: webapp-reconciler + interval: 5m + chart: + spec: + chart: podinfo + sourceRef: + kind: HelmRepository + name: podinfo +``` + +When the controller reconciles the `podinfo` HelmRelease, it will impersonate +the `webapp-reconciler` Service Account. If the chart contains cluster level +objects like CustomResourceDefinitions, the reconciliation will fail since the +account it runs under has no permissions to alter objects outside the +`webapp` namespace. + +#### Enforcing impersonation + +On multi-tenant clusters, platform admins can enforce impersonation with the +`--default-service-account` flag. + +When the flag is set, HelmReleases which do not have a `.spec.serviceAccountName` +specified will use the Service Account name provided by +`--default-service-account=` in the namespace of the HelmRelease object. + +For further best practices on securing helm-controller, see our +[best practices guide](https://fluxcd.io/flux/security/best-practices). + +### Remote clusters / Cluster-API + +Using a [`.spec.kubeConfig` reference](#kubeconfig-reference), it is possible +to manage the full lifecycle of Helm releases on remote clusters. +This composes well with Cluster-API bootstrap providers such as CAPBK (kubeadm), +CAPA (AWS), and others. + +To reconcile a HelmRelease to a CAPI controlled cluster, put the HelmRelease in +the same namespace as your Cluster object, and set the +`.spec.kubeConfig.secretRef.name` to `-kubeconfig`: + +```yaml +--- +apiVersion: cluster.x-k8s.io/v1alpha3 +kind: Cluster +metadata: + name: stage # the kubeconfig Secret will contain the Cluster name + namespace: capi-stage +spec: + clusterNetwork: + pods: + cidrBlocks: + - 10.100.0.0/16 + serviceDomain: stage-cluster.local + services: + cidrBlocks: + - 10.200.0.0/12 + controlPlaneRef: + apiVersion: controlplane.cluster.x-k8s.io/v1alpha3 + kind: KubeadmControlPlane + name: stage-control-plane + namespace: capi-stage + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3 + kind: DockerCluster + name: stage + namespace: capi-stage +--- +# ... unrelated Cluster API objects omitted for brevity ... +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: kube-prometheus-stack + namespace: capi-stage +spec: + kubeConfig: + secretRef: + name: stage-kubeconfig # Cluster API creates this for the matching Cluster + chart: + spec: + chart: prometheus + version: ">=4.0.0 <5.0.0" + sourceRef: + kind: HelmRepository + name: prometheus-community + install: + remediation: + retries: -1 +``` + +The Cluster and HelmRelease can be created at the same time as long as the +[install remediation configuration](#install-remediation) is set to a +forgiving number of `.retries`. The HelmRelease will then eventually succeed +in installing the Helm chart once the cluster is available. + +If you want to target clusters created by other means than Cluster-API, you can +create a Service Account with the necessary permissions on the target cluster, +generate a KubeConfig for that account, and then create a Secret on the cluster +where helm-controller is running. For example: + +```shell +kubectl -n default create secret generic prod-kubeconfig \ + --from-file=value.yaml=./kubeconfig +``` + +### Triggering a reconcile + +To manually tell the helm-controller to reconcile a HelmRelease outside the +[specified interval window](#interval), it can be annotated with +`reconcile.fluxcd.io/requestedAt: `. + +Annotating the resource queues the HelmRelease for reconciliation if the +`` differs from the last value the controller acted on, as +reported in `.status.lastHandledReconcileAt`. + +Using `kubectl`: + +```sh +kubectl annotate --field-manager=flux-client-side-apply --overwrite helmrelease/ reconcile.fluxcd.io/requestedAt="$(date +%s)" +``` + +Using `flux`: + +```sh +flux reconcile helmrelease +``` + +### Forcing a release + +To instruct the helm-controller to forcefully perform a Helm install or +upgrade without making changes to the spec, it can be annotated with +`reconcile.fluxcd.io/forceAt: ` while simultaneously +[triggering a reconcile](#triggering-a-reconcile) with the same value. + +Annotating the resource forces a one-off Helm install or upgrade if the +`` differs from the last value the controller acted on, as +reported in `.status.lastHandledForceAt` and `.status.lastHandledReconcileAt`. + +Using `kubectl`: + +```sh +TOKEN="$(date +%s)"; \ +kubectl annotate --field-manager=flux-client-side-apply --overwrite helmrelease/ \ +"reconcile.fluxcd.io/requestedAt=$TOKEN" \ +"reconcile.fluxcd.io/forceAt=$TOKEN" +``` + +Using `flux`: + +```sh +flux reconcile helmrelease --force +``` + +### Resetting remediation retries + +To instruct the helm-controller to reset the number of retries while +attempting to perform a Helm release, it can be annotated with +`reconcile.fluxcd.io/resetAt: ` while simultaneously +[triggering a reconcile](#triggering-a-reconcile) with the same value. + +Annotating the resource resets the failure counts on the object if the +`` differs from the last value the controller acted on, as +reported in `.status.lastHandledResetAt` and `.status.lastHandledReconcileAt`. +This effectively allows it to continue to attempt to perform a Helm release +based on the [install](#install-remediation) or [upgrade](#upgrade-remediation) +remediation configuration. + +Using `kubectl`: + +```sh +TOKEN="$(date +%s)"; \ +kubectl annotate --field-manager=flux-client-side-apply --overwrite helmrelease/ \ +"reconcile.fluxcd.io/requestedAt=$TOKEN" \ +"reconcile.fluxcd.io/resetAt=$TOKEN" +``` + +Using `flux`: + +```sh +flux reconcile helmrelease --reset +``` + +### Waiting for `Ready` + +When a change is applied, it is possible to wait for the HelmRelease to reach a +`Ready` state using `kubectl`: + +```sh +kubectl wait helmrelease/ --for=condition=ready --timeout=5m +``` + +### Suspending and resuming + +When you find yourself in a situation where you temporarily want to pause the +reconciliation of a HelmRelease, you can suspend it using the +[`.spec.suspend` field](#suspend). + +#### Suspend a HelmRelease + +In your YAML declaration: + +```yaml +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: +spec: + suspend: true +``` + +Using `kubectl`: + +```sh +kubectl patch helmrelease --field-manager=flux-client-side-apply -p '{\"spec\": {\"suspend\" : true }}' +``` + +Using `flux`: + +```sh +flux suspend helmrelease +``` + +##### Resume a HelmRelease + +In your YAML declaration, comment out (or remove) the field: + +```yaml +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: +spec: + # suspend: true +``` + +**Note:** Setting the field value to `false` has the same effect as removing +it, but does not allow for "hot patching" using e.g. `kubectl` while practicing +GitOps; as the manually applied patch would be overwritten by the declared +state in Git. + +Using `kubectl`: + +```sh +kubectl patch helmrelease --field-manager=flux-client-side-apply -p '{\"spec\" : {\"suspend\" : false }}' +``` + +Using `flux`: + +```sh +flux resume helmrelease +``` + +### Debugging a HelmRelease + +There are several ways to gather information about a HelmRelease for debugging +purposes. + +#### Describe the HelmRelease + +Describing a HelmRelease using `kubectl describe helmrelease ` +displays the latest recorded information for the resource in the Status and +Events sections: + +```console +... +Status: + Conditions: + Last Transition Time: 2023-12-06T18:23:21Z + Message: Failed to install after 1 attempt(s) + Observed Generation: 1 + Reason: RetriesExceeded + Status: True + Type: Stalled + Last Transition Time: 2023-12-06T18:23:21Z + Message: Helm test failed for release podinfo/podinfo.v1 with chart podinfo@6.5.3: 1 error occurred: + * pod podinfo-fault-test-a0tew failed + Observed Generation: 1 + Reason: TestFailed + Status: False + Type: Ready + Last Transition Time: 2023-12-06T18:23:16Z + Message: Helm install succeeded for release podinfo/podinfo.v1 with chart podinfo@6.5.3 + Observed Generation: 1 + Reason: InstallSucceeded + Status: True + Type: Released + Last Transition Time: 2023-12-06T18:23:21Z + Message: Helm test failed for release podinfo/podinfo.v1 with chart podinfo@6.5.3: 1 error occurred: + * pod podinfo-fault-test-a0tew failed + Observed Generation: 1 + Reason: TestFailed + Status: False + Type: TestSuccess +... + History: + Chart Name: podinfo + Chart Version: 6.5.3 + Config Digest: sha256:2598fd0e8c65bae746c6686a61c2b2709f47ba8ed5c36450ae1c30aea9c88e9f + Digest: sha256:24f31c6f2f3da97b217a794b5fb9234818296c971ff9f849144bf07438976e4d + First Deployed: 2023-12-06T18:23:12Z + Last Deployed: 2023-12-06T18:23:12Z + Name: podinfo + Namespace: default + Status: deployed + Test Hooks: + podinfo-fault-test-a0tew: + Last Completed: 2023-12-06T18:23:21Z + Last Started: 2023-12-06T18:23:16Z + Phase: Failed + podinfo-grpc-test-rzg5v: + podinfo-jwt-test-7k1hv: + Podinfo - Service - Test - Bgoeg: + Version: 1 +... +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal HelmChartCreated 88s helm-controller Created HelmChart/podinfo/podinfo-podinfo with SourceRef 'HelmRepository/podinfo/podinfo' + Normal HelmChartInSync 88s helm-controller HelmChart/podinfo/podinfo-podinfo with SourceRef 'HelmRepository/podinfo/podinfo' is in-sync + Normal InstallSucceeded 83s helm-controller Helm install succeeded for release podinfo/podinfo.v1 with chart podinfo@6.5.3 + Warning TestFailed 78s helm-controller Helm test failed for release podinfo/podinfo.v1 with chart podinfo@6.5.3: 1 error occurred: + * pod podinfo-fault-test-a0tew failed +``` + +#### Trace emitted Events + +To view events for specific HelmRelease(s), `kubectl events` can be used in +combination with `--for` to list the Events for specific objects. For example, +running + +```shell +kubectl events --for HelmRelease/ +``` + +lists + +```shell +LAST SEEN TYPE REASON OBJECT MESSAGE +88s Normal HelmChartCreated HelmRelease/podinfo Created HelmChart/podinfo/podinfo-podinfo with SourceRef 'HelmRepository/podinfo/podinfo' +88s Normal HelmChartInSync HelmRelease/podinfo HelmChart/podinfo/podinfo-podinfo with SourceRef 'HelmRepository/podinfo/podinfo' is in-sync +83s Normal InstallSucceeded HelmRelease/podinfo Helm install succeeded for release podinfo/podinfo.v1 with chart podinfo@6.5.3 +78s Warning TestFailed HelmRelease/podinfo Helm test failed for release podinfo/podinfo.v1 with chart podinfo@6.5.3: 1 error occurred: + * pod podinfo-fault-test-a0tew failed +``` + +Besides being reported in Events, the controller may also log reconciliation +errors. The Flux CLI offers commands for filtering the logs for a specific +HelmRelease, e.g. `flux logs --level=error --kind=HelmRelease --name=.` + +## HelmRelease Status + +### History + +The HelmRelease shows the history of Helm releases it has performed up to the +previous successful release as a list in the `.status.history` of the resource. +The history is ordered by the time of the release, with the most recent release +first. + +When [Helm tests](#test-configuration) are enabled, the history will also +include the status of the tests which were run for each release. + +#### History example + +```yaml +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: +status: + history: + - chartName: podinfo + chartVersion: 6.5.3 + configDigest: sha256:803f06d4673b07668ff270301ca54ca5829da3133c1219f47bd9f52a60b22f9f + digest: sha256:3036cf7c06fd35b8ccb15c426fed9ce8a059a0a4befab1a47170b6e962c4d784 + firstDeployed: '2023-12-06T20:38:47Z' + lastDeployed: '2023-12-06T20:52:06Z' + name: podinfo + namespace: podinfo + status: deployed + testHooks: + podinfo-grpc-test-qulpw: + lastCompleted: '2023-12-06T20:52:09Z' + lastStarted: '2023-12-06T20:52:07Z' + phase: Succeeded + podinfo-jwt-test-xe0ch: + lastCompleted: '2023-12-06T20:52:12Z' + lastStarted: '2023-12-06T20:52:09Z' + phase: Succeeded + podinfo-service-test-eh6x2: + lastCompleted: '2023-12-06T20:52:14Z' + lastStarted: '2023-12-06T20:52:12Z' + phase: Succeeded + version: 3 + - chartName: podinfo + chartVersion: 6.5.3 + configDigest: sha256:e15c415d62760896bd8bec192a44c5716dc224db9e0fc609b9ac14718f8f9e56 + digest: sha256:858b157a63889b25379e287e24a9b38beb09a8ae21f31ae2cf7ad53d70744375 + firstDeployed: '2023-12-06T20:38:47Z' + lastDeployed: '2023-12-06T20:39:02Z' + name: podinfo + namespace: podinfo + status: superseded + testHooks: + podinfo-grpc-test-aiuee: + lastCompleted: '2023-12-06T20:39:04Z' + lastStarted: '2023-12-06T20:39:02Z' + phase: Succeeded + podinfo-jwt-test-dme3b: + lastCompleted: '2023-12-06T20:39:07Z' + lastStarted: '2023-12-06T20:39:04Z' + phase: Succeeded + podinfo-service-test-fgvte: + lastCompleted: '2023-12-06T20:39:09Z' + lastStarted: '2023-12-06T20:39:07Z' + phase: Succeeded + version: 2 +``` + +### Conditions + +A HelmRelease enters various states during its lifecycle, reflected as +[Kubernetes Conditions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties). +It can be [reconciling](#reconciling-helmrelease) when it is being processed by +the controller, it can be [ready](#ready-helmrelease) when the Helm release is +installed and up-to-date, or it can [fail](#failed-helmrelease) during +reconciliation. + +The HelmRelease API is compatible with the [kstatus specification](https://github.com/kubernetes-sigs/cli-utils/tree/master/pkg/kstatus), +and reports `Reconciling` and `Stalled` conditions where applicable to provide +better (timeout) support to solutions polling the HelmRelease to become `Ready`. + +#### Reconciling HelmRelease + +The helm-controller marks the HelmRepository as _reconciling_ when it is working +on re-assessing the Helm release state, or working on a Helm action such as +installing or upgrading the release. + +This can be due to one of the following reasons (without this being an +exhaustive list): + +- The desired state of the HelmRelease has changed, and the controller is + working on installing or upgrading the Helm release. +- The generation of the HelmRelease is newer than the [Observed + Generation](#observed-generation). +- The HelmRelease has been installed or upgraded, but the [Helm + test](#test-configuration) is still running. +- The HelmRelease is installed or upgraded, but the controller is working on + [detecting](#drift-detection) or [correcting](#drift-correction) drift. + +When the HelmRelease is "reconciling", the `Ready` Condition status becomes +`Unknown` when the controller is working on a Helm install or upgrade, and the +controller adds a Condition with the following attributes to the HelmRelease's +`.status.conditions`: + +- `type: Reconciling` +- `status: "True"` +- `reason: Progressing` | `reason: ProgressingWithRetry` + +The Condition `message` is updated during the course of the reconciliation to +report the Helm action being performed at any particular moment. + +The Condition has a ["negative polarity"](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties), +and is only present on the HelmRelease while the status is `"True"`. + +#### Ready HelmRelease + +The helm-controller marks the HelmRelease as _ready_ when it has the following +characteristics: + +- The Helm release is installed and up-to-date. This means that the Helm + release has been installed or upgraded, the release's chart has the same + version as the [Helm chart referenced by the HelmRelease](#chart-template), + and the [values](#values) used to install or upgrade the release have not + changed. +- The Helm release has passed any [Helm tests](#test-configuration) that are + enabled. +- The HelmRelease is not being [reconciled](#reconciling-helmrelease). + +When the HelmRelease is "ready", the controller sets a Condition with the +following attributes in the HelmRelease's `.status.conditions`: + +- `type: Ready` +- `status: "True"` +- `reason: InstallSucceeded` | `reason: UpgradeSucceeded` | `reason: TestSucceeded` + +This `Ready` Condition will retain a status value of `"True"` until the +HelmRelease is marked as reconciling, or e.g. an [error occurs](#failed-helmrelease) +due to a failed Helm action. + +When a Helm install or upgrade has completed, the controller sets a Condition +with the following attributes in the HelmRelease's `.status.conditions`: + +- `type: Released` +- `status: "True"` +- `reason: InstallSucceeded` | `reason: UpgradeSucceeded` + +The `Released` Condition will retain a status value of `"True"` until the +next Helm install or upgrade has completed. + +When [Helm tests are enabled](#test-configuration) and completed successfully, +the controller sets a Condition with the following attributes in the +HelmRelease's `.status.conditions`: + +- `type: TestSuccess` +- `status: "True"` +- `reason: TestSucceeded` + +The `TestSuccess` Condition will retain a status value of `"True"` until the +next Helm install or upgrade occurs, or the Helm tests are disabled. + +#### Failed HelmRelease + +The helm-controller may get stuck trying to determine state or produce a Helm +release without completing. This can occur due to some of the following factors: + +- The HelmChart does not have an Artifact, or is not ready. +- The HelmRelease's dependencies are not ready. +- The composition of [values references](#values-references) and [inline values](#inline-values) + failed due to a misconfiguration. +- The Helm action (install, upgrade, rollback, uninstall) failed. +- The Helm action succeeded, but the [Helm test](#test-configuration) failed. + +When the failure is due to an error during a Helm install or upgrade, a +Condition with the following attributes is added: + +- `type: Released` +- `status: "False"` +- `reason: InstallFailed` | `reason: UpgradeFailed` + +In case the failure is due to an error during a Helm test, a Condition with the +following attributes is added: + +- `type: TestSuccess` +- `status: "False"` +- `reason: TestFailed` + +This `TestSuccess` Condition will only count as a failure when the Helm test +results have [not been ignored](#configuring-failure-handling). + +When the failure has resulted in a rollback or uninstall, a Condition with the +following attributes is added: + +- `type: Remediated` +- `status: "True"` +- `reason: RollbackSucceeded` | `reason: UninstallSucceeded` | `reason: RollbackFailed` | `reason: UninstallFailed` + +This `Remediated` Condition will retain a status value of `"True"` until the +next Helm install or upgrade has completed. + +When the HelmRelease is "failing", the controller sets a Condition with the +following attributes in the HelmRelease's `.status.conditions`: + +- `type: Ready` +- `status: "False"` +- `reason: InstallFailed` | `reason: UpgradeFailed` | `reason: TestFailed` | `reason: RollbackSucceeded` | `reason: UninstallSucceeded` | `reason: RollbackFailed` | `reason: UninstallFailed` | `reason: ` + +Note that a HelmRelease can be [reconciling](#reconciling-helmrelease) while +failing at the same time. For example, due to a new release attempt after +remediating a failed Helm action. When a reconciliation fails, the `Reconciling` +Condition reason would be `ProgressingWithRetry`. When the reconciliation is +performed again after the failure, the reason is updated to `Progressing`. + +### Storage Namespace + +The helm-controller reports the active storage namespace in the +`.status.storageNamespace` field. + +When the [`.spec.storageNamespace`](#storage-namespace) is changed, the +controller will use the namespace from the Status to perform a Helm uninstall +for the release in the old storage namespace, before performing a Helm install +using the new storage namespace. + +### Failure Counters + +The helm-controller reports the number of failures it encountered for a +HelmRelease in the `.status.failures`, `.status.installFailures` and +`.status.upgradeFailures` fields. + +The `.status.failures` field is a general counter for all failures, while the +`.status.installFailures` and `.status.upgradeFailures` fields are counters +which are specific to the Helm install and upgrade actions respectively. +The latter two counters are used to determine if the controller is allowed to +retry an action when [install](#install-remediation) or [upgrade](#upgrade-remediation) +remediation is enabled. + +The counters are reset when a new configuration is applied to the HelmRelease, +the [values](#values) change, or when a new Helm chart version is discovered. +In addition, they can be [reset using an annotation](#resetting-remediation-retries). + +### Observed Generation + +The helm-controller reports an observed generation in the HelmRelease's +`.status.observedGeneration`. The observed generation is the latest +`.metadata.generation` which resulted in either a [ready state](#ready-helmrelease), +or stalled due to error it can not recover from without human intervention. + +### Last Attempted Config Digest + +The helm-controller reports the digest for the [values](#values) it last +attempted to perform a Helm install or upgrade with in the +`.status.lastAttemptedConfigDigest` field. + +The digest is used to determine if the controller should reset the +[failure counters](#failure-counters) due to a change in the values. + +### Last Attempted Revision + +The helm-controller reports the revision of the Helm chart it last attempted +to perform a Helm install or upgrade with in the +`.status.lastAttemptedRevision` field. + +The revision is used by the controller to determine if it should reset the +[failure counters](#failure-counters) due to a change in the chart version. + +### Last Attempted Release Action + +The helm-controller reports the last Helm release action it attempted to +perform in the `.status.lastAttemptedReleaseAction` field. The possible values +are `install` and `upgrade`. + +This field is used by the controller to determine the active remediation +strategy for the HelmRelease. + +### Last Handled Reconcile At + +The helm-controller reports the last `reconcile.fluxcd.io/requestedAt` +annotation value it acted on in the `.status.lastHandledReconcileAt` field. + +For practical information about this field, see +[triggering a reconcile](#triggering-a-reconcile). + +### Last Handled Force At + +The helm-controller reports the last `reconcile.fluxcd.io/forceAt` +annotation value it acted on in the `.status.lastHandledForceAt` field. + +For practical information about this field, see +[forcing a release](#forcing-a-release). + +### Last Handled Reset At + +The helm-controller reports the last `reconcile.fluxcd.io/resetAt` +annotation value it acted on in the `.status.lastHandledResetAt` field. + +For practical information about this field, see +[resetting remediation retries](#resetting-remediation-retries). diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt index 44d2aa16e..ab427acb1 100644 --- a/hack/boilerplate.go.txt +++ b/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright 2022 The Flux authors +Copyright 2024 The Flux authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/internal/acl/acl_test.go b/internal/acl/acl_test.go index 2ebf8c8bc..3a0ab7c33 100644 --- a/internal/acl/acl_test.go +++ b/internal/acl/acl_test.go @@ -17,11 +17,13 @@ limitations under the License. package acl import ( - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + "testing" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - "testing" + + v2 "github.com/fluxcd/helm-controller/api/v2" ) func TestAllowsAccessTo(t *testing.T) { diff --git a/internal/action/crds.go b/internal/action/crds.go index 9156e7603..883b9ded7 100644 --- a/internal/action/crds.go +++ b/internal/action/crds.go @@ -33,7 +33,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/cli-runtime/pkg/resource" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) const ( diff --git a/internal/action/diff.go b/internal/action/diff.go index 19b85e0dd..1bb9d3a52 100644 --- a/internal/action/diff.go +++ b/internal/action/diff.go @@ -39,7 +39,7 @@ import ( ssanormalize "github.com/fluxcd/pkg/ssa/normalize" ssautil "github.com/fluxcd/pkg/ssa/utils" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/diff" ) diff --git a/internal/action/diff_test.go b/internal/action/diff_test.go index 5ec1c4852..d9a7caf75 100644 --- a/internal/action/diff_test.go +++ b/internal/action/diff_test.go @@ -46,7 +46,7 @@ import ( ssanormalize "github.com/fluxcd/pkg/ssa/normalize" ssautil "github.com/fluxcd/pkg/ssa/utils" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/kube" ) diff --git a/internal/action/install.go b/internal/action/install.go index 035cd3ab5..9584005ff 100644 --- a/internal/action/install.go +++ b/internal/action/install.go @@ -25,19 +25,19 @@ import ( helmchartutil "helm.sh/helm/v3/pkg/chartutil" helmrelease "helm.sh/helm/v3/pkg/release" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/features" "github.com/fluxcd/helm-controller/internal/postrender" "github.com/fluxcd/helm-controller/internal/release" ) // InstallOption can be used to modify Helm's action.Install after the instructions -// from the v2beta2.HelmRelease have been applied. This is for example useful to +// from the v2.HelmRelease have been applied. This is for example useful to // enable the dry-run setting as a CLI. type InstallOption func(action *helmaction.Install) // Install runs the Helm install action with the provided config, using the -// v2beta2.HelmReleaseSpec of the given object to determine the target release +// v2.HelmReleaseSpec of the given object to determine the target release // and rollback configuration. // // It performs the installation according to the spec, which includes installing diff --git a/internal/action/install_test.go b/internal/action/install_test.go index 64e516617..ea116c81c 100644 --- a/internal/action/install_test.go +++ b/internal/action/install_test.go @@ -24,7 +24,7 @@ import ( helmaction "helm.sh/helm/v3/pkg/action" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) func Test_newInstall(t *testing.T) { diff --git a/internal/action/reset.go b/internal/action/reset.go index 0b579ca6d..8dbd4997d 100644 --- a/internal/action/reset.go +++ b/internal/action/reset.go @@ -21,7 +21,7 @@ import ( "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chartutil" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" intchartutil "github.com/fluxcd/helm-controller/internal/chartutil" ) diff --git a/internal/action/reset_test.go b/internal/action/reset_test.go index cd9f6fad6..1aa3ee877 100644 --- a/internal/action/reset_test.go +++ b/internal/action/reset_test.go @@ -26,7 +26,7 @@ import ( "github.com/fluxcd/pkg/apis/meta" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) func TestMustResetFailures(t *testing.T) { diff --git a/internal/action/rollback.go b/internal/action/rollback.go index 3985597d4..559cb5dfb 100644 --- a/internal/action/rollback.go +++ b/internal/action/rollback.go @@ -19,11 +19,11 @@ package action import ( helmaction "helm.sh/helm/v3/pkg/action" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) // RollbackOption can be used to modify Helm's action.Rollback after the -// instructions from the v2beta2.HelmRelease have been applied. This is for +// instructions from the v2.HelmRelease have been applied. This is for // example useful to enable the dry-run setting as a CLI. type RollbackOption func(*helmaction.Rollback) diff --git a/internal/action/rollback_test.go b/internal/action/rollback_test.go index adb66fd5b..f300d94c3 100644 --- a/internal/action/rollback_test.go +++ b/internal/action/rollback_test.go @@ -24,7 +24,7 @@ import ( helmaction "helm.sh/helm/v3/pkg/action" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) func Test_newRollback(t *testing.T) { diff --git a/internal/action/test.go b/internal/action/test.go index 04a4e509d..a469443b8 100644 --- a/internal/action/test.go +++ b/internal/action/test.go @@ -22,16 +22,16 @@ import ( helmaction "helm.sh/helm/v3/pkg/action" helmrelease "helm.sh/helm/v3/pkg/release" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) // TestOption can be used to modify Helm's action.ReleaseTesting after the -// instructions from the v2beta2.HelmRelease have been applied. This is for +// instructions from the v2.HelmRelease have been applied. This is for // example useful to enable the dry-run setting as a CLI. type TestOption func(action *helmaction.ReleaseTesting) // Test runs the Helm test action with the provided config, using the -// v2beta2.HelmReleaseSpec of the given object to determine the target release +// v2.HelmReleaseSpec of the given object to determine the target release // and test configuration. // // It does not determine if there is a desire to perform the action, this is diff --git a/internal/action/test_test.go b/internal/action/test_test.go index a78dcb78f..03222b1c0 100644 --- a/internal/action/test_test.go +++ b/internal/action/test_test.go @@ -24,7 +24,7 @@ import ( helmaction "helm.sh/helm/v3/pkg/action" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) func Test_newTest(t *testing.T) { diff --git a/internal/action/uninstall.go b/internal/action/uninstall.go index 75fe6126d..8afbc4e6f 100644 --- a/internal/action/uninstall.go +++ b/internal/action/uninstall.go @@ -22,16 +22,16 @@ import ( helmaction "helm.sh/helm/v3/pkg/action" helmrelease "helm.sh/helm/v3/pkg/release" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) // UninstallOption can be used to modify Helm's action.Uninstall after the -// instructions from the v2beta2.HelmRelease have been applied. This is for +// instructions from the v2.HelmRelease have been applied. This is for // example useful to enable the dry-run setting as a CLI. type UninstallOption func(cfg *helmaction.Uninstall) // Uninstall runs the Helm uninstall action with the provided config, using the -// v2beta2.HelmReleaseSpec of the given object to determine the target release +// v2.HelmReleaseSpec of the given object to determine the target release // and uninstall configuration. // // It does not determine if there is a desire to perform the action, this is diff --git a/internal/action/uninstall_test.go b/internal/action/uninstall_test.go index dcb3efe1b..9f8329feb 100644 --- a/internal/action/uninstall_test.go +++ b/internal/action/uninstall_test.go @@ -24,7 +24,7 @@ import ( helmaction "helm.sh/helm/v3/pkg/action" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) func Test_newUninstall(t *testing.T) { diff --git a/internal/action/upgrade.go b/internal/action/upgrade.go index 896ccf6a1..9901d7adc 100644 --- a/internal/action/upgrade.go +++ b/internal/action/upgrade.go @@ -25,19 +25,19 @@ import ( helmchartutil "helm.sh/helm/v3/pkg/chartutil" helmrelease "helm.sh/helm/v3/pkg/release" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/features" "github.com/fluxcd/helm-controller/internal/postrender" "github.com/fluxcd/helm-controller/internal/release" ) // UpgradeOption can be used to modify Helm's action.Upgrade after the instructions -// from the v2beta2.HelmRelease have been applied. This is for example useful to +// from the v2.HelmRelease have been applied. This is for example useful to // enable the dry-run setting as a CLI. type UpgradeOption func(upgrade *helmaction.Upgrade) // Upgrade runs the Helm upgrade action with the provided config, using the -// v2beta2.HelmReleaseSpec of the given object to determine the target release +// v2.HelmReleaseSpec of the given object to determine the target release // and upgrade configuration. // // It performs the upgrade according to the spec, which includes upgrading the diff --git a/internal/action/upgrade_test.go b/internal/action/upgrade_test.go index da62479b7..7c2caa221 100644 --- a/internal/action/upgrade_test.go +++ b/internal/action/upgrade_test.go @@ -24,7 +24,7 @@ import ( helmaction "helm.sh/helm/v3/pkg/action" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) func Test_newUpgrade(t *testing.T) { diff --git a/internal/action/verify.go b/internal/action/verify.go index aaa143e1b..e21c63fe6 100644 --- a/internal/action/verify.go +++ b/internal/action/verify.go @@ -27,7 +27,7 @@ import ( helmdriver "helm.sh/helm/v3/pkg/storage/driver" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/release" ) @@ -90,7 +90,7 @@ func LastRelease(config *helmaction.Configuration, releaseName string) (*helmrel return rls, nil } -// VerifySnapshot verifies the data of the given v2beta2.Snapshot +// VerifySnapshot verifies the data of the given v2.Snapshot // matches the release object in the Helm storage. It returns the verified // release, or an error of type ErrReleaseNotFound, ErrReleaseDisappeared, // ErrReleaseDigest or ErrReleaseNotObserved indicating the reason for the @@ -114,7 +114,7 @@ func VerifySnapshot(config *helmaction.Configuration, snapshot *v2.Snapshot) (rl return rls, nil } -// VerifyReleaseObject verifies the data of the given v2beta2.Snapshot +// VerifyReleaseObject verifies the data of the given v2.Snapshot // matches the given Helm release object. It returns an error of type // ErrReleaseDigest or ErrReleaseNotObserved indicating the reason for the // verification failure, or nil. diff --git a/internal/action/verify_test.go b/internal/action/verify_test.go index 4cc4dbb0e..03519ef79 100644 --- a/internal/action/verify_test.go +++ b/internal/action/verify_test.go @@ -29,7 +29,7 @@ import ( "helm.sh/helm/v3/pkg/storage/driver" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/release" "github.com/fluxcd/helm-controller/internal/storage" "github.com/fluxcd/helm-controller/internal/testutil" diff --git a/internal/chartutil/values.go b/internal/chartutil/values.go index a5a196b5e..c19314641 100644 --- a/internal/chartutil/values.go +++ b/internal/chartutil/values.go @@ -32,7 +32,7 @@ import ( "github.com/fluxcd/pkg/runtime/transform" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) // ErrValuesRefReason is the descriptive reason for an ErrValuesReference. diff --git a/internal/chartutil/values_fuzz_test.go b/internal/chartutil/values_fuzz_test.go index bec7d58fe..fc6a248f1 100644 --- a/internal/chartutil/values_fuzz_test.go +++ b/internal/chartutil/values_fuzz_test.go @@ -30,7 +30,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) func FuzzChartValuesFromReferences(f *testing.F) { diff --git a/internal/chartutil/values_test.go b/internal/chartutil/values_test.go index a0895ec22..d692fdd41 100644 --- a/internal/chartutil/values_test.go +++ b/internal/chartutil/values_test.go @@ -28,7 +28,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) func TestChartValuesFromReferences(t *testing.T) { diff --git a/internal/controller/helmrelease_controller.go b/internal/controller/helmrelease_controller.go index 6fda164a3..083a44528 100644 --- a/internal/controller/helmrelease_controller.go +++ b/internal/controller/helmrelease_controller.go @@ -58,7 +58,7 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" intacl "github.com/fluxcd/helm-controller/internal/acl" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" @@ -424,7 +424,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, patchHelpe return jitter.JitteredRequeueInterval(ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}), nil } -// reconcileDelete deletes the v1beta2.HelmChart of the v2beta2.HelmRelease, +// reconcileDelete deletes the v1beta2.HelmChart of the v2.HelmRelease, // and uninstalls the Helm release if the resource has not been suspended. func (r *HelmReleaseReconciler) reconcileDelete(ctx context.Context, obj *v2.HelmRelease) (ctrl.Result, error) { // Only uninstall the release and delete the HelmChart resource if the @@ -566,7 +566,7 @@ func (r *HelmReleaseReconciler) reconcileUninstall(ctx context.Context, getter g return intreconcile.NewUninstall(cfg, r.EventRecorder).Reconcile(ctx, &intreconcile.Request{Object: obj}) } -// checkDependencies checks if the dependencies of the given v2beta2.HelmRelease +// checkDependencies checks if the dependencies of the given v2.HelmRelease // are Ready. // It returns an error if a dependency can not be retrieved or is not Ready, // otherwise nil. @@ -592,10 +592,10 @@ func (r *HelmReleaseReconciler) checkDependencies(ctx context.Context, obj *v2.H return nil } -// adoptLegacyRelease attempts to adopt a v2beta1 release into a v2beta2 +// adoptLegacyRelease attempts to adopt a v2beta1 release into a v2 // release. // This is done by retrieving the last successful release from the Helm storage -// and converting it to a v2beta2 release snapshot. +// and converting it to a v2 release snapshot. // If the v2beta1 release has already been adopted, this function is a no-op. func (r *HelmReleaseReconciler) adoptLegacyRelease(ctx context.Context, getter genericclioptions.RESTClientGetter, obj *v2.HelmRelease) error { if obj.Status.LastReleaseRevision < 1 || len(obj.Status.History) > 0 { @@ -628,7 +628,7 @@ func (r *HelmReleaseReconciler) adoptLegacyRelease(ctx context.Context, getter g return err } - // Convert it to a v2beta2 release snapshot. + // Convert it to a v2 release snapshot. snap := release.ObservedToSnapshot(release.ObserveRelease(rls)) // If tests are enabled, include them as well. @@ -730,7 +730,7 @@ func (r *HelmReleaseReconciler) getSourceFromOCIRef(ctx context.Context, obj *v2 // waitForHistoryCacheSync returns a function that can be used to wait for the // cache backing the Kubernetes client to be in sync with the current state of -// the v2beta2.HelmRelease. +// the v2.HelmRelease. // This is a trade-off between not caching at all, and introducing a slight // delay to ensure we always have the latest history state. func (r *HelmReleaseReconciler) waitForHistoryCacheSync(obj *v2.HelmRelease) wait.ConditionWithContextFunc { diff --git a/internal/controller/helmrelease_controller_fuzz_test.go b/internal/controller/helmrelease_controller_fuzz_test.go index a66978c92..29ffdbe53 100644 --- a/internal/controller/helmrelease_controller_fuzz_test.go +++ b/internal/controller/helmrelease_controller_fuzz_test.go @@ -35,7 +35,7 @@ import ( "github.com/fluxcd/pkg/runtime/patch" sourcev1 "github.com/fluxcd/source-controller/api/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) func FuzzHelmReleaseReconciler_reconcile(f *testing.F) { diff --git a/internal/controller/helmrelease_controller_test.go b/internal/controller/helmrelease_controller_test.go index 76fc285ca..a7921a358 100644 --- a/internal/controller/helmrelease_controller_test.go +++ b/internal/controller/helmrelease_controller_test.go @@ -54,7 +54,7 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" intacl "github.com/fluxcd/helm-controller/internal/acl" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index eeb9e26ba..510d7a5e0 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -34,7 +34,7 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" // +kubebuilder:scaffold:imports ) diff --git a/internal/features/features.go b/internal/features/features.go index 10ce03edf..07d681eb3 100644 --- a/internal/features/features.go +++ b/internal/features/features.go @@ -54,7 +54,7 @@ const ( // AdoptLegacyReleases enables the adoption of the historical Helm release // based on the status fields from a v2beta1 HelmRelease object. - // This is enabled by default to support an upgrade path from v2beta1 to v2beta2 + // This is enabled by default to support an upgrade path from v2beta1 to v2 // without the need to upgrade the Helm release. But it can be disabled to // avoid potential abuse of the adoption mechanism. AdoptLegacyReleases = "AdoptLegacyReleases" diff --git a/internal/postrender/build.go b/internal/postrender/build.go index 5dc419af3..bac4ea48b 100644 --- a/internal/postrender/build.go +++ b/internal/postrender/build.go @@ -19,7 +19,7 @@ package postrender import ( helmpostrender "helm.sh/helm/v3/pkg/postrender" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) // BuildPostRenderers creates the post-renderer instances from a HelmRelease @@ -32,10 +32,8 @@ func BuildPostRenderers(rel *v2.HelmRelease) helmpostrender.PostRenderer { for _, r := range rel.Spec.PostRenderers { if r.Kustomize != nil { renderers = append(renderers, &Kustomize{ - Patches: r.Kustomize.Patches, - PatchesStrategicMerge: r.Kustomize.PatchesStrategicMerge, - PatchesJSON6902: r.Kustomize.PatchesJSON6902, - Images: r.Kustomize.Images, + Patches: r.Kustomize.Patches, + Images: r.Kustomize.Images, }) } } diff --git a/internal/postrender/kustomize.go b/internal/postrender/kustomize.go index 9195be811..5e74954f4 100644 --- a/internal/postrender/kustomize.go +++ b/internal/postrender/kustomize.go @@ -21,7 +21,6 @@ import ( "encoding/json" "sync" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "sigs.k8s.io/kustomize/api/krusty" "sigs.k8s.io/kustomize/api/resmap" kustypes "sigs.k8s.io/kustomize/api/types" @@ -34,12 +33,6 @@ import ( type Kustomize struct { // Patches is a list of patches to apply to the rendered manifests. Patches []kustomize.Patch - // PatchesStrategicMerge is a list of strategic merge patches to apply to - // the rendered manifests. - PatchesStrategicMerge []apiextensionsv1.JSON - // PatchesJSON6902 is a list of JSON patches to apply to the rendered - // manifests. - PatchesJSON6902 []kustomize.JSON6902Patch // Images is a list of images to replace in the rendered manifests. Images []kustomize.Image } @@ -66,23 +59,6 @@ func (k *Kustomize) Run(renderedManifests *bytes.Buffer) (modifiedManifests *byt }) } - // Add strategic merge patches. - for _, m := range k.PatchesStrategicMerge { - cfg.PatchesStrategicMerge = append(cfg.PatchesStrategicMerge, kustypes.PatchStrategicMerge(m.Raw)) - } - - // Add JSON 6902 patches. - for i, m := range k.PatchesJSON6902 { - patch, err := json.Marshal(m.Patch) - if err != nil { - return nil, err - } - cfg.PatchesJson6902 = append(cfg.PatchesJson6902, kustypes.Patch{ - Patch: string(patch), - Target: adaptSelector(&k.PatchesJSON6902[i].Target), - }) - } - // Write kustomization config to file. kustomization, err := json.Marshal(cfg) if err != nil { diff --git a/internal/postrender/kustomize_test.go b/internal/postrender/kustomize_test.go index c526b8795..7681603c1 100644 --- a/internal/postrender/kustomize_test.go +++ b/internal/postrender/kustomize_test.go @@ -18,16 +18,14 @@ package postrender import ( "bytes" - "encoding/json" "testing" . "github.com/onsi/gomega" - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "sigs.k8s.io/yaml" "github.com/fluxcd/pkg/apis/kustomize" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) const replaceImageMock = `apiVersion: v1 @@ -61,14 +59,12 @@ spec: func Test_postRendererKustomize_Run(t *testing.T) { tests := []struct { - name string - renderedManifests string - patches string - patchesStrategicMerge string - patchesJson6902 string - images string - expectManifests string - expectErr bool + name string + renderedManifests string + patches string + images string + expectManifests string + expectErr bool }{ { name: "image tag", @@ -121,12 +117,12 @@ spec: { name: "json 6902", renderedManifests: json6902Mock, - patchesJson6902: ` + patches: ` - target: version: v1 kind: Pod name: json6902 - patch: + patch: | - op: test path: /metadata/annotations/c value: foo @@ -188,33 +184,6 @@ metadata: d: "42" e: "42" name: json6902 -`, - }, - { - name: "strategic merge test", - renderedManifests: strategicMergeMock, - patchesStrategicMerge: ` -- apiVersion: apps/v1 - kind: Deployment - metadata: - name: nginx - spec: - template: - spec: - containers: - - name: nginx - image: nignx:latest -`, - expectManifests: `apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx -spec: - template: - spec: - containers: - - image: nignx:latest - name: nginx `, }, { @@ -255,14 +224,12 @@ spec: t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - spec, err := mockKustomize(tt.patches, tt.patchesStrategicMerge, tt.patchesJson6902, tt.images) + spec, err := mockKustomize(tt.patches, tt.images) g.Expect(err).ToNot(HaveOccurred()) k := &Kustomize{ - Patches: spec.Patches, - PatchesStrategicMerge: spec.PatchesStrategicMerge, - PatchesJSON6902: spec.PatchesJSON6902, - Images: spec.Images, + Patches: spec.Patches, + Images: spec.Images, } gotModifiedManifests, err := k.Run(bytes.NewBufferString(tt.renderedManifests)) if tt.expectErr { @@ -277,31 +244,17 @@ spec: } } -func mockKustomize(patches, patchesStrategicMerge, patchesJson6902, images string) (*v2.Kustomize, error) { +func mockKustomize(patches, images string) (*v2.Kustomize, error) { var targeted []kustomize.Patch if err := yaml.Unmarshal([]byte(patches), &targeted); err != nil { return nil, err } - b, err := yaml.YAMLToJSON([]byte(patchesStrategicMerge)) - if err != nil { - return nil, err - } - var strategicMerge []v1.JSON - if err := json.Unmarshal(b, &strategicMerge); err != nil { - return nil, err - } - var json6902 []kustomize.JSON6902Patch - if err := yaml.Unmarshal([]byte(patchesJson6902), &json6902); err != nil { - return nil, err - } var imgs []kustomize.Image if err := yaml.Unmarshal([]byte(images), &imgs); err != nil { return nil, err } return &v2.Kustomize{ - Patches: targeted, - PatchesStrategicMerge: strategicMerge, - PatchesJSON6902: json6902, - Images: imgs, + Patches: targeted, + Images: imgs, }, nil } diff --git a/internal/reconcile/atomic_release.go b/internal/reconcile/atomic_release.go index 6f233613b..d26324a78 100644 --- a/internal/reconcile/atomic_release.go +++ b/internal/reconcile/atomic_release.go @@ -35,7 +35,7 @@ import ( "github.com/fluxcd/pkg/runtime/patch" "github.com/fluxcd/pkg/ssa/jsondiff" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/diff" interrors "github.com/fluxcd/helm-controller/internal/errors" @@ -315,10 +315,6 @@ func (r *AtomicRelease) actionForState(ctx context.Context, req *Request, state } req.Object.Status.History.Truncate(ignoreFailures) - // TODO(hidde): this allows existing UIs to continue to display this - // field, but should be removed in a future release. - req.Object.Status.LastAppliedRevision = req.Object.Status.History.Latest().ChartVersion - if forceRequested { log.Info(msgWithReason("forcing upgrade for in-sync release", "force requested through annotation")) return NewUpgrade(r.configFactory, r.eventRecorder), nil diff --git a/internal/reconcile/atomic_release_test.go b/internal/reconcile/atomic_release_test.go index 5d0aee128..108f39154 100644 --- a/internal/reconcile/atomic_release_test.go +++ b/internal/reconcile/atomic_release_test.go @@ -41,7 +41,7 @@ import ( "github.com/fluxcd/pkg/runtime/patch" "github.com/fluxcd/pkg/ssa/jsondiff" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/kube" "github.com/fluxcd/helm-controller/internal/release" diff --git a/internal/reconcile/correct_cluster_drift.go b/internal/reconcile/correct_cluster_drift.go index 320b4feef..693d2bff8 100644 --- a/internal/reconcile/correct_cluster_drift.go +++ b/internal/reconcile/correct_cluster_drift.go @@ -29,7 +29,7 @@ import ( "github.com/fluxcd/pkg/ssa" "github.com/fluxcd/pkg/ssa/jsondiff" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" ) diff --git a/internal/reconcile/correct_cluster_drift_test.go b/internal/reconcile/correct_cluster_drift_test.go index 3efde7955..66dcc9dee 100644 --- a/internal/reconcile/correct_cluster_drift_test.go +++ b/internal/reconcile/correct_cluster_drift_test.go @@ -34,7 +34,7 @@ import ( "github.com/fluxcd/pkg/ssa" "github.com/fluxcd/pkg/ssa/jsondiff" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/testutil" ) diff --git a/internal/reconcile/helmchart_template.go b/internal/reconcile/helmchart_template.go index aa0772052..6529bf206 100644 --- a/internal/reconcile/helmchart_template.go +++ b/internal/reconcile/helmchart_template.go @@ -34,7 +34,7 @@ import ( "github.com/fluxcd/pkg/ssa" sourcev1 "github.com/fluxcd/source-controller/api/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/acl" "github.com/fluxcd/helm-controller/internal/strings" ) @@ -43,22 +43,22 @@ import ( // based on the given Request data. // // It does this by building a v1beta2.HelmChart from the template declared in -// the v2beta2.HelmRelease, and then reconciling that v1beta2.HelmChart using +// the v2.HelmRelease, and then reconciling that v1beta2.HelmChart using // a server-side apply. // // When the server-side apply succeeds, the namespaced name of the chart is -// written to the Status.HelmChart field of the v2beta2.HelmRelease. If the +// written to the Status.HelmChart field of the v2.HelmRelease. If the // server-side apply fails, the error is returned to the caller and indicates // they should retry. // // When at the beginning of the reconciliation the deletion timestamp is set -// on the v2beta2.HelmRelease, or the Status.HelmChart differs from the +// on the v2.HelmRelease, or the Status.HelmChart differs from the // namespaced name of the chart to be applied, the existing chart is deleted. // The deletion is observed, and when it completes, the Status.HelmChart is // cleared. If the deletion fails, the error is returned to the caller and // indicates they should retry. // -// In case the v2beta2.HelmRelease is marked for deletion, the reconciler will +// In case the v2.HelmRelease is marked for deletion, the reconciler will // not continue to attempt to create or update the v1beta2.HelmChart. type HelmChartTemplate struct { client client.Client diff --git a/internal/reconcile/helmchart_template_test.go b/internal/reconcile/helmchart_template_test.go index 8cee2bcc7..287274dcc 100644 --- a/internal/reconcile/helmchart_template_test.go +++ b/internal/reconcile/helmchart_template_test.go @@ -34,7 +34,7 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/acl" ) diff --git a/internal/reconcile/install.go b/internal/reconcile/install.go index 0d51a5556..db92e0cb2 100644 --- a/internal/reconcile/install.go +++ b/internal/reconcile/install.go @@ -28,7 +28,7 @@ import ( "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" diff --git a/internal/reconcile/install_test.go b/internal/reconcile/install_test.go index 4b4fe3efe..011fd842a 100644 --- a/internal/reconcile/install_test.go +++ b/internal/reconcile/install_test.go @@ -38,7 +38,7 @@ import ( "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" diff --git a/internal/reconcile/reconcile.go b/internal/reconcile/reconcile.go index 1cd9a0999..6c51dd1cc 100644 --- a/internal/reconcile/reconcile.go +++ b/internal/reconcile/reconcile.go @@ -22,7 +22,7 @@ import ( helmchart "helm.sh/helm/v3/pkg/chart" helmchartutil "helm.sh/helm/v3/pkg/chartutil" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) const ( diff --git a/internal/reconcile/release.go b/internal/reconcile/release.go index 825d81291..233c38d94 100644 --- a/internal/reconcile/release.go +++ b/internal/reconcile/release.go @@ -26,7 +26,7 @@ import ( helmrelease "helm.sh/helm/v3/pkg/release" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/release" "github.com/fluxcd/helm-controller/internal/storage" diff --git a/internal/reconcile/release_test.go b/internal/reconcile/release_test.go index 66ca6c9b2..9c050cf1e 100644 --- a/internal/reconcile/release_test.go +++ b/internal/reconcile/release_test.go @@ -28,7 +28,7 @@ import ( "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" ) diff --git a/internal/reconcile/rollback_remediation.go b/internal/reconcile/rollback_remediation.go index 3fbad7099..7a5301883 100644 --- a/internal/reconcile/rollback_remediation.go +++ b/internal/reconcile/rollback_remediation.go @@ -29,7 +29,7 @@ import ( "github.com/fluxcd/pkg/runtime/conditions" "github.com/fluxcd/pkg/runtime/logger" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" diff --git a/internal/reconcile/rollback_remediation_test.go b/internal/reconcile/rollback_remediation_test.go index 71d0bb134..b3d3ff5ba 100644 --- a/internal/reconcile/rollback_remediation_test.go +++ b/internal/reconcile/rollback_remediation_test.go @@ -37,7 +37,7 @@ import ( "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" diff --git a/internal/reconcile/state.go b/internal/reconcile/state.go index e4b32594b..b3bf7d03e 100644 --- a/internal/reconcile/state.go +++ b/internal/reconcile/state.go @@ -31,7 +31,7 @@ import ( ) // ReleaseStatus represents the status of a Helm release as determined by -// comparing the Helm storage with the v2beta2.HelmRelease object. +// comparing the Helm storage with the v2.HelmRelease object. type ReleaseStatus string // String returns the string representation of the release status. @@ -47,10 +47,10 @@ const ( // Helm storage. ReleaseStatusAbsent ReleaseStatus = "Absent" // ReleaseStatusUnmanaged indicates that the release is present in the Helm - // storage, but is not managed by the v2beta2.HelmRelease object. + // storage, but is not managed by the v2.HelmRelease object. ReleaseStatusUnmanaged ReleaseStatus = "Unmanaged" // ReleaseStatusOutOfSync indicates that the release is present in the Helm - // storage, but is not in sync with the v2beta2.HelmRelease object. + // storage, but is not in sync with the v2.HelmRelease object. ReleaseStatusOutOfSync ReleaseStatus = "OutOfSync" // ReleaseStatusDrifted indicates that the release is present in the Helm // storage, but the cluster state has drifted from the manifest in the @@ -63,7 +63,7 @@ const ( // storage, but has not been tested. ReleaseStatusUntested ReleaseStatus = "Untested" // ReleaseStatusInSync indicates that the release is present in the Helm - // storage, and is in sync with the v2beta2.HelmRelease object. + // storage, and is in sync with the v2.HelmRelease object. ReleaseStatusInSync ReleaseStatus = "InSync" // ReleaseStatusFailed indicates that the release is present in the Helm // storage, but has failed. @@ -71,7 +71,7 @@ const ( ) // ReleaseState represents the state of a Helm release as determined by -// comparing the Helm storage with the v2beta2.HelmRelease object. +// comparing the Helm storage with the v2.HelmRelease object. type ReleaseState struct { // Status is the status of the release. Status ReleaseStatus @@ -83,7 +83,7 @@ type ReleaseState struct { } // DetermineReleaseState determines the state of the Helm release as compared -// to the v2beta2.HelmRelease object. It returns a ReleaseState that indicates +// to the v2.HelmRelease object. It returns a ReleaseState that indicates // the status of the release, and an error if the state could not be determined. func DetermineReleaseState(ctx context.Context, cfg *action.ConfigFactory, req *Request) (ReleaseState, error) { rls, err := action.LastRelease(cfg.Build(nil), req.Object.GetReleaseName()) diff --git a/internal/reconcile/state_test.go b/internal/reconcile/state_test.go index 3f702d696..2a53980c2 100644 --- a/internal/reconcile/state_test.go +++ b/internal/reconcile/state_test.go @@ -33,7 +33,7 @@ import ( ssanormalize "github.com/fluxcd/pkg/ssa/normalize" ssautil "github.com/fluxcd/pkg/ssa/utils" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/kube" "github.com/fluxcd/helm-controller/internal/release" diff --git a/internal/reconcile/suite_test.go b/internal/reconcile/suite_test.go index caa7fdee7..c3d8b1d97 100644 --- a/internal/reconcile/suite_test.go +++ b/internal/reconcile/suite_test.go @@ -42,7 +42,7 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" ) const testFieldManager = "helm-controller" diff --git a/internal/reconcile/test.go b/internal/reconcile/test.go index 56e9882ef..e8a2dfda6 100644 --- a/internal/reconcile/test.go +++ b/internal/reconcile/test.go @@ -29,7 +29,7 @@ import ( "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/release" "github.com/fluxcd/helm-controller/internal/storage" diff --git a/internal/reconcile/test_test.go b/internal/reconcile/test_test.go index 8ed0bdd8c..bf4b68c9e 100644 --- a/internal/reconcile/test_test.go +++ b/internal/reconcile/test_test.go @@ -36,7 +36,7 @@ import ( "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" diff --git a/internal/reconcile/uninstall.go b/internal/reconcile/uninstall.go index f92843499..4aac7df08 100644 --- a/internal/reconcile/uninstall.go +++ b/internal/reconcile/uninstall.go @@ -31,7 +31,7 @@ import ( "github.com/fluxcd/pkg/runtime/conditions" "github.com/fluxcd/pkg/runtime/logger" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/release" "github.com/fluxcd/helm-controller/internal/storage" diff --git a/internal/reconcile/uninstall_remediation.go b/internal/reconcile/uninstall_remediation.go index eeea2d0fc..d36ba9281 100644 --- a/internal/reconcile/uninstall_remediation.go +++ b/internal/reconcile/uninstall_remediation.go @@ -29,7 +29,7 @@ import ( "github.com/fluxcd/pkg/runtime/conditions" "github.com/fluxcd/pkg/runtime/logger" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/release" ) diff --git a/internal/reconcile/uninstall_remediation_test.go b/internal/reconcile/uninstall_remediation_test.go index f6abe2745..9836007c7 100644 --- a/internal/reconcile/uninstall_remediation_test.go +++ b/internal/reconcile/uninstall_remediation_test.go @@ -35,7 +35,7 @@ import ( eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" diff --git a/internal/reconcile/uninstall_test.go b/internal/reconcile/uninstall_test.go index 65b118ccc..3211b46f8 100644 --- a/internal/reconcile/uninstall_test.go +++ b/internal/reconcile/uninstall_test.go @@ -36,7 +36,7 @@ import ( "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" diff --git a/internal/reconcile/unlock.go b/internal/reconcile/unlock.go index e6e4da5fa..18da580a6 100644 --- a/internal/reconcile/unlock.go +++ b/internal/reconcile/unlock.go @@ -28,7 +28,7 @@ import ( "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/release" "github.com/fluxcd/helm-controller/internal/storage" @@ -41,7 +41,7 @@ import ( // This write to the Helm storage is observed, and updates the Status.History // field if the persisted object targets the same release version. // -// Any pending state marks the v2beta2.HelmRelease object with +// Any pending state marks the v2.HelmRelease object with // ReleasedCondition=False, even if persisting the object to the Helm storage // fails. // diff --git a/internal/reconcile/unlock_test.go b/internal/reconcile/unlock_test.go index 7b13c8300..7c797b26a 100644 --- a/internal/reconcile/unlock_test.go +++ b/internal/reconcile/unlock_test.go @@ -36,7 +36,7 @@ import ( "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" diff --git a/internal/reconcile/upgrade.go b/internal/reconcile/upgrade.go index 6a43b79d1..070e5d4f0 100644 --- a/internal/reconcile/upgrade.go +++ b/internal/reconcile/upgrade.go @@ -28,7 +28,7 @@ import ( "github.com/fluxcd/pkg/runtime/conditions" "github.com/fluxcd/pkg/runtime/logger" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" diff --git a/internal/reconcile/upgrade_test.go b/internal/reconcile/upgrade_test.go index 08bc98632..9a1c4c13e 100644 --- a/internal/reconcile/upgrade_test.go +++ b/internal/reconcile/upgrade_test.go @@ -38,7 +38,7 @@ import ( "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/conditions" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" diff --git a/internal/release/observation.go b/internal/release/observation.go index 0afcc0be9..437bb94f8 100644 --- a/internal/release/observation.go +++ b/internal/release/observation.go @@ -25,7 +25,7 @@ import ( helmrelease "helm.sh/helm/v3/pkg/release" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/chartutil" "github.com/fluxcd/helm-controller/internal/digest" ) @@ -152,7 +152,7 @@ func ObserveRelease(rel *helmrelease.Release, filter ...DataFilter) Observation return obsRel } -// ObservedToSnapshot returns a v2beta2.Snapshot constructed from the +// ObservedToSnapshot returns a v2.Snapshot constructed from the // Observation data. Calculating the (config) digest using the // digest.Canonical algorithm. func ObservedToSnapshot(rls Observation) *v2.Snapshot { @@ -172,7 +172,7 @@ func ObservedToSnapshot(rls Observation) *v2.Snapshot { } } -// TestHooksFromRelease returns the list of v2beta2.TestHookStatus for the +// TestHooksFromRelease returns the list of v2.TestHookStatus for the // given release, indexed by name. func TestHooksFromRelease(rls *helmrelease.Release) map[string]*v2.TestHookStatus { hooks := make(map[string]*v2.TestHookStatus) diff --git a/internal/release/observation_test.go b/internal/release/observation_test.go index a6875631e..afd3182c4 100644 --- a/internal/release/observation_test.go +++ b/internal/release/observation_test.go @@ -25,7 +25,7 @@ import ( helmrelease "helm.sh/helm/v3/pkg/release" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/testutil" ) diff --git a/main.go b/main.go index 43ceec888..679c8853d 100644 --- a/main.go +++ b/main.go @@ -49,7 +49,7 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2" - v2 "github.com/fluxcd/helm-controller/api/v2beta2" + v2 "github.com/fluxcd/helm-controller/api/v2" intdigest "github.com/fluxcd/helm-controller/internal/digest" // +kubebuilder:scaffold:imports