Skip to content

Commit

Permalink
A better implementation for observing Postrenderers
Browse files Browse the repository at this point in the history
This implementation update `.status.ObervedPostRenderersDigest` after a
successful reconciliation if `spec.postRenderers` is not nil.

Changes to `spec.postRenderers` are detected for `deployed` releases.
`Failed` and `Uninstalled` releases are detected earlier in the
reconciliation process.

Signed-off-by: Soule BA <bah.soule@gmail.com>
  • Loading branch information
souleb committed May 7, 2024
1 parent e58a102 commit 4069ad4
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 78 deletions.
10 changes: 5 additions & 5 deletions api/v2/helmrelease_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,11 @@ type HelmReleaseStatus struct {
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// ObservedPostRenderersDigest is the digest for the post-renderers of
// the last successful reconciliation attempt.
// +optional
ObservedPostRenderersDigest string `json:"ObservedPostRenderersDigest,omitempty"`

// LastAttemptedGeneration is the last generation the controller attempted
// to reconcile.
// +optional
Expand Down Expand Up @@ -1008,11 +1013,6 @@ type HelmReleaseStatus struct {
// +optional
LastAttemptedConfigDigest string `json:"lastAttemptedConfigDigest,omitempty"`

// LastAttemptedPostRenderersDigest is the digest for the post-renderers of
// the last reconciliation attempt.
// +optional
LastAttemptedPostRenderersDigest string `json:"lastAttemptedPostRenderersDigest,omitempty"`

// LastHandledForceAt holds the value of the most recent force request
// value, so a change of the annotation value can be detected.
// +optional
Expand Down
10 changes: 5 additions & 5 deletions api/v2beta1/helmrelease_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,11 @@ type HelmReleaseStatus struct {
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// ObservedPostRenderersDigest is the digest for the post-renderers of
// the last successful reconciliation attempt.
// +optional
ObservedPostRenderersDigest string `json:"ObservedPostRenderersDigest,omitempty"`

meta.ReconcileRequestStatus `json:",inline"`

// Conditions holds the conditions for the HelmRelease.
Expand Down Expand Up @@ -950,11 +955,6 @@ type HelmReleaseStatus struct {
// +optional
LastAttemptedConfigDigest string `json:"lastAttemptedConfigDigest,omitempty"`

// LastAttemptedPostRenderersDigest is the digest for the post-renderers of
// the last reconciliation attempt.
// +optional
LastAttemptedPostRenderersDigest string `json:"lastAttemptedPostRenderersDigest,omitempty"`

// LastAttemptedReleaseAction is the last release action performed for this
// HelmRelease. It is used to determine the active remediation strategy.
//
Expand Down
10 changes: 5 additions & 5 deletions api/v2beta2/helmrelease_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,11 @@ type HelmReleaseStatus struct {
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// ObservedPostRenderersDigest is the digest for the post-renderers of
// the last successful reconciliation attempt.
// +optional
ObservedPostRenderersDigest string `json:"ObservedPostRenderersDigest,omitempty"`

// LastAttemptedGeneration is the last generation the controller attempted
// to reconcile.
// +optional
Expand Down Expand Up @@ -1034,11 +1039,6 @@ type HelmReleaseStatus struct {
// +optional
LastAttemptedConfigDigest string `json:"lastAttemptedConfigDigest,omitempty"`

// LastAttemptedPostRenderersDigest is the digest for the post-renderers of
// the last reconciliation attempt.
// +optional
LastAttemptedPostRenderersDigest string `json:"lastAttemptedPostRenderersDigest,omitempty"`

// LastHandledForceAt holds the value of the most recent force request
// value, so a change of the annotation value can be detected.
// +optional
Expand Down
30 changes: 15 additions & 15 deletions config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,11 @@ spec:
observedGeneration: -1
description: HelmReleaseStatus defines the observed state of a HelmRelease.
properties:
ObservedPostRenderersDigest:
description: |-
ObservedPostRenderersDigest is the digest for the post-renderers of
the last successful reconciliation attempt.
type: string
conditions:
description: Conditions holds the conditions for the HelmRelease.
items:
Expand Down Expand Up @@ -1110,11 +1115,6 @@ spec:
to reconcile.
format: int64
type: integer
lastAttemptedPostRenderersDigest:
description: |-
LastAttemptedPostRenderersDigest is the digest for the post-renderers of
the last reconciliation attempt.
type: string
lastAttemptedReleaseAction:
description: |-
LastAttemptedReleaseAction is the last release action performed for this
Expand Down Expand Up @@ -2129,6 +2129,11 @@ spec:
observedGeneration: -1
description: HelmReleaseStatus defines the observed state of a HelmRelease.
properties:
ObservedPostRenderersDigest:
description: |-
ObservedPostRenderersDigest is the digest for the post-renderers of
the last successful reconciliation attempt.
type: string
conditions:
description: Conditions holds the conditions for the HelmRelease.
items:
Expand Down Expand Up @@ -2349,11 +2354,6 @@ spec:
by v2beta1 HelmReleases.
format: int64
type: integer
lastAttemptedPostRenderersDigest:
description: |-
LastAttemptedPostRenderersDigest is the digest for the post-renderers of
the last reconciliation attempt.
type: string
lastAttemptedReleaseAction:
description: |-
LastAttemptedReleaseAction is the last release action performed for this
Expand Down Expand Up @@ -3427,6 +3427,11 @@ spec:
observedGeneration: -1
description: HelmReleaseStatus defines the observed state of a HelmRelease.
properties:
ObservedPostRenderersDigest:
description: |-
ObservedPostRenderersDigest is the digest for the post-renderers of
the last successful reconciliation attempt.
type: string
conditions:
description: Conditions holds the conditions for the HelmRelease.
items:
Expand Down Expand Up @@ -3637,11 +3642,6 @@ spec:
to reconcile.
format: int64
type: integer
lastAttemptedPostRenderersDigest:
description: |-
LastAttemptedPostRenderersDigest is the digest for the post-renderers of
the last reconciliation attempt.
type: string
lastAttemptedReleaseAction:
description: |-
LastAttemptedReleaseAction is the last release action performed for this
Expand Down
26 changes: 13 additions & 13 deletions docs/api/v2/helm.md
Original file line number Diff line number Diff line change
Expand Up @@ -1434,6 +1434,19 @@ int64
</tr>
<tr>
<td>
<code>ObservedPostRenderersDigest</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>ObservedPostRenderersDigest is the digest for the post-renderers of
the last successful reconciliation attempt.</p>
</td>
</tr>
<tr>
<td>
<code>lastAttemptedGeneration</code><br>
<em>
int64
Expand Down Expand Up @@ -1623,19 +1636,6 @@ string
</tr>
<tr>
<td>
<code>lastAttemptedPostRenderersDigest</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>LastAttemptedPostRenderersDigest is the digest for the post-renderers of
the last reconciliation attempt.</p>
</td>
</tr>
<tr>
<td>
<code>lastHandledForceAt</code><br>
<em>
string
Expand Down
19 changes: 10 additions & 9 deletions docs/spec/v2/helmreleases.md
Original file line number Diff line number Diff line change
Expand Up @@ -1666,6 +1666,16 @@ The helm-controller reports an observed generation in the HelmRelease's
`.metadata.generation` which resulted in either a [ready state](#ready-helmrelease),
or stalled due to error it can not recover from without human intervention.

### Oberved Post Renderers Digest

The helm-controller reports the digest for the [post renderers](#post-renderers)
it last rendered the Helm chart with in the for a successful Helm install or
upgrade in the `.status.ObervedPostRenderersDigest` field.

This field is used by the controller to determine if a deployed Helm release
is in sync with the HelmRelease `spec.PostRenderers` configuration and whether
it should trigger a Helm upgrade.

### Last Attempted Config Digest

The helm-controller reports the digest for the [values](#values) it last
Expand All @@ -1675,15 +1685,6 @@ attempted to perform a Helm install or upgrade with in the
The digest is used to determine if the controller should reset the
[failure counters](#failure-counters) due to a change in the values.

### Last Attempted Post Renderers Digest

The helm-controller reports the digest for the [post renderers](#post-renderers)
it last attempted to perform a Helm install or upgrade with in the
`.status.lastAttemptedPostRenderersDigest` field.

This field is used by the controller to determine if a deployed Helm release
is in sync with the HelmRelease `spec.PostRenderers` configuration and whether
it should trigger a Helm upgrade.

### Last Attempted Revision

Expand Down
24 changes: 7 additions & 17 deletions internal/controller/helmrelease_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,15 +393,6 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, patchHelpe
obj.Status.LastAttemptedRevisionDigest = ociDigest
obj.Status.LastAttemptedConfigDigest = chartutil.DigestValues(digest.Canonical, values).String()
obj.Status.LastAttemptedValuesChecksum = ""
// Keep track of the post-renderers digest used during the last reconciliation.
// This is used to determine if the post-renderers have changed.
oldPostRenderersDigest := obj.Status.LastAttemptedPostRenderersDigest
// remove stale post-renderers digest
obj.Status.LastAttemptedPostRenderersDigest = ""
if obj.Spec.PostRenderers != nil {
// Update the post-renderers digest if the post-renderers exist.
obj.Status.LastAttemptedPostRenderersDigest = postrender.Digest(digest.Canonical, obj.Spec.PostRenderers).String()
}
obj.Status.LastReleaseRevision = 0

// Construct config factory for any further Helm actions.
Expand All @@ -420,10 +411,9 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, patchHelpe

// Off we go!
if err = intreconcile.NewAtomicRelease(patchHelper, cfg, r.EventRecorder, r.FieldManager).Reconcile(ctx, &intreconcile.Request{
Object: obj,
Chart: loadedChart,
Values: values,
PreviousPostrendersDigest: oldPostRenderersDigest,
Object: obj,
Chart: loadedChart,
Values: values,
}); err != nil {
if errors.Is(err, intreconcile.ErrMustRequeue) {
return ctrl.Result{Requeue: true}, nil
Expand Down Expand Up @@ -658,17 +648,17 @@ func (r *HelmReleaseReconciler) adoptLegacyRelease(ctx context.Context, getter g
return nil
}

// adoptPostRenderersStatus attempts to set obj.Status.LastAttemptedPostRenderersDigest
// for v1beta1 and v1beta2 HelmReleases.
// adoptPostRenderersStatus attempts to set obj.Status.ObservedPostRenderersDigest
// for v2beta1 and v2beta2 HelmReleases.
func (*HelmReleaseReconciler) adoptPostRenderersStatus(obj *v2.HelmRelease) {
if obj.GetGeneration() != obj.Status.ObservedGeneration {
return
}

// if we have a reconciled object with PostRenderers not reflected in the
// status, we need to update the status.
if obj.Spec.PostRenderers != nil && obj.Status.LastAttemptedPostRenderersDigest == "" {
obj.Status.LastAttemptedPostRenderersDigest = postrender.Digest(digest.Canonical, obj.Spec.PostRenderers).String()
if obj.Spec.PostRenderers != nil && obj.Status.ObservedPostRenderersDigest == "" {
obj.Status.ObservedPostRenderersDigest = postrender.Digest(digest.Canonical, obj.Spec.PostRenderers).String()
}
}

Expand Down
7 changes: 2 additions & 5 deletions internal/controller/helmrelease_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromHelmChartSource(t *testing.T)
},
}

obj.Status.LastAttemptedPostRenderersDigest = postrender.Digest(digest.Canonical, obj.Spec.PostRenderers).String()
obj.Status.ObservedPostRenderersDigest = postrender.Digest(digest.Canonical, obj.Spec.PostRenderers).String()
obj.Status.LastAttemptedConfigDigest = chartutil.DigestValues(digest.Canonical, chartMock.Values).String()

c := fake.NewClientBuilder().
Expand Down Expand Up @@ -1304,13 +1304,10 @@ func TestHelmReleaseReconciler_reconcileReleaseFromHelmChartSource(t *testing.T)

// Verify attempted values are set.
g.Expect(obj.Status.LastAttemptedGeneration).To(Equal(obj.Generation))
g.Expect(obj.Status.LastAttemptedPostRenderersDigest).To(Equal(postrender.Digest(digest.Canonical, obj.Spec.PostRenderers).String()))
g.Expect(obj.Status.ObservedPostRenderersDigest).To(Equal(postrender.Digest(digest.Canonical, obj.Spec.PostRenderers).String()))

// verify upgrade succeeded
g.Expect(obj.Status.Conditions).To(conditions.MatchConditions([]metav1.Condition{
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingWithRetryReason, "Helm upgrade succeeded for release %s with chart %s",
fmt.Sprintf("%s/%s.v%d", rls.Namespace, rls.Name, rls.Version+1), fmt.Sprintf("%s@%s", chartMock.Name(),
chartMock.Metadata.Version)),
*conditions.TrueCondition(meta.ReadyCondition, v2.UpgradeSucceededReason, "Helm upgrade succeeded for release %s with chart %s",
fmt.Sprintf("%s/%s.v%d", rls.Namespace, rls.Name, rls.Version+1), fmt.Sprintf("%s@%s", chartMock.Name(),
chartMock.Metadata.Version)),
Expand Down
3 changes: 0 additions & 3 deletions internal/reconcile/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,6 @@ type Request struct {
// Values is the Helm chart values to be used for the installation or
// upgrade.
Values helmchartutil.Values
// PreviousPostrendersDigest is the digest of the post-renderers that were used
// during the last reconciliation.
PreviousPostrendersDigest string
}

// ActionReconciler is an interface which defines the methods that a reconciler
Expand Down
11 changes: 11 additions & 0 deletions internal/reconcile/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (

v2 "github.com/fluxcd/helm-controller/api/v2"
"github.com/fluxcd/helm-controller/internal/action"
"github.com/fluxcd/helm-controller/internal/digest"
"github.com/fluxcd/helm-controller/internal/postrender"
"github.com/fluxcd/helm-controller/internal/release"
"github.com/fluxcd/helm-controller/internal/storage"
)
Expand Down Expand Up @@ -137,6 +139,8 @@ func observeRelease(observed observedReleases) storage.ObserveFunc {
// tests are not enabled, and excluding it when failures must be ignored.
//
// If Ready=True, any Stalled condition is removed.
//
// The ObservedPostRenderersDigest is updated if the post-renderers exist.
func summarize(req *Request) {
var sumConds = []string{v2.RemediatedCondition, v2.ReleasedCondition}
if req.Object.GetTest().Enable && !req.Object.GetTest().IgnoreFailures {
Expand Down Expand Up @@ -186,6 +190,13 @@ func summarize(req *Request) {
Message: conds[0].Message,
ObservedGeneration: req.Object.Generation,
})

// remove stale post-renderers digest
req.Object.Status.ObservedPostRenderersDigest = ""
if req.Object.Spec.PostRenderers != nil {
// Update the post-renderers digest if the post-renderers exist.
req.Object.Status.ObservedPostRenderersDigest = postrender.Digest(digest.Canonical, req.Object.Spec.PostRenderers).String()
}
}

// eventMessageWithLog returns an event message composed out of the given
Expand Down
8 changes: 7 additions & 1 deletion internal/reconcile/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import (
ctrl "sigs.k8s.io/controller-runtime"

"github.com/fluxcd/helm-controller/internal/action"
"github.com/fluxcd/helm-controller/internal/digest"
interrors "github.com/fluxcd/helm-controller/internal/errors"
"github.com/fluxcd/helm-controller/internal/postrender"
)

// ReleaseStatus represents the status of a Helm release as determined by
Expand Down Expand Up @@ -142,7 +144,11 @@ func DetermineReleaseState(ctx context.Context, cfg *action.ConfigFactory, req *
}

// Verify if postrender digest has changed
if req.PreviousPostrendersDigest != req.Object.Status.LastAttemptedPostRenderersDigest {
var postrenderersDigest string
if req.Object.Spec.PostRenderers != nil {
postrenderersDigest = postrender.Digest(digest.Canonical, req.Object.Spec.PostRenderers).String()
}
if postrenderersDigest != req.Object.Status.ObservedPostRenderersDigest {
return ReleaseState{Status: ReleaseStatusOutOfSync, Reason: "postrender digest has changed"}, nil
}

Expand Down

0 comments on commit 4069ad4

Please sign in to comment.