From 4f9e5abe3180214e2fcb7a992475530394a29ce1 Mon Sep 17 00:00:00 2001 From: Johannes Aubart Date: Mon, 4 Aug 2025 14:59:04 +0200 Subject: [PATCH] add ConditionsToRemove field to ReconcileResult struct --- docs/libs/status.md | 1 + pkg/controller/status_updater.go | 8 ++++++++ pkg/controller/status_updater_test.go | 19 ++++++------------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/libs/status.md b/docs/libs/status.md index d510c66..113e1f2 100644 --- a/docs/libs/status.md +++ b/docs/libs/status.md @@ -174,6 +174,7 @@ The `ReconcileResult` that is passed into the status updater is expected to cont - `Reason` and `Message` can be set to set the status' corresponding fields. - If either one is nil, but `ReconcileError` is not, it will be filled with a value derived from the error. - `Conditions` contains the updated conditions. Depending on with which arguments `WithConditionUpdater` was called, the existing conditions will be either updated with these ones (keeping the other ones), or be replaced by them. +- `ConditionsToRemove` is a list of condition types that should be removed from the conditions. This is mostly useful if the condition updater is used in the 'keep untouched conditions' mode. - `Object` contains the object to be updated. - If `Object` is nil, no status update will be performed. - `OldObject` holds the version of the object that will be used as a base for constructing the patch during the status update. diff --git a/pkg/controller/status_updater.go b/pkg/controller/status_updater.go index 7ee8bba..e6d609a 100644 --- a/pkg/controller/status_updater.go +++ b/pkg/controller/status_updater.go @@ -265,6 +265,11 @@ func (s *statusUpdater[Obj]) UpdateStatus(ctx context.Context, c client.Client, } cu.UpdateCondition(con.Type, con.Status, gen, con.Reason, con.Message) } + if len(rr.ConditionsToRemove) > 0 { + for _, conType := range rr.ConditionsToRemove { + cu.RemoveCondition(conType) + } + } newCons, _ := cu.Record(rr.Object).Conditions() SetField(status, s.fieldNames[STATUS_FIELD_CONDITIONS], newCons) } @@ -416,6 +421,9 @@ type ReconcileResult[Obj client.Object] struct { // Also note that names of conditions are globally unique, so take care to avoid conflicts with other objects. // The lastTransition timestamp of the condition will be overwritten with the current time while updating. Conditions []metav1.Condition + // ConditionsToRemove is an optional slice of condition types for which the corresponding conditions should be removed from the status. + // This is useful if you want to remove conditions that are no longer relevant. + ConditionsToRemove []string } // GenerateCreateConditionFunc returns a function that can be used to add a condition to the given ReconcileResult. diff --git a/pkg/controller/status_updater_test.go b/pkg/controller/status_updater_test.go index 86ff0cb..b3e113a 100644 --- a/pkg/controller/status_updater_test.go +++ b/pkg/controller/status_updater_test.go @@ -123,7 +123,6 @@ var _ = Describe("Status Updater", func() { env := testutils.NewEnvironmentBuilder().WithFakeClient(coScheme).WithInitObjectPath("testdata", "test-02").WithDynamicObjectsWithStatus(&CustomObject{}).Build() obj := &CustomObject{} Expect(env.Client().Get(env.Ctx, controller.ObjectKey("status", "default"), obj)).To(Succeed()) - oldTransitionTime := conditions.GetCondition(obj.Status.Conditions, "TestConditionTrue").LastTransitionTime before := obj.DeepCopy() for _, disabledField := range controller.AllStatusFields() { By(fmt.Sprintf("Testing disabled field %s", disabledField)) @@ -132,10 +131,11 @@ var _ = Describe("Status Updater", func() { obj.Status = before.Status Expect(env.Client().Status().Patch(env.Ctx, obj, client.MergeFrom(modified))).To(Succeed()) rr := controller.ReconcileResult[*CustomObject]{ - Object: obj, - Conditions: dummyConditions(), - Reason: "TestReason", - Message: "TestMessage", + Object: obj, + Conditions: dummyConditions(), + Reason: "TestReason", + Message: "TestMessage", + ConditionsToRemove: []string{"TestConditionTrue"}, } su := preconfiguredStatusUpdaterBuilder().WithPhaseUpdateFunc(func(obj *CustomObject, rr controller.ReconcileResult[*CustomObject]) (string, error) { return PhaseSucceeded, nil @@ -176,13 +176,6 @@ var _ = Describe("Status Updater", func() { Expect(obj.Status.Conditions).To(Equal(before.Status.Conditions)) } else { Expect(obj.Status.Conditions).To(ConsistOf( - MatchCondition(TestCondition(). - WithType("TestConditionTrue"). - WithStatus(metav1.ConditionTrue). - WithObservedGeneration(10). - WithReason("TestReasonTrue"). - WithMessage("TestMessageTrue"). - WithLastTransitionTime(oldTransitionTime)), MatchCondition(TestCondition(). WithType("TestConditionFalse"). WithStatus(metav1.ConditionFalse). @@ -190,7 +183,7 @@ var _ = Describe("Status Updater", func() { WithReason("TestReasonFalse"). WithMessage("TestMessageFalse"). WithLastTransitionTime(now). - WithTimestampTolerance(1*time.Second)), + WithTimestampTolerance(1 * time.Second)), )) } }