diff --git a/internal/reconcile/atomic_release.go b/internal/reconcile/atomic_release.go index 4326a31ca..b57f31587 100644 --- a/internal/reconcile/atomic_release.go +++ b/internal/reconcile/atomic_release.go @@ -377,6 +377,14 @@ func (r *AtomicRelease) actionForState(ctx context.Context, req *Request, state replaceCondition(req.Object, v2.RemediatedCondition, v2.ReleasedCondition, v2.UpgradeSucceededReason, msg, metav1.ConditionTrue) } + // Since the release is in-sync, replace any Status=False released condition for any previous upgrade failure with Status=True + // This can happen when the desired configuration is changed back to match the current release following an upgrade failure + if conditions.IsFalse(req.Object, v2.ReleasedCondition) && conditions.GetReason(req.Object, v2.ReleasedCondition) == v2.UpgradeFailedReason { + cur := req.Object.Status.History.Latest() + msg := fmt.Sprintf(fmtUpgradeSuccess, cur.FullReleaseName(), cur.VersionedChartName()) + conditions.MarkTrue(req.Object, v2.ReleasedCondition, v2.UpgradeSucceededReason, "%s", msg) + } + return nil, nil case ReleaseStatusLocked: log.Info(msgWithReason("release locked", state.Reason)) diff --git a/internal/reconcile/atomic_release_test.go b/internal/reconcile/atomic_release_test.go index 75eaf2a8f..6da82cec3 100644 --- a/internal/reconcile/atomic_release_test.go +++ b/internal/reconcile/atomic_release_test.go @@ -1532,6 +1532,26 @@ func TestAtomicRelease_actionForState(t *testing.T) { *conditions.TrueCondition(v2.ReleasedCondition, v2.UpgradeSucceededReason, "upgrade succeeded"), }, }, + { + name: "in-sync release with stale ready condition", + status: func(releases []*helmrelease.Release) v2.HelmReleaseStatus { + return v2.HelmReleaseStatus{ + History: v2.Snapshots{ + {Version: 1}, + }, + Conditions: []metav1.Condition{ + *conditions.FalseCondition(v2.ReleasedCondition, v2.UpgradeFailedReason, "upgrade failed"), + *conditions.FalseCondition(meta.ReadyCondition, v2.UpgradeFailedReason, "upgrade failed"), + }, + } + }, + state: ReleaseState{Status: ReleaseStatusInSync}, + want: nil, + assertConditions: []metav1.Condition{ + *conditions.TrueCondition(v2.ReleasedCondition, v2.UpgradeSucceededReason, "upgrade succeeded"), + *conditions.FalseCondition(meta.ReadyCondition, v2.UpgradeFailedReason, "upgrade failed"), + }, + }, { name: "locked release triggers unlock action", state: ReleaseState{Status: ReleaseStatusLocked},