Skip to content

Commit ebbc998

Browse files
authored
Merge pull request #907 from fluxcd/summarize-with-bipolarity
Consider bipolarity conditions in Ready condition summarization
2 parents c9a5a56 + 90b7cec commit ebbc998

File tree

4 files changed

+131
-10
lines changed

4 files changed

+131
-10
lines changed

controllers/gitrepository_controller.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques
194194
summarizeHelper := summarize.NewHelper(r.EventRecorder, patchHelper)
195195
summarizeOpts := []summarize.Option{
196196
summarize.WithConditions(gitRepositoryReadyCondition),
197+
summarize.WithBiPolarityConditionTypes(sourcev1.SourceVerifiedCondition),
197198
summarize.WithReconcileResult(recResult),
198199
summarize.WithReconcileError(retErr),
199200
summarize.WithIgnoreNotFound(),
@@ -430,6 +431,15 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
430431
errors.New("libgit2 managed transport not initialized"), "Libgit2TransportNotEnabled",
431432
)
432433
}
434+
435+
// Remove previously failed source verification status conditions. The
436+
// failing verification should be recalculated. But an existing successful
437+
// verification need not be removed as it indicates verification of previous
438+
// version.
439+
if conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) {
440+
conditions.Delete(obj, sourcev1.SourceVerifiedCondition)
441+
}
442+
433443
// Configure authentication strategy to access the source
434444
var authOpts *git.AuthOptions
435445
var err error

controllers/ocirepository_controller.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ func (r *OCIRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques
194194
summarizeHelper := summarize.NewHelper(r.EventRecorder, patchHelper)
195195
summarizeOpts := []summarize.Option{
196196
summarize.WithConditions(ociRepositoryReadyCondition),
197+
summarize.WithBiPolarityConditionTypes(sourcev1.SourceVerifiedCondition),
197198
summarize.WithReconcileResult(recResult),
198199
summarize.WithReconcileError(retErr),
199200
summarize.WithIgnoreNotFound(),
@@ -297,6 +298,14 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
297298
ctxTimeout, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration)
298299
defer cancel()
299300

301+
// Remove previously failed source verification status conditions. The
302+
// failing verification should be recalculated. But an existing successful
303+
// verification need not be removed as it indicates verification of previous
304+
// version.
305+
if conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) {
306+
conditions.Delete(obj, sourcev1.SourceVerifiedCondition)
307+
}
308+
300309
options := r.craneOptions(ctxTimeout, obj.Spec.Insecure)
301310

302311
// Generate the registry credential keychain either from static credentials or using cloud OIDC
@@ -710,7 +719,7 @@ func (r *OCIRepositoryReconciler) keychain(ctx context.Context, obj *sourcev1.OC
710719
imagePullSecret := corev1.Secret{}
711720
err := r.Get(ctx, types.NamespacedName{Namespace: obj.Namespace, Name: imagePullSecretName}, &imagePullSecret)
712721
if err != nil {
713-
r.eventLogf(ctx, obj, events.EventSeverityTrace, sourcev1.AuthenticationFailedReason,
722+
r.eventLogf(ctx, obj, events.EventTypeTrace, sourcev1.AuthenticationFailedReason,
714723
"auth secret '%s' not found", imagePullSecretName)
715724
return nil, err
716725
}

internal/reconcile/summarize/summary.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ type HelperOptions struct {
9090
// PatchFieldOwner defines the field owner configuration for the Kubernetes
9191
// patch operation.
9292
PatchFieldOwner string
93+
// BiPolarityConditionTypes is a list of bipolar conditions in the order
94+
// of priority.
95+
BiPolarityConditionTypes []string
9396
}
9497

9598
// Option is configuration that modifies SummarizeAndPatch.
@@ -149,6 +152,14 @@ func WithPatchFieldOwner(fieldOwner string) Option {
149152
}
150153
}
151154

155+
// WithBiPolarityConditionTypes sets the BiPolarityConditionTypes used to
156+
// calculate the value of Ready condition in SummarizeAndPatch.
157+
func WithBiPolarityConditionTypes(types ...string) Option {
158+
return func(s *HelperOptions) {
159+
s.BiPolarityConditionTypes = types
160+
}
161+
}
162+
152163
// SummarizeAndPatch summarizes and patches the result to the target object.
153164
// When used at the very end of a reconciliation, the result builder must be
154165
// specified using the Option WithResultBuilder(). The returned result and error
@@ -206,6 +217,26 @@ func (h *Helper) SummarizeAndPatch(ctx context.Context, obj conditions.Setter, o
206217
)
207218
}
208219

