Skip to content

Add reconcile strategy for HelmCharts #308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 8, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions api/v1beta1/helmchart_types.go
Original file line number Diff line number Diff line change
@@ -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 {
7 changes: 7 additions & 0 deletions config/crd/bases/source.toolkit.fluxcd.io_helmcharts.yaml
Original file line number Diff line number Diff line change
@@ -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:
25 changes: 23 additions & 2 deletions controllers/helmchart_controller.go
Original file line number Diff line number Diff line change
@@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to extract this in fluxcd/pkg as it's being used in notification-controller also.

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())
17 changes: 16 additions & 1 deletion controllers/helmchart_controller_test.go
Original file line number Diff line number Diff line change
@@ -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())
30 changes: 30 additions & 0 deletions docs/api/source.md
Original file line number Diff line number Diff line change
@@ -555,6 +555,21 @@ Kubernetes meta/v1.Duration
</tr>
<tr>
<td>
<code>reconcileStrategy</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>Determines what enables reconciliation. Valid values are (&lsquo;ChartVersion&rsquo;,
&lsquo;Revision&rsquo;). See the documentation of the values for an explanation on their
behavior.
Defaults to ChartVersion when omitted.</p>
</td>
</tr>
<tr>
<td>
<code>valuesFiles</code><br>
<em>
[]string
@@ -1613,6 +1628,21 @@ Kubernetes meta/v1.Duration
</tr>
<tr>
<td>
<code>reconcileStrategy</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>Determines what enables reconciliation. Valid values are (&lsquo;ChartVersion&rsquo;,
&lsquo;Revision&rsquo;). See the documentation of the values for an explanation on their
behavior.
Defaults to ChartVersion when omitted.</p>
</td>
</tr>
<tr>
<td>
<code>valuesFiles</code><br>
<em>
[]string
38 changes: 38 additions & 0 deletions docs/spec/v1beta1/helmcharts.md
Original file line number Diff line number Diff line change
@@ -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: