Skip to content

Commit 1166002

Browse files
committed
Track action in snapshots
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
1 parent c16db59 commit 1166002

23 files changed

Lines changed: 388 additions & 41 deletions

api/v2/helmrelease_types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,12 @@ const (
11951195
ReleaseActionInstall ReleaseAction = "install"
11961196
// ReleaseActionUpgrade represents a Helm upgrade action.
11971197
ReleaseActionUpgrade ReleaseAction = "upgrade"
1198+
// ReleaseActionRollback represents a Helm rollback action.
1199+
ReleaseActionRollback ReleaseAction = "rollback"
1200+
// ReleaseActionUninstall represents a Helm uninstall action.
1201+
ReleaseActionUninstall ReleaseAction = "uninstall"
1202+
// ReleaseActionUninstallRemediation represents a Helm uninstall action for remediation.
1203+
ReleaseActionUninstallRemediation ReleaseAction = "uninstall-remediation"
11981204
)
11991205

12001206
// HelmReleaseStatus defines the observed state of a HelmRelease.

api/v2/snapshot_types.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ func (in *Snapshots) TruncateIgnoringPreviousSnapshots() {
135135
// as managed by the controller.
136136
type Snapshot struct {
137137
// APIVersion is the API version of the Snapshot.
138-
// Provisional: when the calculation method of the Digest field is changed,
139-
// this field will be used to distinguish between the old and new methods.
138+
// When the calculation method of the Digest field is changed, this
139+
// field will be used to distinguish between the old and new methods.
140140
// +optional
141141
APIVersion string `json:"apiVersion,omitempty"`
142142
// Digest is the checksum of the release object in storage.
@@ -155,6 +155,9 @@ type Snapshot struct {
155155
// Status is the current state of the release.
156156
// +required
157157
Status string `json:"status"`
158+
// Action is the action that resulted in this snapshot being created.
159+
// +optional
160+
Action ReleaseAction `json:"action,omitempty"`
158161
// ChartName is the chart name of the release object in storage.
159162
// +required
160163
ChartName string `json:"chartName"`
@@ -248,6 +251,14 @@ func (in *Snapshot) Targets(name, namespace string, version int) bool {
248251
return false
249252
}
250253

254+
// GetAction returns the ReleaseAction for the Snapshot.
255+
func (in *Snapshot) GetAction() ReleaseAction {
256+
if in == nil {
257+
return ""
258+
}
259+
return in.Action
260+
}
261+
251262
// TestHookStatus holds the status information for a test hook as observed
252263
// to be run by the controller.
253264
type TestHookStatus struct {

api/v2/snapshot_types_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,57 @@ func TestSnapshots_Truncate(t *testing.T) {
297297
}
298298
}
299299

300+
func TestSnapshot_GetAction(t *testing.T) {
301+
tests := []struct {
302+
name string
303+
snapshot *Snapshot
304+
want ReleaseAction
305+
}{
306+
{
307+
name: "nil snapshot",
308+
snapshot: nil,
309+
want: "",
310+
},
311+
{
312+
name: "empty action",
313+
snapshot: &Snapshot{},
314+
want: "",
315+
},
316+
{
317+
name: "install action",
318+
snapshot: &Snapshot{Action: ReleaseActionInstall},
319+
want: ReleaseActionInstall,
320+
},
321+
{
322+
name: "upgrade action",
323+
snapshot: &Snapshot{Action: ReleaseActionUpgrade},
324+
want: ReleaseActionUpgrade,
325+
},
326+
{
327+
name: "rollback action",
328+
snapshot: &Snapshot{Action: ReleaseActionRollback},
329+
want: ReleaseActionRollback,
330+
},
331+
{
332+
name: "uninstall action",
333+
snapshot: &Snapshot{Action: ReleaseActionUninstall},
334+
want: ReleaseActionUninstall,
335+
},
336+
{
337+
name: "uninstall-remediation action",
338+
snapshot: &Snapshot{Action: ReleaseActionUninstallRemediation},
339+
want: ReleaseActionUninstallRemediation,
340+
},
341+
}
342+
for _, tt := range tests {
343+
t.Run(tt.name, func(t *testing.T) {
344+
if got := tt.snapshot.GetAction(); got != tt.want {
345+
t.Errorf("GetAction() = %v, want %v", got, tt.want)
346+
}
347+
})
348+
}
349+
}
350+
300351
func TestSnapshots_TruncateIgnoringPreviousSnapshots(t *testing.T) {
301352
tests := []struct {
302353
name string

config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,11 +1203,15 @@ spec:
12031203
Snapshot captures a point-in-time copy of the status information for a Helm release,
12041204
as managed by the controller.
12051205
properties:
1206+
action:
1207+
description: Action is the action that resulted in this snapshot
1208+
being created.
1209+
type: string
12061210
apiVersion:
12071211
description: |-
12081212
APIVersion is the API version of the Snapshot.
1209-
Provisional: when the calculation method of the Digest field is changed,
1210-
this field will be used to distinguish between the old and new methods.
1213+
When the calculation method of the Digest field is changed, this
1214+
field will be used to distinguish between the old and new methods.
12111215
type: string
12121216
appVersion:
12131217
description: AppVersion is the chart app version of the release
@@ -2550,11 +2554,15 @@ spec:
25502554
Snapshot captures a point-in-time copy of the status information for a Helm release,
25512555
as managed by the controller.
25522556
properties:
2557+
action:
2558+
description: Action is the action that resulted in this snapshot
2559+
being created.
2560+
type: string
25532561
apiVersion:
25542562
description: |-
25552563
APIVersion is the API version of the Snapshot.
2556-
Provisional: when the calculation method of the Digest field is changed,
2557-
this field will be used to distinguish between the old and new methods.
2564+
When the calculation method of the Digest field is changed, this
2565+
field will be used to distinguish between the old and new methods.
25582566
type: string
25592567
appVersion:
25602568
description: AppVersion is the chart app version of the release

docs/api/v2/helm.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2408,7 +2408,8 @@ Kustomize
24082408
(<code>string</code> alias)</h3>
24092409
<p>
24102410
(<em>Appears on:</em>
2411-
<a href="#helm.toolkit.fluxcd.io/v2.HelmReleaseStatus">HelmReleaseStatus</a>)
2411+
<a href="#helm.toolkit.fluxcd.io/v2.HelmReleaseStatus">HelmReleaseStatus</a>,
2412+
<a href="#helm.toolkit.fluxcd.io/v2.Snapshot">Snapshot</a>)
24122413
</p>
24132414
<p>ReleaseAction is the action to perform a Helm release.</p>
24142415
<h3 id="helm.toolkit.fluxcd.io/v2.Remediation">Remediation
@@ -2676,8 +2677,8 @@ string
26762677
<td>
26772678
<em>(Optional)</em>
26782679
<p>APIVersion is the API version of the Snapshot.
2679-
Provisional: when the calculation method of the Digest field is changed,
2680-
this field will be used to distinguish between the old and new methods.</p>
2680+
When the calculation method of the Digest field is changed, this
2681+
field will be used to distinguish between the old and new methods.</p>
26812682
</td>
26822683
</tr>
26832684
<tr>
@@ -2738,6 +2739,20 @@ string
27382739
</tr>
27392740
<tr>
27402741
<td>
2742+
<code>action</code><br>
2743+
<em>
2744+
<a href="#helm.toolkit.fluxcd.io/v2.ReleaseAction">
2745+
ReleaseAction
2746+
</a>
2747+
</em>
2748+
</td>
2749+
<td>
2750+
<em>(Optional)</em>
2751+
<p>Action is the action that resulted in this snapshot being created.</p>
2752+
</td>
2753+
</tr>
2754+
<tr>
2755+
<td>
27412756
<code>chartName</code><br>
27422757
<em>
27432758
string

docs/spec/v2/helmreleases.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,6 +1954,7 @@ status:
19541954
namespace: podinfo
19551955
ociDigest: sha256:0cc9a8446c95009ef382f5eade883a67c257f77d50f84e78ecef2aac9428d1e5
19561956
status: deployed
1957+
action: upgrade
19571958
testHooks:
19581959
podinfo-grpc-test-goyey:
19591960
lastCompleted: "2024-05-07T04:55:11Z"
@@ -1971,6 +1972,7 @@ status:
19711972
namespace: podinfo
19721973
ociDigest: sha256:cdd538a0167e4b51152b71a477e51eb6737553510ce8797dbcc537e1342311bb
19731974
status: superseded
1975+
action: install
19741976
testHooks:
19751977
podinfo-grpc-test-q0ucx:
19761978
lastCompleted: "2024-05-07T04:54:25Z"

internal/reconcile/install.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ func (r *Install) Reconcile(ctx context.Context, req *Request) error {
9696
req.Object.Status.LastAttemptedReleaseActionDuration = &metav1.Duration{Duration: time.Since(startTime)}
9797

9898
// Record the history of releases observed during the install.
99-
obsReleases.recordOnObject(req.Object, mutateOCIDigest)
99+
obsReleases.recordOnObject(req.Object,
100+
mutateOCIDigest,
101+
mutateAction(v2.ReleaseActionInstall))
100102

101103
if err != nil {
102104
r.failure(req, logBuf, err)

internal/reconcile/install_test.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ func TestInstall_Reconcile(t *testing.T) {
100100
},
101101
expectHistory: func(releases []*helmrelease.Release) v2.Snapshots {
102102
return v2.Snapshots{
103-
release.ObservedToSnapshot(release.ObserveRelease(releases[0])),
103+
func() *v2.Snapshot {
104+
obs := release.ObserveRelease(releases[0])
105+
obs.Action = v2.ReleaseActionInstall
106+
return release.ObservedToSnapshot(obs)
107+
}(),
104108
}
105109
},
106110
expectInventory: func(namespace string) *v2.ResourceInventory {
@@ -125,7 +129,11 @@ func TestInstall_Reconcile(t *testing.T) {
125129
},
126130
expectHistory: func(releases []*helmrelease.Release) v2.Snapshots {
127131
return v2.Snapshots{
128-
release.ObservedToSnapshot(release.ObserveRelease(releases[0])),
132+
func() *v2.Snapshot {
133+
obs := release.ObserveRelease(releases[0])
134+
obs.Action = v2.ReleaseActionInstall
135+
return release.ObservedToSnapshot(obs)
136+
}(),
129137
}
130138
},
131139
expectFailures: 1,
@@ -184,7 +192,11 @@ func TestInstall_Reconcile(t *testing.T) {
184192
},
185193
expectHistory: func(releases []*helmrelease.Release) v2.Snapshots {
186194
return v2.Snapshots{
187-
release.ObservedToSnapshot(release.ObserveRelease(releases[1])),
195+
func() *v2.Snapshot {
196+
obs := release.ObserveRelease(releases[1])
197+
obs.Action = v2.ReleaseActionInstall
198+
return release.ObservedToSnapshot(obs)
199+
}(),
188200
}
189201
},
190202
},
@@ -212,7 +224,11 @@ func TestInstall_Reconcile(t *testing.T) {
212224
},
213225
expectHistory: func(releases []*helmrelease.Release) v2.Snapshots {
214226
return v2.Snapshots{
215-
release.ObservedToSnapshot(release.ObserveRelease(releases[0])),
227+
func() *v2.Snapshot {
228+
obs := release.ObserveRelease(releases[0])
229+
obs.Action = v2.ReleaseActionInstall
230+
return release.ObservedToSnapshot(obs)
231+
}(),
216232
}
217233
},
218234
},
@@ -235,7 +251,11 @@ func TestInstall_Reconcile(t *testing.T) {
235251
},
236252
expectHistory: func(releases []*helmrelease.Release) v2.Snapshots {
237253
return v2.Snapshots{
238-
release.ObservedToSnapshot(release.ObserveRelease(releases[0])),
254+
func() *v2.Snapshot {
255+
obs := release.ObserveRelease(releases[0])
256+
obs.Action = v2.ReleaseActionInstall
257+
return release.ObservedToSnapshot(obs)
258+
}(),
239259
}
240260
},
241261
},
@@ -251,7 +271,11 @@ func TestInstall_Reconcile(t *testing.T) {
251271
},
252272
expectHistory: func(releases []*helmrelease.Release) v2.Snapshots {
253273
return v2.Snapshots{
254-
release.ObservedToSnapshot(release.ObserveRelease(releases[0])),
274+
func() *v2.Snapshot {
275+
obs := release.ObserveRelease(releases[0])
276+
obs.Action = v2.ReleaseActionInstall
277+
return release.ObservedToSnapshot(obs)
278+
}(),
255279
}
256280
},
257281
expectInventory: func(namespace string) *v2.ResourceInventory {
@@ -374,8 +398,10 @@ func TestInstall_Reconcile_withSubchartWithCRDs(t *testing.T) {
374398
}
375399

376400
expectHistory := func(releases []*helmrelease.Release) v2.Snapshots {
401+
obs := release.ObserveRelease(releases[0])
402+
obs.Action = v2.ReleaseActionInstall
377403
return v2.Snapshots{
378-
release.ObservedToSnapshot(release.ObserveRelease(releases[0])),
404+
release.ObservedToSnapshot(obs),
379405
}
380406
}
381407

internal/reconcile/release.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,19 @@ func mutateOCIDigest(obj *v2.HelmRelease, obs release.Observation) release.Obser
113113
return obs
114114
}
115115

116-
func releaseToObservation(rls *helmreleasev1.Release, snapshot *v2.Snapshot) release.Observation {
116+
func mutateAction(action v2.ReleaseAction) func(obj *v2.HelmRelease, obs release.Observation) release.Observation {
117+
return func(obj *v2.HelmRelease, obs release.Observation) release.Observation {
118+
obs.Action = action
119+
return obs
120+
}
121+
}
122+
123+
func releaseToObservation(rls *helmreleasev1.Release,
124+
snapshot *v2.Snapshot, action v2.ReleaseAction) release.Observation {
125+
117126
obs := release.ObserveRelease(rls)
118127
obs.OCIDigest = snapshot.OCIDigest
128+
obs.Action = action
119129
return obs
120130
}
121131

0 commit comments

Comments
 (0)