Skip to content

Commit 93d2118

Browse files
committed
controller: enrich "HelmChart not ready" messages
This propagates the reason a HelmChart is (likely) not ready to the message of the Ready condition. The goal of this is to make it easier for people to reason about a potential failure that may be happening while retrieving the chart, without having to inspect the HelmChart itself. As at times, they may not have access (due to e.g. not being able to access the namespace, while the controller is allowed to create the object there), or are simply not aware of the fact that this object is created by the controller for them. Signed-off-by: Hidde Beydals <hidde@hhh.computer>
1 parent ee8177e commit 93d2118

File tree

2 files changed

+116
-6
lines changed

2 files changed

+116
-6
lines changed

internal/controller/helmrelease_controller.go

+29-3
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,9 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, patchHelpe
276276
return ctrl.Result{}, err
277277
}
278278

279-
// Check chart readiness.
280-
if hc.Generation != hc.Status.ObservedGeneration || !conditions.IsReady(hc) || hc.GetArtifact() == nil {
281-
msg := fmt.Sprintf("HelmChart '%s/%s' is not ready", hc.GetNamespace(), hc.GetName())
279+
// Check if the HelmChart is ready.
280+
if ready, reason := isHelmChartReady(hc); !ready {
281+
msg := fmt.Sprintf("HelmChart '%s/%s' is not ready: %s", hc.GetNamespace(), hc.GetName(), reason)
282282
log.Info(msg)
283283
conditions.MarkFalse(obj, meta.ReadyCondition, "HelmChartNotReady", msg)
284284
// Do not requeue immediately, when the artifact is created
@@ -703,3 +703,29 @@ func (r *HelmReleaseReconciler) requestsForHelmChartChange(ctx context.Context,
703703
}
704704
return reqs
705705
}
706+
707+
// isHelmChartReady returns true if the given HelmChart is ready, and a reason
708+
// why it is not ready otherwise.
709+
func isHelmChartReady(obj *sourcev1.HelmChart) (bool, string) {
710+
switch {
711+
case obj.Generation != obj.Status.ObservedGeneration:
712+
msg := "latest generation of object has not been reconciled"
713+
714+
// If the chart is not ready, we can likely provide a more
715+
// concise reason why.
716+
// We do not do this while the Generation matches the Observed
717+
// Generation, as we could then potentially stall on e.g.
718+
// temporary errors which do not have an impact as long as
719+
// there is an Artifact for the current Generation.
720+
if conditions.IsFalse(obj, meta.ReadyCondition) {
721+
msg = conditions.GetMessage(obj, meta.ReadyCondition)
722+
}
723+
return false, msg
724+
case conditions.IsStalled(obj):
725+
return false, conditions.GetMessage(obj, meta.StalledCondition)
726+
case obj.Status.Artifact == nil:
727+
return false, "does not have an artifact"
728+
default:
729+
return true, ""
730+
}
731+
}

internal/controller/helmrelease_controller_test.go

+87-3
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
179179
}))
180180
})
181181

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

185185
chart := &sourcev1b2.HelmChart{
@@ -190,11 +190,11 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
190190
},
191191
Status: sourcev1b2.HelmChartStatus{
192192
ObservedGeneration: 2,
193-
Artifact: &sourcev1.Artifact{},
193+
Artifact: nil,
194194
Conditions: []metav1.Condition{
195195
{
196196
Type: meta.ReadyCondition,
197-
Status: metav1.ConditionFalse,
197+
Status: metav1.ConditionTrue,
198198
},
199199
},
200200
},
@@ -2206,3 +2206,87 @@ func TestValuesReferenceValidation(t *testing.T) {
22062206
})
22072207
}
22082208
}
2209+
2210+
func Test_isHelmChartReady(t *testing.T) {
2211+
mock := &sourcev1b2.HelmChart{
2212+
ObjectMeta: metav1.ObjectMeta{
2213+
Generation: 2,
2214+
},
2215+
Status: sourcev1b2.HelmChartStatus{
2216+
ObservedGeneration: 2,
2217+
Conditions: []metav1.Condition{
2218+
{
2219+
Type: meta.ReadyCondition,
2220+
Status: metav1.ConditionTrue,
2221+
},
2222+
},
2223+
Artifact: &sourcev1.Artifact{},
2224+
},
2225+
}
2226+
2227+
tests := []struct {
2228+
name string
2229+
obj *sourcev1b2.HelmChart
2230+
want bool
2231+
wantReason string
2232+
}{
2233+
{
2234+
name: "chart is ready",
2235+
obj: mock.DeepCopy(),
2236+
want: true,
2237+
},
2238+
{
2239+
name: "chart generation differs from observed generation while Ready=True",
2240+
obj: func() *sourcev1b2.HelmChart {
2241+
m := mock.DeepCopy()
2242+
m.Generation = 3
2243+
return m
2244+
}(),
2245+
want: false,
2246+
wantReason: "latest generation of object has not been reconciled",
2247+
},
2248+
{
2249+
name: "chart generation differs from observed generation while Ready=False",
2250+
obj: func() *sourcev1b2.HelmChart {
2251+
m := mock.DeepCopy()
2252+
m.Generation = 3
2253+
conditions.MarkFalse(m, meta.ReadyCondition, "Reason", "some reason")
2254+
return m
2255+
}(),
2256+
want: false,
2257+
wantReason: "some reason",
2258+
},
2259+
{
2260+
name: "chart has Stalled=True",
2261+
obj: func() *sourcev1b2.HelmChart {
2262+
m := mock.DeepCopy()
2263+
conditions.MarkFalse(m, meta.ReadyCondition, "Reason", "some reason")
2264+
conditions.MarkStalled(m, "Reason", "some stalled reason")
2265+
return m
2266+
}(),
2267+
want: false,
2268+
wantReason: "some stalled reason",
2269+
},
2270+
{
2271+
name: "chart does not have an Artifact",
2272+
obj: func() *sourcev1b2.HelmChart {
2273+
m := mock.DeepCopy()
2274+
m.Status.Artifact = nil
2275+
return m
2276+
}(),
2277+
want: false,
2278+
wantReason: "does not have an artifact",
2279+
},
2280+
}
2281+
for _, tt := range tests {
2282+
t.Run(tt.name, func(t *testing.T) {
2283+
got, gotReason := isHelmChartReady(tt.obj)
2284+
if got != tt.want {
2285+
t.Errorf("isHelmChartReady() got = %v, want %v", got, tt.want)
2286+
}
2287+
if gotReason != tt.wantReason {
2288+
t.Errorf("isHelmChartReady() reason = %v, want %v", gotReason, tt.wantReason)
2289+
}
2290+
})
2291+
}
2292+
}

0 commit comments

Comments
 (0)