Skip to content

Commit 3b6abe1

Browse files
authored
Merge pull request #1412 from fluxcd/fix-postrenderers-digest
Fix postRenderers not causing new upgrade when applied during ongoing upgrade
2 parents a5e62ed + 93299f1 commit 3b6abe1

5 files changed

Lines changed: 34 additions & 44 deletions

File tree

internal/reconcile/atomic_release.go

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ import (
3838
v2 "github.com/fluxcd/helm-controller/api/v2"
3939
"github.com/fluxcd/helm-controller/internal/action"
4040
"github.com/fluxcd/helm-controller/internal/diff"
41-
"github.com/fluxcd/helm-controller/internal/digest"
4241
interrors "github.com/fluxcd/helm-controller/internal/errors"
43-
"github.com/fluxcd/helm-controller/internal/postrender"
4442
)
4543

4644
// OwnedConditions is a list of Condition types owned by the HelmRelease object.
@@ -219,23 +217,10 @@ func (r *AtomicRelease) Reconcile(ctx context.Context, req *Request) error {
219217
conditions.Delete(req.Object, meta.ReconcilingCondition)
220218

221219
// Always summarize; this ensures we restore transient errors
222-
// written to Ready.
220+
// written to Ready, and updates the observed post-renderers
221+
// and common-metadata digests.
223222
summarize(req)
224223

225-
// remove stale post-renderers digest on successful reconciliation.
226-
if conditions.IsReady(req.Object) {
227-
req.Object.Status.ObservedPostRenderersDigest = ""
228-
if req.Object.Spec.PostRenderers != nil {
229-
// Update the post-renderers digest if the post-renderers exist.
230-
req.Object.Status.ObservedPostRenderersDigest = postrender.Digest(digest.Canonical, req.Object.Spec.PostRenderers).String()
231-
}
232-
req.Object.Status.ObservedCommonMetadataDigest = ""
233-
if req.Object.Spec.CommonMetadata != nil {
234-
// Update the common-metadata digest if common-metadata exist.
235-
req.Object.Status.ObservedCommonMetadataDigest = postrender.CommonMetadataDigest(digest.Canonical, req.Object.Spec.CommonMetadata).String()
236-
}
237-
}
238-
239224
return nil
240225
}
241226