220+
// Check any BiPolarity conditions in the status that are False. Failing
221+
// BiPolarity condition should be set as the Ready condition value to
222+
// reflect the actual cause of the reconciliation failure.
223+
// NOTE: This is applicable to Ready condition only because it is a special
224+
// condition in kstatus that reflects the overall state of an object.
225+
// IMPLEMENTATION NOTE: An implementation of this within the
226+
// conditions.merge() exists in fluxcd/pkg repo branch `bipolarity`
227+
// (https://github.com/fluxcd/pkg/commit/756b9e6d253a4fae93c05419b7019d0169454858).
228+
// If that gets added to conditions.merge, the following can be removed.
229+
var failedBiPolarity []string
230+
for _, c := range opts.BiPolarityConditionTypes {
231+
if conditions.IsFalse(obj, c) {
232+
failedBiPolarity = append(failedBiPolarity, c)
233+
}
234+
}
235+
if len(failedBiPolarity) > 0 {
236+
topFailedBiPolarity := conditions.Get(obj, failedBiPolarity[0])
237+
conditions.MarkFalse(obj, meta.ReadyCondition, topFailedBiPolarity.Reason, topFailedBiPolarity.Message)
238+
}
239+
209240
// If object is not stalled, result is success and runtime error is nil,
210241
// ensure that Ready=True. Else, use the Ready failure message as the
211242
// runtime error message. This ensures that the reconciliation would be

