Skip to content

Commit

Permalink
Merge pull request #834 from fluxcd/propagate-hc-msg
Browse files Browse the repository at this point in the history
controller: enrich "HelmChart not ready" messages
  • Loading branch information
hiddeco authored Dec 8, 2023
2 parents ee8177e + 93d2118 commit fe8569b
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 6 deletions.
32 changes: 29 additions & 3 deletions internal/controller/helmrelease_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,9 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, patchHelpe
return ctrl.Result{}, err
}

// Check chart readiness.
if hc.Generation != hc.Status.ObservedGeneration || !conditions.IsReady(hc) || hc.GetArtifact() == nil {
msg := fmt.Sprintf("HelmChart '%s/%s' is not ready", hc.GetNamespace(), hc.GetName())
// Check if the HelmChart is ready.
if ready, reason := isHelmChartReady(hc); !ready {
msg := fmt.Sprintf("HelmChart '%s/%s' is not ready: %s", hc.GetNamespace(), hc.GetName(), reason)
log.Info(msg)
conditions.MarkFalse(obj, meta.ReadyCondition, "HelmChartNotReady", msg)
// Do not requeue immediately, when the artifact is created
Expand Down Expand Up @@ -703,3 +703,29 @@ func (r *HelmReleaseReconciler) requestsForHelmChartChange(ctx context.Context,
}
return reqs
}

// isHelmChartReady returns true if the given HelmChart is ready, and a reason
// why it is not ready otherwise.
func isHelmChartReady(obj *sourcev1.HelmChart) (bool, string) {
switch {
case obj.Generation != obj.Status.ObservedGeneration:
msg := "latest generation of object has not been reconciled"

// If the chart is not ready, we can likely provide a more
// concise reason why.
// We do not do this while the Generation matches the Observed
// Generation, as we could then potentially stall on e.g.
// temporary errors which do not have an impact as long as
// there is an Artifact for the current Generation.
if conditions.IsFalse(obj, meta.ReadyCondition) {
msg = conditions.GetMessage(obj, meta.ReadyCondition)
}
return false, msg
case conditions.IsStalled(obj):
return false, conditions.GetMessage(obj, meta.StalledCondition)
case obj.Status.Artifact == nil:
return false, "does not have an artifact"
default:
return true, ""
}
}
90 changes: 87 additions & 3 deletions internal/controller/helmrelease_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
}))
})

t.Run("waits for HelmChart to be ready", func(t *testing.T) {
t.Run("waits for HelmChart to have an Artifact", func(t *testing.T) {
g := NewWithT(t)

chart := &sourcev1b2.HelmChart{
Expand All @@ -190,11 +190,11 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
},
Status: sourcev1b2.HelmChartStatus{
ObservedGeneration: 2,
Artifact: &sourcev1.Artifact{},
Artifact: nil,
Conditions: []metav1.Condition{
{
Type: meta.ReadyCondition,
Status: metav1.ConditionFalse,
Status: metav1.ConditionTrue,
},
},
},
Expand Down Expand Up @@ -2206,3 +2206,87 @@ func TestValuesReferenceValidation(t *testing.T) {
})
}
}

func Test_isHelmChartReady(t *testing.T) {
mock := &sourcev1b2.HelmChart{
ObjectMeta: metav1.ObjectMeta{
Generation: 2,
},
Status: sourcev1b2.HelmChartStatus{
ObservedGeneration: 2,
Conditions: []metav1.Condition{
{
Type: meta.ReadyCondition,
Status: metav1.ConditionTrue,
},
},
Artifact: &sourcev1.Artifact{},
},
}

tests := []struct {
name string
obj *sourcev1b2.HelmChart
want bool
wantReason string
}{
{
name: "chart is ready",
obj: mock.DeepCopy(),
want: true,
},
{
name: "chart generation differs from observed generation while Ready=True",
obj: func() *sourcev1b2.HelmChart {
m := mock.DeepCopy()
m.Generation = 3
return m
}(),
want: false,
wantReason: "latest generation of object has not been reconciled",
},
{
name: "chart generation differs from observed generation while Ready=False",
obj: func() *sourcev1b2.HelmChart {
m := mock.DeepCopy()
m.Generation = 3
conditions.MarkFalse(m, meta.ReadyCondition, "Reason", "some reason")
return m
}(),
want: false,
wantReason: "some reason",
},
{
name: "chart has Stalled=True",
obj: func() *sourcev1b2.HelmChart {
m := mock.DeepCopy()
conditions.MarkFalse(m, meta.ReadyCondition, "Reason", "some reason")
conditions.MarkStalled(m, "Reason", "some stalled reason")
return m
}(),
want: false,
wantReason: "some stalled reason",
},
{
name: "chart does not have an Artifact",
obj: func() *sourcev1b2.HelmChart {
m := mock.DeepCopy()
m.Status.Artifact = nil
return m
}(),
want: false,
wantReason: "does not have an artifact",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, gotReason := isHelmChartReady(tt.obj)
if got != tt.want {
t.Errorf("isHelmChartReady() got = %v, want %v", got, tt.want)
}
if gotReason != tt.wantReason {
t.Errorf("isHelmChartReady() reason = %v, want %v", gotReason, tt.wantReason)
}
})
}
}

0 comments on commit fe8569b

Please sign in to comment.