Skip to content

Commit 95e1378

Browse files
Merge pull request #1798 from deads2k/apply-04-lasttransitiontime
API-1835: set the last transition time for conditions
2 parents f6478ea + cf085ed commit 95e1378

File tree

2 files changed

+60
-5
lines changed

2 files changed

+60
-5
lines changed

pkg/operator/genericoperatorclient/dynamic_operator_client.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,11 @@ func (c dynamicOperatorClient) applyOperatorStatus(ctx context.Context, fieldMan
282282
uncastOriginal, err := c.informer.Lister().Get(c.configName)
283283
switch {
284284
case apierrors.IsNotFound(err):
285-
// do nothing and proceed with the apply
285+
// set last transitionTimes and then apply
286+
// If our cache improperly 404's (the lister wasn't synchronized), then we will improperly reset all the last transition times.
287+
// This isn't ideal, but we shouldn't hit this case unless a loop isn't waiting for HasSynced.
288+
v1helpers.SetApplyConditionsLastTransitionTime(&desiredConfiguration.Conditions, nil)
289+
286290
case err != nil:
287291
return fmt.Errorf("unable to read existing %q: %w", c.configName, err)
288292
default:
@@ -295,6 +299,24 @@ func (c dynamicOperatorClient) applyOperatorStatus(ctx context.Context, fieldMan
295299
return fmt.Errorf("unable to extract status for %q: %w", fieldManager, err)
296300
}
297301

302+
// set last transitionTimes to properly calculate a difference
303+
// It is possible for last transition time to shift a couple times until the cache updates to have the condition[*].status match,
304+
// but it will eventually settle. The failing sequence looks like
305+
/*
306+
1. type=foo, status=false, time=t0.Now
307+
2. type=foo, status=true, time=t1.Now
308+
3. rapid update happens and the cache still indicates #1
309+
4. type=foo, status=true, time=t2.Now (this *should* be t1.Now)
310+
*/
311+
// Eventually the cache updates to see at #2 and we stop applying new times.
312+
// This only becomes pathological if the condition is also flapping, but if that happens the time should also update.
313+
switch {
314+
case desiredConfiguration != nil && desiredConfiguration.Conditions != nil && previouslyDesiredConfiguration != nil:
315+
v1helpers.SetApplyConditionsLastTransitionTime(&desiredConfiguration.Conditions, previouslyDesiredConfiguration.Conditions)
316+
case desiredConfiguration != nil && desiredConfiguration.Conditions != nil && previouslyDesiredConfiguration == nil:
317+
v1helpers.SetApplyConditionsLastTransitionTime(&desiredConfiguration.Conditions, nil)
318+
}
319+
298320
// canonicalize so the DeepEqual works consistently
299321
v1helpers.CanonicalizeStaticPodOperatorStatus(previouslyDesiredConfiguration)
300322
v1helpers.CanonicalizeStaticPodOperatorStatus(desiredConfiguration)

pkg/operator/v1helpers/canonicalize.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ package v1helpers
22

33
import (
44
"fmt"
5-
"slices"
6-
"strings"
7-
85
operatorv1 "github.com/openshift/api/operator/v1"
6+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
97
"k8s.io/apimachinery/pkg/util/json"
10-
118
"k8s.io/utils/ptr"
9+
"slices"
10+
"strings"
1211

1312
applyoperatorv1 "github.com/openshift/client-go/operator/applyconfigurations/operator/v1"
1413
)
@@ -33,6 +32,40 @@ func ToStaticPodOperator(in *applyoperatorv1.StaticPodOperatorStatusApplyConfigu
3332
return ret, nil
3433
}
3534

35+
func SetApplyConditionsLastTransitionTime(newConditions *[]applyoperatorv1.OperatorConditionApplyConfiguration, oldConditions []applyoperatorv1.OperatorConditionApplyConfiguration) {
36+
if newConditions == nil {
37+
return
38+
}
39+
40+
now := metav1.Now()
41+
for i := range *newConditions {
42+
newCondition := (*newConditions)[i]
43+
44+
// if the condition status is the same, then the lastTransitionTime doesn't change
45+
if existingCondition := FindApplyCondition(oldConditions, newCondition.Type); existingCondition != nil && ptr.Equal(existingCondition.Status, newCondition.Status) {
46+
newCondition.LastTransitionTime = existingCondition.LastTransitionTime
47+
}
48+
49+
// backstop to handle upgrade case too. If the newCondition doesn't have a lastTransitionTime it needs something
50+
if newCondition.LastTransitionTime == nil {
51+
newCondition.LastTransitionTime = &now
52+
}
53+
54+
(*newConditions)[i] = newCondition
55+
}
56+
}
57+
58+
func FindApplyCondition(haystack []applyoperatorv1.OperatorConditionApplyConfiguration, conditionType *string) *applyoperatorv1.OperatorConditionApplyConfiguration {
59+
for i := range haystack {
60+
curr := haystack[i]
61+
if ptr.Equal(curr.Type, conditionType) {
62+
return &curr
63+
}
64+
}
65+
66+
return nil
67+
}
68+
3669
func CanonicalizeStaticPodOperatorStatus(obj *applyoperatorv1.StaticPodOperatorStatusApplyConfiguration) {
3770
if obj == nil {
3871
return

0 commit comments

Comments
 (0)