diff --git a/api/v1beta1/helmchart_types.go b/api/v1beta1/helmchart_types.go index 96f027800..01fde150f 100644 --- a/api/v1beta1/helmchart_types.go +++ b/api/v1beta1/helmchart_types.go @@ -45,6 +45,15 @@ type HelmChartSpec struct { // +required Interval metav1.Duration `json:"interval"` + // 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 @@ -65,6 +74,14 @@ type HelmChartSpec struct { Suspend bool `json:"suspend,omitempty"` } +const ( + // ReconcileStrategyChartVersion reconciles when the version of the Helm chart is different. + ReconcileStrategyChartVersion string = "ChartVersion" + + // ReconcileStrategyRevision reconciles when the Revision of the source is different. + ReconcileStrategyRevision string = "Revision" +) + // LocalHelmChartSourceReference contains enough information to let you locate // the typed referenced object at namespace level. type LocalHelmChartSourceReference struct { diff --git a/config/crd/bases/source.toolkit.fluxcd.io_helmcharts.yaml b/config/crd/bases/source.toolkit.fluxcd.io_helmcharts.yaml index 21a499a8d..fe40562bf 100644 --- a/config/crd/bases/source.toolkit.fluxcd.io_helmcharts.yaml +++ b/config/crd/bases/source.toolkit.fluxcd.io_helmcharts.yaml @@ -62,6 +62,13 @@ spec: interval: description: The interval at which to check the Source for updates. 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 reference to the Source the chart is available at. properties: diff --git a/controllers/helmchart_controller.go b/controllers/helmchart_controller.go index 50583bca5..ee6b93e90 100644 --- a/controllers/helmchart_controller.go +++ b/controllers/helmchart_controller.go @@ -27,6 +27,7 @@ import ( "strings" "time" + "github.com/Masterminds/semver/v3" securejoin "github.com/cyphar/filepath-securejoin" "github.com/go-logr/logr" helmchart "helm.sh/helm/v3/pkg/chart" @@ -526,9 +527,29 @@ func (r *HelmChartReconciler) reconcileFromTarballArtifact(ctx context.Context, return sourcev1.HelmChartNotReady(chart, sourcev1.StorageOperationFailedReason, err.Error()), err } + v, err := semver.NewVersion(helmChart.Metadata.Version) + if err != nil { + err = fmt.Errorf("semver error: %w", err) + return sourcev1.HelmChartNotReady(chart, sourcev1.StorageOperationFailedReason, err.Error()), err + } + + version := v.String() + if chart.Spec.ReconcileStrategy == sourcev1.ReconcileStrategyRevision { + // Isolate the commit SHA from GitRepository type artifacts by removing the branch/ prefix. + splitRev := strings.Split(artifact.Revision, "/") + v, err := v.SetMetadata(splitRev[len(splitRev)-1]) + if err != nil { + err = fmt.Errorf("semver error: %w", err) + return sourcev1.HelmChartNotReady(chart, sourcev1.StorageOperationFailedReason, err.Error()), err + } + + version = v.String() + helmChart.Metadata.Version = v.String() + } + // Return early if the revision is still the same as the current chart artifact - newArtifact := r.Storage.NewArtifactFor(chart.Kind, chart.ObjectMeta.GetObjectMeta(), helmChart.Metadata.Version, - fmt.Sprintf("%s-%s.tgz", helmChart.Metadata.Name, helmChart.Metadata.Version)) + newArtifact := r.Storage.NewArtifactFor(chart.Kind, chart.ObjectMeta.GetObjectMeta(), version, + fmt.Sprintf("%s-%s.tgz", helmChart.Metadata.Name, version)) if !force && apimeta.IsStatusConditionTrue(chart.Status.Conditions, meta.ReadyCondition) && chart.GetArtifact().HasRevision(newArtifact.Revision) { if newArtifact.URL != artifact.URL { r.Storage.SetArtifactURL(chart.GetArtifact()) diff --git a/controllers/helmchart_controller_test.go b/controllers/helmchart_controller_test.go index ba107daad..de3f7ad32 100644 --- a/controllers/helmchart_controller_test.go +++ b/controllers/helmchart_controller_test.go @@ -709,7 +709,7 @@ var _ = Describe("HelmChartReconciler", func() { err = f.Close() Expect(err).NotTo(HaveOccurred()) - _, err = wt.Commit("Chart version bump", &git.CommitOptions{ + commit, err := wt.Commit("Chart version bump", &git.CommitOptions{ Author: &object.Signature{ Name: "John Doe", Email: "john@example.com", @@ -735,6 +735,21 @@ var _ = Describe("HelmChartReconciler", func() { Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeFalse()) + When("Setting reconcileStrategy to Revision", func() { + updated := &sourcev1.HelmChart{} + Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) + updated.Spec.ReconcileStrategy = sourcev1.ReconcileStrategyRevision + Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) + got := &sourcev1.HelmChart{} + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), key, got) + return got.Status.Artifact.Revision != updated.Status.Artifact.Revision && + storage.ArtifactExist(*got.Status.Artifact) + }, timeout, interval).Should(BeTrue()) + Expect(got.Status.Artifact.Revision).To(ContainSubstring(updated.Status.Artifact.Revision)) + Expect(got.Status.Artifact.Revision).To(ContainSubstring(commit.String())) + }) + When("Setting valid valuesFiles attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) diff --git a/docs/api/source.md b/docs/api/source.md index 7b1fede46..05852e456 100644 --- a/docs/api/source.md +++ b/docs/api/source.md @@ -555,6 +555,21 @@ Kubernetes meta/v1.Duration +reconcileStrategy
+ +string + + + +(Optional) +

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

+ + + + valuesFiles
[]string @@ -1613,6 +1628,21 @@ Kubernetes meta/v1.Duration +reconcileStrategy
+ +string + + + +(Optional) +

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

+ + + + valuesFiles
[]string diff --git a/docs/spec/v1beta1/helmcharts.md b/docs/spec/v1beta1/helmcharts.md index 067004f93..6c4461c2e 100644 --- a/docs/spec/v1beta1/helmcharts.md +++ b/docs/spec/v1beta1/helmcharts.md @@ -28,6 +28,15 @@ type HelmChartSpec struct { // +required Interval metav1.Duration `json:"interval"` + // 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 @@ -49,6 +58,18 @@ type HelmChartSpec struct { } ``` +### Reconciliation strategies + +```go +const ( + // ReconcileStrategyChartVersion creates a new chart artifact when the version of the Helm chart is different. + ReconcileStrategyChartVersion string = "ChartVersion" + + // ReconcileStrategyRevision creates a new chart artifact when the Revision of the SourceRef is different. + ReconcileStrategyRevision string = "Revision" +) +``` + ### Reference types ```go @@ -230,6 +251,23 @@ spec: - ./charts/podinfo/values-production.yaml ``` +Reconcile with every change to the source revision: + +```yaml +apiVersion: source.toolkit.fluxcd.io/v1beta1 +kind: HelmChart +metadata: + name: podinfo + namespace: default +spec: + chart: ./charts/podinfo + sourceRef: + name: podinfo + kind: GitRepository + interval: 10m + reconcileStrategy: Revision +``` + ## Status examples Successful chart pull: