Skip to content
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

Add reconcile strategy for HelmCharts #308

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions api/v1beta1/helmchart_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
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
Expand Up @@ -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:
Expand Down
25 changes: 23 additions & 2 deletions controllers/helmchart_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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())
Expand Down
17 changes: 16 additions & 1 deletion controllers/helmchart_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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())
Expand Down
30 changes: 30 additions & 0 deletions docs/api/source.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
38 changes: 38 additions & 0 deletions docs/spec/v1beta1/helmcharts.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down