Record SHA1 of values of last release attempt
This commit is contained in:
parent
6dba659c3c
commit
6b1d28a736
|
@ -357,6 +357,10 @@ type HelmReleaseStatus struct {
|
|||
// +optional
|
||||
LastAttemptedRevision string `json:"lastAttemptedRevision,omitempty"`
|
||||
|
||||
// LastAttemptedValuesChecksum is the SHA1 checksum of the values of the last reconciliation attempt.
|
||||
// +optional
|
||||
LastAttemptedValuesChecksum string `json:"lastAttemptedValuesChecksum,omitempty"`
|
||||
|
||||
// LastReleaseRevision is the revision of the last successful Helm release.
|
||||
// +optional
|
||||
LastReleaseRevision int `json:"lastReleaseRevision,omitempty"`
|
||||
|
@ -410,30 +414,31 @@ func SetHelmReleaseCondition(hr *HelmRelease, condition string, status corev1.Co
|
|||
|
||||
// SetHelmReleaseReadiness sets the ReadyCondition, ObservedGeneration, LastAttemptedRevision,
|
||||
// and LastReleaseRevision, on the HelmRelease.
|
||||
func SetHelmReleaseReadiness(hr *HelmRelease, status corev1.ConditionStatus, reason, message string, revision string, releaseRevision int) {
|
||||
func SetHelmReleaseReadiness(hr *HelmRelease, status corev1.ConditionStatus, reason, message string, revision string, releaseRevision int, valuesChecksum string) {
|
||||
SetHelmReleaseCondition(hr, ReadyCondition, status, reason, message)
|
||||
hr.Status.ObservedGeneration = hr.Generation
|
||||
hr.Status.LastAttemptedRevision = revision
|
||||
hr.Status.LastReleaseRevision = releaseRevision
|
||||
hr.Status.LastAttemptedValuesChecksum = valuesChecksum
|
||||
}
|
||||
|
||||
// HelmReleaseNotReady registers a failed release attempt of the given HelmRelease.
|
||||
func HelmReleaseNotReady(hr HelmRelease, revision string, releaseRevision int, reason, message string) HelmRelease {
|
||||
SetHelmReleaseReadiness(&hr, corev1.ConditionFalse, reason, message, revision, releaseRevision)
|
||||
func HelmReleaseNotReady(hr HelmRelease, revision string, releaseRevision int, valuesChecksum, reason, message string) HelmRelease {
|
||||
SetHelmReleaseReadiness(&hr, corev1.ConditionFalse, reason, message, revision, releaseRevision, valuesChecksum)
|
||||
hr.Status.Failures = hr.Status.Failures + 1
|
||||
return hr
|
||||
}
|
||||
|
||||
// HelmReleaseReady registers a successful release attempt of the given HelmRelease.
|
||||
func HelmReleaseReady(hr HelmRelease, revision string, releaseRevision int, reason, message string) HelmRelease {
|
||||
SetHelmReleaseReadiness(&hr, corev1.ConditionTrue, reason, message, revision, releaseRevision)
|
||||
func HelmReleaseReady(hr HelmRelease, revision string, releaseRevision int, valuesChecksum, reason, message string) HelmRelease {
|
||||
SetHelmReleaseReadiness(&hr, corev1.ConditionTrue, reason, message, revision, releaseRevision, valuesChecksum)
|
||||
hr.Status.LastAppliedRevision = revision
|
||||
hr.Status.Failures = 0
|
||||
return hr
|
||||
}
|
||||
|
||||
// ShouldUpgrade determines if an Helm upgrade action needs to be performed for the given HelmRelease.
|
||||
func ShouldUpgrade(hr HelmRelease, revision string, releaseRevision int) bool {
|
||||
func ShouldUpgrade(hr HelmRelease, revision string, releaseRevision int, valuesChecksum string) bool {
|
||||
switch {
|
||||
case hr.Status.LastAttemptedRevision != revision:
|
||||
return true
|
||||
|
@ -441,6 +446,8 @@ func ShouldUpgrade(hr HelmRelease, revision string, releaseRevision int) bool {
|
|||
return true
|
||||
case hr.Generation != hr.Status.ObservedGeneration:
|
||||
return true
|
||||
case hr.Status.LastAttemptedValuesChecksum != valuesChecksum:
|
||||
return true
|
||||
case hr.Status.Failures > 0 &&
|
||||
(hr.Spec.Upgrade.MaxRetries < 0 || hr.Status.Failures < int64(hr.Spec.Upgrade.MaxRetries)):
|
||||
return true
|
||||
|
@ -489,17 +496,6 @@ func ShouldUninstall(hr HelmRelease, releaseRevision int) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// HelmReleaseReadyMessage returns the message of the HelmRelease
|
||||
// of type Ready with status true if present, or an empty string.
|
||||
func HelmReleaseReadyMessage(hr HelmRelease) string {
|
||||
for _, condition := range hr.Status.Conditions {
|
||||
if condition.Type == ReadyCondition && condition.Status == corev1.ConditionTrue {
|
||||
return condition.Message
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
const (
|
||||
// ReconcileAtAnnotation is the annotation used for triggering a
|
||||
// reconciliation outside of the defined schedule.
|
||||
|
|
|
@ -345,6 +345,10 @@ spec:
|
|||
description: LastAttemptedRevision is the revision of the last reconciliation
|
||||
attempt.
|
||||
type: string
|
||||
lastAttemptedValuesChecksum:
|
||||
description: LastAttemptedValuesChecksum is the SHA1 checksum of the
|
||||
values of the last reconciliation attempt.
|
||||
type: string
|
||||
lastReleaseRevision:
|
||||
description: LastReleaseRevision is the revision of the last successful
|
||||
Helm release.
|
||||
|
|
|
@ -18,6 +18,7 @@ package controllers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -115,7 +116,7 @@ func (r *HelmReleaseReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error)
|
|||
|
||||
if hr.Spec.Suspend {
|
||||
msg := "HelmRelease is suspended, skipping reconciliation"
|
||||
hr = v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, v2.SuspendedReason, msg)
|
||||
hr = v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, hr.Status.LastAttemptedValuesChecksum, v2.SuspendedReason, msg)
|
||||
if err := r.Status().Update(ctx, &hr); err != nil {
|
||||
log.Error(err, "unable to update status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
|
@ -141,7 +142,7 @@ func (r *HelmReleaseReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error)
|
|||
msg = "HelmChart is not ready"
|
||||
r.event(hr, hr.Status.LastAttemptedRevision, recorder.EventSeverityInfo, msg)
|
||||
}
|
||||
hr = v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, v2.ArtifactFailedReason, msg)
|
||||
hr = v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, hr.Status.LastAttemptedValuesChecksum, v2.ArtifactFailedReason, msg)
|
||||
if err := r.Status().Update(ctx, &hr); err != nil {
|
||||
log.Error(err, "unable to update status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
|
@ -152,7 +153,7 @@ func (r *HelmReleaseReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error)
|
|||
// Check chart artifact readiness
|
||||
if hc.GetArtifact() == nil {
|
||||
msg := "HelmChart is not ready"
|
||||
hr = v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, v2.ArtifactFailedReason, msg)
|
||||
hr = v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, hr.Status.LastAttemptedValuesChecksum, v2.ArtifactFailedReason, msg)
|
||||
r.event(hr, hr.Status.LastAttemptedRevision, recorder.EventSeverityInfo, msg)
|
||||
log.Info(msg)
|
||||
if err := r.Status().Update(ctx, &hr); err != nil {
|
||||
|
@ -169,7 +170,7 @@ func (r *HelmReleaseReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error)
|
|||
r.event(hr, hc.GetArtifact().Revision, recorder.EventSeverityInfo, msg)
|
||||
log.Info(msg)
|
||||
|
||||
hr = v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, v2.DependencyNotReadyReason, err.Error())
|
||||
hr = v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, hr.Status.LastAttemptedValuesChecksum, v2.DependencyNotReadyReason, err.Error())
|
||||
if err := r.Status().Update(ctx, &hr); err != nil {
|
||||
log.Error(err, "unable to update status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
|
@ -184,7 +185,7 @@ func (r *HelmReleaseReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error)
|
|||
// Compose values
|
||||
values, err := r.composeValues(ctx, hr)
|
||||
if err != nil {
|
||||
hr = v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, v2.InitFailedReason, err.Error())
|
||||
hr = v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, hr.Status.LastAttemptedValuesChecksum, v2.InitFailedReason, err.Error())
|
||||
r.event(hr, hr.Status.LastAttemptedRevision, recorder.EventSeverityError, err.Error())
|
||||
if err := r.Status().Update(ctx, &hr); err != nil {
|
||||
log.Error(err, "unable to update status")
|
||||
|
@ -282,7 +283,7 @@ func (r *HelmReleaseReconciler) release(log logr.Logger, hr v2.HelmRelease, sour
|
|||
unlock, err := lock(fmt.Sprintf("%s-%s", hr.GetName(), hr.GetNamespace()))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("lockfile error: %w", err)
|
||||
return v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
return v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, hr.Status.LastAttemptedValuesChecksum, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
|
@ -296,34 +297,36 @@ func (r *HelmReleaseReconciler) release(log logr.Logger, hr v2.HelmRelease, sour
|
|||
// Download artifact
|
||||
artifactPath, err := download(source.GetArtifact().URL, tmpDir)
|
||||
if err != nil {
|
||||
return v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, v2.ArtifactFailedReason, "artifact acquisition failed"), err
|
||||
return v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, hr.Status.LastAttemptedValuesChecksum, v2.ArtifactFailedReason, "artifact acquisition failed"), err
|
||||
}
|
||||
|
||||
// Load chart
|
||||
loadedChart, err := loader.Load(artifactPath)
|
||||
if err != nil {
|
||||
return v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, v2.ArtifactFailedReason, "failed to load chart"), err
|
||||
return v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, hr.Status.LastAttemptedValuesChecksum, v2.ArtifactFailedReason, "failed to load chart"), err
|
||||
}
|
||||
|
||||
// Initialize config
|
||||
cfg, err := newActionCfg(log, r.Config, hr)
|
||||
if err != nil {
|
||||
return v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, v2.InitFailedReason, "failed to initialize Helm action configuration"), err
|
||||
return v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, hr.Status.LastAttemptedValuesChecksum, v2.InitFailedReason, "failed to initialize Helm action configuration"), err
|
||||
}
|
||||
|
||||
// Get the current release
|
||||
rel, err := cfg.Releases.Deployed(hr.GetReleaseName())
|
||||
if err != nil && !errors.Is(err, driver.ErrNoDeployedReleases) {
|
||||
return v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, v2.InitFailedReason, "failed to determine if release exists"), err
|
||||
return v2.HelmReleaseNotReady(hr, hr.Status.LastAttemptedRevision, hr.Status.LastReleaseRevision, hr.Status.LastAttemptedValuesChecksum, v2.InitFailedReason, "failed to determine if release exists"), err
|
||||
}
|
||||
|
||||
valuesChecksum := calculateValuesChecksum(values)
|
||||
|
||||
// Install or upgrade the release
|
||||
success := true
|
||||
if errors.Is(err, driver.ErrNoDeployedReleases) {
|
||||
rel, err = install(cfg, loadedChart, hr, values)
|
||||
r.handleHelmActionResult(hr, source, err, "install", v2.InstalledCondition, v2.InstallSucceededReason, v2.InstallFailedReason)
|
||||
success = err == nil
|
||||
} else if v2.ShouldUpgrade(hr, source.GetArtifact().Revision, rel.Version) {
|
||||
} else if v2.ShouldUpgrade(hr, source.GetArtifact().Revision, rel.Version, valuesChecksum) {
|
||||
rel, err = upgrade(cfg, loadedChart, hr, values)
|
||||
r.handleHelmActionResult(hr, source, err, "upgrade", v2.UpgradedCondition, v2.UpgradeSucceededReason, v2.UpgradeFailedReason)
|
||||
success = err == nil
|
||||
|
@ -359,9 +362,9 @@ func (r *HelmReleaseReconciler) release(log logr.Logger, hr v2.HelmRelease, sour
|
|||
}
|
||||
|
||||
if !success {
|
||||
return v2.HelmReleaseNotReady(hr, source.GetArtifact().Revision, releaseRevision, v2.ReconciliationFailedReason, "release reconciliation failed"), err
|
||||
return v2.HelmReleaseNotReady(hr, source.GetArtifact().Revision, releaseRevision, valuesChecksum, v2.ReconciliationFailedReason, "release reconciliation failed"), err
|
||||
}
|
||||
return v2.HelmReleaseReady(hr, source.GetArtifact().Revision, releaseRevision, v2.ReconciliationSucceededReason, "release reconciliation succeeded"), nil
|
||||
return v2.HelmReleaseReady(hr, source.GetArtifact().Revision, releaseRevision, valuesChecksum, v2.ReconciliationSucceededReason, "release reconciliation succeeded"), nil
|
||||
}
|
||||
|
||||
func (r *HelmReleaseReconciler) checkDependencies(hr v2.HelmRelease) error {
|
||||
|
@ -658,6 +661,12 @@ func actionLogger(logger logr.Logger) func(format string, v ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
// calculateValuesChecksum calculates the SHA1 checksum for the given Values.
|
||||
func calculateValuesChecksum(values chartutil.Values) string {
|
||||
s, _ := values.YAML()
|
||||
return fmt.Sprintf("%x", sha1.Sum([]byte(s)))
|
||||
}
|
||||
|
||||
func containsString(slice []string, s string) bool {
|
||||
for _, item := range slice {
|
||||
if item == s {
|
||||
|
|
|
@ -806,6 +806,18 @@ string
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>lastAttemptedValuesChecksum</code><br>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>LastAttemptedValuesChecksum is the SHA1 checksum of the values of the last reconciliation attempt.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>lastReleaseRevision</code><br>
|
||||
<em>
|
||||
int
|
||||
|
|
Loading…
Reference in New Issue