internal/reconcile/summarize/summary_test.go

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,26 @@ import (
4444
// This tests the scenario where SummarizeAndPatch is used at the very end of a
4545
// reconciliation.
4646
func TestSummarizeAndPatch(t *testing.T) {
47+
testBipolarCondition1 := "FooChecked1"
48+
testBipolarCondition2 := "FooChecked2"
4749
var testReadyConditions = Conditions{
4850
Target: meta.ReadyCondition,
4951
Owned: []string{
5052
sourcev1.FetchFailedCondition,
5153
sourcev1.ArtifactOutdatedCondition,
54+
sourcev1.SourceVerifiedCondition,
55+
testBipolarCondition1,
56+
testBipolarCondition2,
5257
meta.ReadyCondition,
5358
meta.ReconcilingCondition,
5459
meta.StalledCondition,
5560
},
5661
Summarize: []string{
5762
sourcev1.FetchFailedCondition,
5863
sourcev1.ArtifactOutdatedCondition,
64+
sourcev1.SourceVerifiedCondition,
65+
testBipolarCondition1,
66+
testBipolarCondition2,
5967
meta.StalledCondition,
6068
meta.ReconcilingCondition,
6169
},
@@ -66,6 +74,7 @@ func TestSummarizeAndPatch(t *testing.T) {
6674
meta.ReconcilingCondition,
6775
},
6876
}
77+
var testBipolarConditions = []string{sourcev1.SourceVerifiedCondition, testBipolarCondition1, testBipolarCondition2}
6978
var testFooConditions = Conditions{
7079
Target: "Foo",
7180
Owned: []string{
@@ -83,15 +92,16 @@ func TestSummarizeAndPatch(t *testing.T) {
8392
}
8493

8594
tests := []struct {
86-
name string
87-
generation int64
88-
beforeFunc func(obj conditions.Setter)
89-
result reconcile.Result
90-
reconcileErr error
91-
conditions []Conditions
92-
wantErr bool
93-
afterFunc func(t *WithT, obj client.Object)
94-
assertConditions []metav1.Condition
95+
name string
96+
generation int64
97+
beforeFunc func(obj conditions.Setter)
98+
result reconcile.Result
99+
reconcileErr error
100+
conditions []Conditions
101+
bipolarConditions []string
102+
wantErr bool
103+
afterFunc func(t *WithT, obj client.Object)
104+
assertConditions []metav1.Condition
95105
}{
96106
// Success/Fail indicates if a reconciliation succeeded or failed.
97107
// The object generation is expected to match the observed generation in
@@ -250,6 +260,64 @@ func TestSummarizeAndPatch(t *testing.T) {
250260
},
251261
wantErr: true,
252262
},
263+
{
264+
name: "Fail, reconciling with bipolar condition False, Ready gets bipolar failure value",
265+
generation: 2,
266+
beforeFunc: func(obj conditions.Setter) {
267+
conditions.MarkReconciling(obj, "NewRevision", "new index revision")
268+
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, "VerifyFailed", "verify failed")
269+
},
270+
result: reconcile.ResultEmpty,
271+
reconcileErr: errors.New("failed to verify source"),
272+
conditions: []Conditions{testReadyConditions},
273+
bipolarConditions: testBipolarConditions,
274+
wantErr: true,
275+
assertConditions: []metav1.Condition{
276+
*conditions.FalseCondition(meta.ReadyCondition, "VerifyFailed", "verify failed"),
277+
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, "VerifyFailed", "verify failed"),
278+
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new index revision"),
279+
},
280+
},
281+
{
282+
name: "Fail, bipolar condition True, negative polarity True, Ready gets negative polarity value",
283+
generation: 2,
284+
beforeFunc: func(obj conditions.Setter) {
285+
conditions.MarkReconciling(obj, "NewGeneration", "new obj gen")
286+
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", "new digest")
287+
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Success", "verified")
288+
},
289+
result: reconcile.ResultEmpty,
290+
reconcileErr: errors.New("failed to create dir"),
291+
conditions: []Conditions{testReadyConditions},
292+
bipolarConditions: testBipolarConditions,
293+
wantErr: true,
294+
assertConditions: []metav1.Condition{
295+
*conditions.FalseCondition(meta.ReadyCondition, "NewRevision", "new digest"),
296+
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "new digest"),
297+
*conditions.TrueCondition(meta.ReconcilingCondition, "NewGeneration", "new obj gen"),
298+
*conditions.TrueCondition(sourcev1.SourceVerifiedCondition, "Success", "verified"),
299+
},
300+
},
301+
{
302+
name: "Fail, multiple bipolar conditions False, Ready gets the bipolar with high priority",
303+
generation: 2,
304+
beforeFunc: func(obj conditions.Setter) {
305+
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Success", "verified")
306+
conditions.MarkFalse(obj, testBipolarCondition1, "AAA", "aaa")
307+
conditions.MarkFalse(obj, testBipolarCondition2, "BBB", "bbb")
308+
},
309+
result: reconcile.ResultEmpty,
310+
reconcileErr: errors.New("some failure"),
311+
conditions: []Conditions{testReadyConditions},
312+
bipolarConditions: testBipolarConditions,
313+
wantErr: true,
314+
assertConditions: []metav1.Condition{
315+
*conditions.FalseCondition(meta.ReadyCondition, "AAA", "aaa"),
316+
*conditions.FalseCondition(testBipolarCondition1, "AAA", "aaa"),
317+
*conditions.FalseCondition(testBipolarCondition2, "BBB", "bbb"),
318+
*conditions.TrueCondition(sourcev1.SourceVerifiedCondition, "Success", "verified"),
319+
},
320+
},
253321
}
254322

255323
for _, tt := range tests {
@@ -289,6 +357,9 @@ func TestSummarizeAndPatch(t *testing.T) {
289357
WithProcessors(RecordContextualError, RecordReconcileReq),
290358
WithResultBuilder(reconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.Spec.Interval.Duration}),
291359
}
360+
if tt.bipolarConditions != nil {
361+
summaryOpts = append(summaryOpts, WithBiPolarityConditionTypes(tt.bipolarConditions...))
362+
}
292363
_, gotErr := summaryHelper.SummarizeAndPatch(ctx, obj, summaryOpts...)
293364
g.Expect(gotErr != nil).To(Equal(tt.wantErr))
294365

0 commit comments

Comments
 (0)