Skip to content

Commit eaf9fe3

Browse files
authored
Merge pull request #1360 from fluxcd/fix-1359
Fix history truncation logic for RetryOnFailure
2 parents 80bfa2e + 831c8fc commit eaf9fe3

File tree

3 files changed

+83
-8
lines changed

3 files changed

+83
-8
lines changed

api/v2/snapshot_types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,15 @@ func (in *Snapshots) Truncate(ignoreTests bool) {
107107
}
108108
}
109109

110+
// TruncateIgnoringPreviousSnapshots sorts the Snapshots by version
111+
// in descending order and retains only the most recent 5 Snapshots.
112+
func (in *Snapshots) TruncateIgnoringPreviousSnapshots() {
113+
in.SortByVersion()
114+
if in.Len() > defaultMaxHistory {
115+
*in = (*in)[:defaultMaxHistory]
116+
}
117+
}
118+
110119
// Snapshot captures a point-in-time copy of the status information for a Helm release,
111120
// as managed by the controller.
112121
type Snapshot struct {

api/v2/snapshot_types_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,63 @@ func TestSnapshots_Truncate(t *testing.T) {
296296
})
297297
}
298298
}
299+
300+
func TestSnapshots_TruncateIgnoringPreviousSnapshots(t *testing.T) {
301+
tests := []struct {
302+
name string
303+
in Snapshots
304+
want Snapshots
305+
}{
306+
{
307+
name: "keeps previous snapshot",
308+
in: Snapshots{
309+
{Version: 1, Status: "superseded"},
310+
{Version: 3, Status: "failed"},
311+
{Version: 2, Status: "superseded"},
312+
{Version: 4, Status: "deployed"},
313+
},
314+
want: Snapshots{
315+
{Version: 4, Status: "deployed"},
316+
{Version: 3, Status: "failed"},
317+
{Version: 2, Status: "superseded"},
318+
{Version: 1, Status: "superseded"},
319+
},
320+
},
321+
{
322+
name: "retains most recent snapshots when all have failed",
323+
in: Snapshots{
324+
{Version: 6, Status: "deployed"},
325+
{Version: 5, Status: "failed"},
326+
{Version: 4, Status: "failed"},
327+
{Version: 3, Status: "failed"},
328+
{Version: 2, Status: "failed"},
329+
{Version: 1, Status: "failed"},
330+
},
331+
want: Snapshots{
332+
{Version: 6, Status: "deployed"},
333+
{Version: 5, Status: "failed"},
334+
{Version: 4, Status: "failed"},
335+
{Version: 3, Status: "failed"},
336+
{Version: 2, Status: "failed"},
337+
},
338+
},
339+
{
340+
name: "without previous snapshot",
341+
in: Snapshots{
342+
{Version: 1, Status: "deployed"},
343+
},
344+
want: Snapshots{
345+
{Version: 1, Status: "deployed"},
346+
},
347+
},
348+
}
349+
for _, tt := range tests {
350+
t.Run(tt.name, func(t *testing.T) {
351+
tt.in.TruncateIgnoringPreviousSnapshots()
352+
353+
if !reflect.DeepEqual(tt.in, tt.want) {
354+
t.Errorf("TruncateIgnoringPreviousSnapshots() got %v, want %v", tt.in, tt.want)
355+
}
356+
})
357+
}
358+
}

internal/reconcile/atomic_release.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -344,15 +344,19 @@ func (r *AtomicRelease) actionForState(ctx context.Context, req *Request, state
344344
case ReleaseStatusInSync:
345345
log.Info("release in-sync with desired state")
346346

347-
// Remove all history up to the previous release action.
348-
// We need to continue to hold on to the previous release result
349-
// to ensure we can e.g. roll back when tests are enabled without
350-
// any further changes to the release.
351-
ignoreFailures := req.Object.GetTest().IgnoreFailures
352-
if remediation := req.Object.GetActiveRemediation(); remediation != nil {
353-
ignoreFailures = remediation.MustIgnoreTestFailures(req.Object.GetTest().IgnoreFailures)
347+
if retry := req.Object.GetActiveRetry(); retry != nil {
348+
req.Object.Status.History.TruncateIgnoringPreviousSnapshots()
349+
} else {
350+
// Remove all history up to the previous release action.
351+
// We need to continue to hold on to the previous release result
352+
// to ensure we can e.g. roll back when tests are enabled without
353+
// any further changes to the release.
354+
ignoreFailures := req.Object.GetTest().IgnoreFailures
355+
if remediation := req.Object.GetActiveRemediation(); remediation != nil {
356+
ignoreFailures = remediation.MustIgnoreTestFailures(req.Object.GetTest().IgnoreFailures)
357+
}
358+
req.Object.Status.History.Truncate(ignoreFailures)
354359
}
355-
req.Object.Status.History.Truncate(ignoreFailures)
356360

357361
if forceRequested {
358362
log.Info(msgWithReason("forcing upgrade for in-sync release", "force requested through annotation"))
@@ -460,6 +464,8 @@ func (r *AtomicRelease) actionForState(ctx context.Context, req *Request, state
460464
// If the action strategy is to retry (and not remediate), we behave just like
461465
// "flux reconcile hr --force" and .spec.<action>.remediation.retries set to 0.
462466
if req.Object.GetActiveRetry() != nil {
467+
req.Object.Status.History.TruncateIgnoringPreviousSnapshots()
468+
463469
log.V(logger.DebugLevel).Info("retrying upgrade for failed release")
464470
return NewUpgrade(r.configFactory, r.eventRecorder), nil
465471
}

0 commit comments

Comments
 (0)