internal/reconcile/atomic_release_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,14 +1376,15 @@ func TestAtomicRelease_Reconcile_PostRenderers_Scenarios(t *testing.T) {
13761376
},
13771377
status: func(releases []*helmrelease.Release) v2.HelmReleaseStatus {
13781378
return v2.HelmReleaseStatus{
1379+
ObservedGeneration: 2, // Matches obj.Generation to skip the digest check.
13791380
History: v2.Snapshots{
13801381
release.ObservedToSnapshot(release.ObserveRelease(releases[0])),
13811382
},
13821383
Conditions: []metav1.Condition{
13831384
{
13841385
Type: meta.ReadyCondition,
13851386
Status: metav1.ConditionTrue,
1386-
ObservedGeneration: 2, // This is used to set processed config generation.
1387+
ObservedGeneration: 2,
13871388
},
13881389
},
13891390
ObservedPostRenderersDigest: postrender.Digest(digest.Canonical, postRenderers).String(),
@@ -2357,14 +2358,15 @@ func TestAtomicRelease_Reconcile_CommonMetadata_Scenarios(t *testing.T) {
23572358
},
23582359
status: func(releases []*helmrelease.Release) v2.HelmReleaseStatus {
23592360
return v2.HelmReleaseStatus{
2361+
ObservedGeneration: 2, // Matches obj.Generation to skip the digest check.
23602362
History: v2.Snapshots{
23612363
release.ObservedToSnapshot(release.ObserveRelease(releases[0])),
23622364
},
23632365
Conditions: []metav1.Condition{
23642366
{
23652367
Type: meta.ReadyCondition,
23662368
Status: metav1.ConditionTrue,
2367-
ObservedGeneration: 2, // This is used to set processed config generation.
2369+
ObservedGeneration: 2,
23682370
},
23692371
},
23702372
ObservedPostRenderersDigest: postrender.CommonMetadataDigest(digest.Canonical, commonMetadata).String(),

internal/reconcile/release.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ import (
3636

3737
v2 "github.com/fluxcd/helm-controller/api/v2"
3838
"github.com/fluxcd/helm-controller/internal/action"
39+
"github.com/fluxcd/helm-controller/internal/digest"
3940
"github.com/fluxcd/helm-controller/internal/inventory"
41+
"github.com/fluxcd/helm-controller/internal/postrender"
4042
"github.com/fluxcd/helm-controller/internal/release"
4143
"github.com/fluxcd/helm-controller/internal/storage"
4244
)
@@ -206,7 +208,8 @@ func observeInventory(obj *v2.HelmRelease, chart *helmchart.Chart, getter generi
206208
//
207209
// If Ready=True, any Stalled condition is removed.
208210
//
209-
// The ObservedPostRenderersDigest is updated if the post-renderers exist.
211+
// The ObservedPostRenderersDigest and ObservedCommonMetadataDigest are
212+
// updated to reflect the current spec.
210213
func summarize(req *Request) {
211214
var sumConds = []string{v2.RemediatedCondition, v2.ReleasedCondition}
212215
if req.Object.GetTest().Enable && !req.Object.GetTest().IgnoreFailures {
@@ -256,6 +259,17 @@ func summarize(req *Request) {
256259
Message: conds[0].Message,
257260
ObservedGeneration: req.Object.Generation,
258261
})
262+
263+
// Update the observed post-renderers and common-metadata digests to
264+
// reflect the current spec.
265+
req.Object.Status.ObservedPostRenderersDigest = ""
266+
if req.Object.Spec.PostRenderers != nil {
267+
req.Object.Status.ObservedPostRenderersDigest = postrender.Digest(digest.Canonical, req.Object.Spec.PostRenderers).String()
268+
}
269+
req.Object.Status.ObservedCommonMetadataDigest = ""
270+
if req.Object.Spec.CommonMetadata != nil {
271+
req.Object.Status.ObservedCommonMetadataDigest = postrender.CommonMetadataDigest(digest.Canonical, req.Object.Spec.CommonMetadata).String()
272+
}
259273
}
260274

261275
// eventMessageWithLog returns an event message composed out of the given

internal/reconcile/state.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ import (
2121
"errors"
2222
"fmt"
2323

24-
"github.com/fluxcd/pkg/apis/meta"
25-
"github.com/fluxcd/pkg/runtime/conditions"
2624
"github.com/fluxcd/pkg/ssa/jsondiff"
2725
"helm.sh/helm/v4/pkg/kube"
2826
helmreleasecommon "helm.sh/helm/v4/pkg/release/common"
@@ -147,14 +145,17 @@ func DetermineReleaseState(ctx context.Context, cfg *action.ConfigFactory, req *
147145
}
148146

149147
// Verify if postrender digest or common metadata digest has changed
150-
// if config has not been processed. For the processed or partially processed generation,
151-
// the updated observation will only be reflected at the end of a successful
152-
// reconciliation. Comparing here would result the reconciliation to
153-
// get stuck in this check due to a mismatch forever. The value can't
154-
// change without a new generation. Hence, compare the observed digest
155-
// for new generations only.
156-
ready := conditions.Get(req.Object, meta.ReadyCondition)
157-
if ready != nil && ready.ObservedGeneration != req.Object.Generation {
148+
// for new generations only. The observed digests are updated after
149+
// each successful release action, so comparing here will not cause
150+
// an infinite loop within the same reconciliation.
151+
//
152+
// We use the top-level status.observedGeneration rather than the
153+
// Ready condition's ObservedGeneration, because the latter can be
154+
// inadvertently advanced by the patch helper's conflict resolution
155+
// (patchStatusConditions re-fetches the object from the API server,
156+
// and conditions.Set always sets ObservedGeneration to the latest
157+
// metadata.generation).
158+
if req.Object.Status.ObservedGeneration != req.Object.Generation {
158159
var postrenderersDigest string
159160
if req.Object.Spec.PostRenderers != nil {
160161
postrenderersDigest = postrender.Digest(digest.Canonical, req.Object.Spec.PostRenderers).String()

internal/reconcile/state_test.go

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -508,17 +508,11 @@ func Test_DetermineReleaseState(t *testing.T) {
508508
},
509509
status: func(releases []*helmrelease.Release) v2.HelmReleaseStatus {
510510
return v2.HelmReleaseStatus{
511+
ObservedGeneration: 2,
511512
History: v2.Snapshots{
512513
release.ObservedToSnapshot(release.ObserveRelease(releases[0])),
513514
},
514515
ObservedPostRenderersDigest: postrender.Digest(digest.Canonical, postRenderers).String(),
515-
Conditions: []metav1.Condition{
516-
{
517-
Type: meta.ReadyCondition,
518-
Status: metav1.ConditionTrue,
519-
ObservedGeneration: 2,
520-
},
521-
},
522516
}
523517
},
524518
chart: testutil.BuildChart(),
@@ -578,17 +572,11 @@ func Test_DetermineReleaseState(t *testing.T) {
578572
},
579573
status: func(releases []*helmrelease.Release) v2.HelmReleaseStatus {
580574
return v2.HelmReleaseStatus{
575+
ObservedGeneration: 2,
581576
History: v2.Snapshots{
582577
release.ObservedToSnapshot(release.ObserveRelease(releases[0])),
583578
},
584579
ObservedCommonMetadataDigest: postrender.CommonMetadataDigest(digest.Canonical, commonMetadata).String(),
585-
Conditions: []metav1.Condition{
586-
{
587-
Type: meta.ReadyCondition,
588-
Status: metav1.ConditionTrue,
589-
ObservedGeneration: 2,
590-
},
591-
},
592580
}
593581
},
594582
chart: testutil.BuildChart(),

0 commit comments

Comments
 (0)