Skip to content

Commit d1ca160

Browse files
authored
Merge pull request #11342 from fabriziopandini/truncate-lastTransitionTime-v1beta2-conditions
🌱 Truncate lastTransitionTime for v1beta2 conditions
2 parents 419a9a6 + 09e6c8a commit d1ca160

File tree

4 files changed

+70
-13
lines changed

4 files changed

+70
-13
lines changed

util/conditions/v1beta2/patch.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func (p Patch) Apply(latest Setter, opts ...PatchApplyOption) error {
148148
case AddConditionPatch:
149149
// If the condition is owned, always keep the after value.
150150
if applyOpt.forceOverwrite || applyOpt.isOwnedConditionType(conditionPatch.After.Type) {
151-
meta.SetStatusCondition(&latestConditions, *conditionPatch.After)
151+
setStatusCondition(&latestConditions, *conditionPatch.After)
152152
continue
153153
}
154154

@@ -163,12 +163,12 @@ func (p Patch) Apply(latest Setter, opts ...PatchApplyOption) error {
163163
continue
164164
}
165165
// If the condition does not exists on the latest, add the new after condition.
166-
meta.SetStatusCondition(&latestConditions, *conditionPatch.After)
166+
setStatusCondition(&latestConditions, *conditionPatch.After)
167167

168168
case ChangeConditionPatch:
169169
// If the conditions is owned, always keep the after value.
170170
if applyOpt.forceOverwrite || applyOpt.isOwnedConditionType(conditionPatch.After.Type) {
171-
meta.SetStatusCondition(&latestConditions, *conditionPatch.After)
171+
setStatusCondition(&latestConditions, *conditionPatch.After)
172172
continue
173173
}
174174

@@ -190,7 +190,7 @@ func (p Patch) Apply(latest Setter, opts ...PatchApplyOption) error {
190190
continue
191191
}
192192
// Otherwise apply the new after condition.
193-
meta.SetStatusCondition(&latestConditions, *conditionPatch.After)
193+
setStatusCondition(&latestConditions, *conditionPatch.After)
194194

195195
case RemoveConditionPatch:
196196
// If latestConditions is nil or empty, nothing to remove.

util/conditions/v1beta2/patch_test.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1beta2
1818

1919
import (
2020
"testing"
21+
"time"
2122

2223
. "github.com/onsi/gomega"
2324
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -26,8 +27,9 @@ import (
2627
)
2728

2829
func TestNewPatch(t *testing.T) {
29-
fooTrue := metav1.Condition{Type: "foo", Status: metav1.ConditionTrue}
30-
fooFalse := metav1.Condition{Type: "foo", Status: metav1.ConditionFalse}
30+
now := metav1.Now()
31+
fooTrue := metav1.Condition{Type: "foo", Status: metav1.ConditionTrue, LastTransitionTime: now}
32+
fooFalse := metav1.Condition{Type: "foo", Status: metav1.ConditionFalse, LastTransitionTime: now}
3133

3234
tests := []struct {
3335
name string
@@ -133,6 +135,12 @@ func TestApply(t *testing.T) {
133135
fooFalse := metav1.Condition{Type: "foo", Status: metav1.ConditionFalse, LastTransitionTime: now}
134136
fooFalse2 := metav1.Condition{Type: "foo", Status: metav1.ConditionFalse, Reason: "Something else", LastTransitionTime: now}
135137

138+
addMilliseconds := func(c metav1.Condition) metav1.Condition {
139+
c1 := c.DeepCopy()
140+
c1.LastTransitionTime.Time = c1.LastTransitionTime.Add(10 * time.Millisecond)
141+
return *c1
142+
}
143+
136144
tests := []struct {
137145
name string
138146
before Setter
@@ -169,7 +177,7 @@ func TestApply(t *testing.T) {
169177
{
170178
name: "Add: When a condition does not exists, it should add",
171179
before: objectWithConditions(),
172-
after: objectWithConditions(fooTrue),
180+
after: objectWithConditions(addMilliseconds(fooTrue)), // this will force the test to fail if an AddConditionPatch operation doesn't drop milliseconds
173181
latest: objectWithConditions(),
174182
want: []metav1.Condition{fooTrue},
175183
wantErr: false,
@@ -193,7 +201,7 @@ func TestApply(t *testing.T) {
193201
{
194202
name: "Add: When a condition already exists but with conflicts, it should not error if force override is set",
195203
before: objectWithConditions(),
196-
after: objectWithConditions(fooTrue),
204+
after: objectWithConditions(addMilliseconds(fooTrue)), // this will force the test to fail if an AddConditionPatch operation doesn't drop milliseconds
197205
latest: objectWithConditions(fooFalse),
198206
options: []PatchApplyOption{ForceOverwrite(true)},
199207
want: []metav1.Condition{fooTrue}, // after condition should be kept in case of error
@@ -202,7 +210,7 @@ func TestApply(t *testing.T) {
202210
{
203211
name: "Add: When a condition already exists but with conflicts, it should not error if the condition is owned",
204212
before: objectWithConditions(),
205-
after: objectWithConditions(fooTrue),
213+
after: objectWithConditions(addMilliseconds(fooTrue)), // this will force the test to fail if an AddConditionPatch operation doesn't drop milliseconds
206214
latest: objectWithConditions(fooFalse),
207215
options: []PatchApplyOption{OwnedConditionTypes{"foo"}},
208216
want: []metav1.Condition{fooTrue}, // after condition should be kept in case of error
@@ -253,7 +261,7 @@ func TestApply(t *testing.T) {
253261
{
254262
name: "Change: When a condition exists without conflicts, it should change",
255263
before: objectWithConditions(fooTrue),
256-
after: objectWithConditions(fooFalse),
264+
after: objectWithConditions(addMilliseconds(fooFalse)), // this will force the test to fail if an ChangeConditionPatch operation doesn't drop milliseconds
257265
latest: objectWithConditions(fooTrue),
258266
want: []metav1.Condition{fooFalse},
259267
wantErr: false,
@@ -277,7 +285,7 @@ func TestApply(t *testing.T) {
277285
{
278286
name: "Change: When a condition exists with conflicts but there is no agreement on the final state, it should not error if force override is set",
279287
before: objectWithConditions(fooFalse),
280-
after: objectWithConditions(fooFalse2),
288+
after: objectWithConditions(addMilliseconds(fooFalse2)), // this will force the test to fail if an ChangeConditionPatch operation doesn't drop milliseconds
281289
latest: objectWithConditions(fooTrue),
282290
options: []PatchApplyOption{ForceOverwrite(true)},
283291
want: []metav1.Condition{fooFalse2},
@@ -286,7 +294,7 @@ func TestApply(t *testing.T) {
286294
{
287295
name: "Change: When a condition exists with conflicts but there is no agreement on the final state, it should not error if the condition is owned",
288296
before: objectWithConditions(fooFalse),
289-
after: objectWithConditions(fooFalse2),
297+
after: objectWithConditions(addMilliseconds(fooFalse2)), // this will force the test to fail if an ChangeConditionPatch operation doesn't drop milliseconds
290298
latest: objectWithConditions(fooTrue),
291299
options: []PatchApplyOption{OwnedConditionTypes{"foo"}},
292300
want: []metav1.Condition{fooFalse2},

util/conditions/v1beta2/setter.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1beta2
1818

1919
import (
2020
"sort"
21+
"time"
2122

2223
"k8s.io/apimachinery/pkg/api/meta"
2324
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -85,7 +86,7 @@ func Set(targetObj Setter, condition metav1.Condition, opts ...SetOption) {
8586
}
8687

8788
conditions := targetObj.GetV1Beta2Conditions()
88-
if changed := meta.SetStatusCondition(&conditions, condition); !changed {
89+
if changed := setStatusCondition(&conditions, condition); !changed {
8990
return
9091
}
9192

@@ -98,6 +99,17 @@ func Set(targetObj Setter, condition metav1.Condition, opts ...SetOption) {
9899
targetObj.SetV1Beta2Conditions(conditions)
99100
}
100101

102+
func setStatusCondition(conditions *[]metav1.Condition, condition metav1.Condition) bool {
103+
// Truncate last transition time to seconds.
104+
// This prevents inconsistencies from what we have in objects in memory and what Marshal/Unmarshal
105+
// will do while the data is sent to/read from the API server.
106+
if condition.LastTransitionTime.IsZero() {
107+
condition.LastTransitionTime = metav1.Now()
108+
}
109+
condition.LastTransitionTime.Time = condition.LastTransitionTime.Truncate(1 * time.Second)
110+
return meta.SetStatusCondition(conditions, condition)
111+
}
112+
101113
// Delete deletes the condition with the given type.
102114
func Delete(to Setter, conditionType string) {
103115
if to == nil {

util/conditions/v1beta2/setter_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1beta2
1818

1919
import (
2020
"testing"
21+
"time"
2122

2223
"github.com/google/go-cmp/cmp"
2324
. "github.com/onsi/gomega"
@@ -176,6 +177,42 @@ func TestSet(t *testing.T) {
176177
expected := []metav1.Condition{condition}
177178
g.Expect(foo.Status.Conditions).To(Equal(expected), cmp.Diff(foo.Status.Conditions, expected))
178179
})
180+
181+
t.Run("Set drops milliseconds", func(t *testing.T) {
182+
g := NewWithT(t)
183+
foo := &builder.Phase3Obj{
184+
ObjectMeta: metav1.ObjectMeta{Generation: 123},
185+
Status: builder.Phase3ObjStatus{
186+
Conditions: nil,
187+
},
188+
}
189+
190+
condition := metav1.Condition{
191+
Type: "fooCondition",
192+
Status: metav1.ConditionTrue,
193+
Reason: "FooReason",
194+
Message: "FooMessage",
195+
}
196+
197+
// Check LastTransitionTime after setting a condition for the first time
198+
Set(foo, condition)
199+
ltt1 := foo.Status.Conditions[0].LastTransitionTime.Time
200+
g.Expect(ltt1).To(Equal(ltt1.Truncate(1*time.Second)), cmp.Diff(ltt1, ltt1.Truncate(1*time.Second)))
201+
202+
// Check LastTransitionTime after changing an existing condition
203+
condition.Status = metav1.ConditionFalse // this will force set to change the LastTransitionTime
204+
condition.LastTransitionTime = metav1.Time{} // this will force set to compute a new LastTransitionTime
205+
Set(foo, condition)
206+
ltt2 := foo.Status.Conditions[0].LastTransitionTime.Time
207+
g.Expect(ltt2).To(Equal(ltt2.Truncate(1*time.Second)), cmp.Diff(ltt2, ltt2.Truncate(1*time.Second)))
208+
209+
// Check LastTransitionTime after setting a Time with milliseconds
210+
condition.Status = metav1.ConditionTrue // this will force set to change the LastTransitionTime
211+
condition.LastTransitionTime = metav1.Now() // this will force set to not default LastTransitionTime
212+
Set(foo, condition)
213+
ltt3 := foo.Status.Conditions[0].LastTransitionTime.Time
214+
g.Expect(ltt3).To(Equal(ltt3.Truncate(1*time.Second)), cmp.Diff(ltt3, ltt3.Truncate(1*time.Second)))
215+
})
179216
}
180217

181218
func TestDelete(t *testing.T) {

0 commit comments

Comments
 (0)