Add ReconcileStrategy to HelmChart
This commit adds a `ReconcileStrategy` field to the `HelmChart` resource, which allows defining when a new chart should be packaged and/or published if it originates from a `Bucket` or `GitRepository` resource. The two available strategies are: - `ChartVersion`: creates a new artifact when the version of the Helm chart as defined in the `Chart.yaml` from the Source is different from the current version. - `Revision`: creates a new artifact when the revision of the Source is different from the current revision. For the `Revision` strategy, the (checksum part of the) revision of the artifact the chart originatesfrom is added as SemVer metadata. A chart from a `GitRepository` with Artifact revision `main/f0faacd5164a875ebdbd9e3fab778f49c5aadbbc` and a chart with e.g. SemVer `0.1.0` will be published as `0.1.0+f0faacd5164a875ebdbd9e3fab778f49c5aadbbc`. A chart from a `Bucket` with Artifact revision `f0faacd5164a875ebdbd9e3fab778f49c5aadbbc` and a chart with e.g. SemVer `0.1.0` will be published as `0.1.0+f0faacd5164a875ebdbd9e3fab778f49c5aadbbc`. Signed-off-by: Dylan Arbour <arbourd@users.noreply.github.com>
This commit is contained in:
parent
c4cc0a7ccf
commit
27c385b957
|
@ -45,6 +45,15 @@ type HelmChartSpec struct {
|
||||||
// +required
|
// +required
|
||||||
Interval metav1.Duration `json:"interval"`
|
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
|
// 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.
|
// 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
|
// 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"`
|
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
|
// LocalHelmChartSourceReference contains enough information to let you locate
|
||||||
// the typed referenced object at namespace level.
|
// the typed referenced object at namespace level.
|
||||||
type LocalHelmChartSourceReference struct {
|
type LocalHelmChartSourceReference struct {
|
||||||
|
|
|
@ -62,6 +62,13 @@ spec:
|
||||||
interval:
|
interval:
|
||||||
description: The interval at which to check the Source for updates.
|
description: The interval at which to check the Source for updates.
|
||||||
type: string
|
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:
|
sourceRef:
|
||||||
description: The reference to the Source the chart is available at.
|
description: The reference to the Source the chart is available at.
|
||||||
properties:
|
properties:
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
securejoin "github.com/cyphar/filepath-securejoin"
|
securejoin "github.com/cyphar/filepath-securejoin"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
helmchart "helm.sh/helm/v3/pkg/chart"
|
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
|
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
|
// 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,
|
newArtifact := r.Storage.NewArtifactFor(chart.Kind, chart.ObjectMeta.GetObjectMeta(), version,
|
||||||
fmt.Sprintf("%s-%s.tgz", helmChart.Metadata.Name, helmChart.Metadata.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 !force && apimeta.IsStatusConditionTrue(chart.Status.Conditions, meta.ReadyCondition) && chart.GetArtifact().HasRevision(newArtifact.Revision) {
|
||||||
if newArtifact.URL != artifact.URL {
|
if newArtifact.URL != artifact.URL {
|
||||||
r.Storage.SetArtifactURL(chart.GetArtifact())
|
r.Storage.SetArtifactURL(chart.GetArtifact())
|
||||||
|
|
|
@ -709,7 +709,7 @@ var _ = Describe("HelmChartReconciler", func() {
|
||||||
err = f.Close()
|
err = f.Close()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = wt.Commit("Chart version bump", &git.CommitOptions{
|
commit, err := wt.Commit("Chart version bump", &git.CommitOptions{
|
||||||
Author: &object.Signature{
|
Author: &object.Signature{
|
||||||
Name: "John Doe",
|
Name: "John Doe",
|
||||||
Email: "john@example.com",
|
Email: "john@example.com",
|
||||||
|
@ -735,6 +735,21 @@ var _ = Describe("HelmChartReconciler", func() {
|
||||||
Expect(helmChart.Values["testDefault"]).To(BeTrue())
|
Expect(helmChart.Values["testDefault"]).To(BeTrue())
|
||||||
Expect(helmChart.Values["testOverride"]).To(BeFalse())
|
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() {
|
When("Setting valid valuesFiles attribute", func() {
|
||||||
updated := &sourcev1.HelmChart{}
|
updated := &sourcev1.HelmChart{}
|
||||||
Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed())
|
Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed())
|
||||||
|
|
|
@ -555,6 +555,21 @@ Kubernetes meta/v1.Duration
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
<code>reconcileStrategy</code><br>
|
||||||
|
<em>
|
||||||
|
string
|
||||||
|
</em>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<em>(Optional)</em>
|
||||||
|
<p>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.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
<code>valuesFiles</code><br>
|
<code>valuesFiles</code><br>
|
||||||
<em>
|
<em>
|
||||||
[]string
|
[]string
|
||||||
|
@ -1613,6 +1628,21 @@ Kubernetes meta/v1.Duration
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
<code>reconcileStrategy</code><br>
|
||||||
|
<em>
|
||||||
|
string
|
||||||
|
</em>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<em>(Optional)</em>
|
||||||
|
<p>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.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
<code>valuesFiles</code><br>
|
<code>valuesFiles</code><br>
|
||||||
<em>
|
<em>
|
||||||
[]string
|
[]string
|
||||||
|
|
|
@ -28,6 +28,15 @@ type HelmChartSpec struct {
|
||||||
// +required
|
// +required
|
||||||
Interval metav1.Duration `json:"interval"`
|
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
|
// 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.
|
// 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
|
// 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
|
### Reference types
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -230,6 +251,23 @@ spec:
|
||||||
- ./charts/podinfo/values-production.yaml
|
- ./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
|
## Status examples
|
||||||
|
|
||||||
Successful chart pull:
|
Successful chart pull:
|
||||||
|
|
Loading…
Reference in New Issue