Skip to content

Commit 245188c

Browse files
committed
add tests for condition event recording
1 parent 6405b79 commit 245188c

File tree

2 files changed

+218
-12
lines changed

2 files changed

+218
-12
lines changed

pkg/conditions/updater.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,20 @@ import (
1313
"k8s.io/client-go/tools/record"
1414
)
1515

16+
const EventReasonConditionChanged = "ConditionChanged"
17+
1618
type EventVerbosity string
1719

1820
const (
1921
// EventPerChange causes one event to be recorded for each condition that has changed.
2022
// This is the most verbose setting. The old and new status of each changed condition will be visible in the event message.
2123
EventPerChange EventVerbosity = "perChange"
2224
// EventPerNewStatus causes one event to be recorded for each new status that any condition has reached.
23-
// This means that at max three events will be recorded:
24-
// - the following conditions changed to True
25-
// - the following conditions changed to False
26-
// - the following conditions changed to Unknown
25+
// This means that at max four events will be recorded:
26+
// - the following conditions changed to True (including newly added conditions)
27+
// - the following conditions changed to False (including newly added conditions)
28+
// - the following conditions changed to Unknown (including newly added conditions)
29+
// - the following conditions were removed
2730
// The old status of the conditions will not be part of the event message.
2831
EventPerNewStatus EventVerbosity = "perNewStatus"
2932
// EventIfChanged causes a single event to be recorded if any condition's status has changed.
@@ -188,16 +191,16 @@ func (c *conditionUpdater) Record(obj runtime.Object) *conditionUpdater {
188191
for _, con := range updatedCons {
189192
oldCon, found := c.original[con.Type]
190193
if !found {
191-
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, "Condition '%s' added with status '%s'", con.Type, string(con.Status))
194+
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, EventReasonConditionChanged, "Condition '%s' added with status '%s'", con.Type, con.Status)
192195
continue
193196
}
194197
if con.Status != oldCon.Status {
195-
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, "Condition '%s' changed from '%s' to '%s'", con.Type, string(oldCon.Status), string(con.Status))
198+
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, EventReasonConditionChanged, "Condition '%s' changed from '%s' to '%s'", con.Type, oldCon.Status, con.Status)
196199
continue
197200
}
198201
}
199202
for conType, oldStatus := range lostCons {
200-
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, "Condition '%s' with status '%s' removed", conType, string(oldStatus))
203+
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, EventReasonConditionChanged, "Condition '%s' with status '%s' removed", conType, oldStatus)
201204
}
202205

203206
case EventPerNewStatus:
@@ -221,16 +224,16 @@ func (c *conditionUpdater) Record(obj runtime.Object) *conditionUpdater {
221224
}
222225

223226
if trueCons.Len() > 0 {
224-
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, "The following conditions changed to 'True': %s", strings.Join(sets.List(trueCons), ", "))
227+
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, EventReasonConditionChanged, "The following conditions changed to 'True': %s", strings.Join(sets.List(trueCons), ", "))
225228
}
226229
if falseCons.Len() > 0 {
227-
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, "The following conditions changed to 'False': %s", strings.Join(sets.List(falseCons), ", "))
230+
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, EventReasonConditionChanged, "The following conditions changed to 'False': %s", strings.Join(sets.List(falseCons), ", "))
228231
}
229232
if unknownCons.Len() > 0 {
230-
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, "The following conditions changed to 'Unknown': %s", strings.Join(sets.List(unknownCons), ", "))
233+
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, EventReasonConditionChanged, "The following conditions changed to 'Unknown': %s", strings.Join(sets.List(unknownCons), ", "))
231234
}
232235
if len(lostCons) > 0 {
233-
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, "The following conditions were removed: %s", strings.Join(sets.List(sets.KeySet(lostCons)), ", "))
236+
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, EventReasonConditionChanged, "The following conditions were removed: %s", strings.Join(sets.List(sets.KeySet(lostCons)), ", "))
234237
}
235238

236239
case EventIfChanged:
@@ -244,7 +247,7 @@ func (c *conditionUpdater) Record(obj runtime.Object) *conditionUpdater {
244247
changedCons.Insert(conType)
245248
}
246249
if changedCons.Len() > 0 {
247-
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, "The following conditions have changed: %s", strings.Join(sets.List(changedCons), ", "))
250+
c.eventRecoder.Eventf(obj, corev1.EventTypeNormal, EventReasonConditionChanged, "The following conditions have changed: %s", strings.Join(sets.List(changedCons), ", "))
248251
}
249252
}
250253

pkg/conditions/updater_test.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
. "github.com/openmcp-project/controller-utils/pkg/testing/matchers"
1212

1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/apimachinery/pkg/runtime"
15+
"k8s.io/client-go/tools/record"
1416

1517
"github.com/openmcp-project/controller-utils/pkg/conditions"
1618
)
@@ -213,4 +215,205 @@ var _ = Describe("Conditions", func() {
213215

214216
})
215217

218+
Context("EventRecorder", func() {
219+
220+
var recorder *record.FakeRecorder
221+
222+
dummy := &dummyObject{
223+
TypeMeta: metav1.TypeMeta{
224+
Kind: "Dummy",
225+
APIVersion: "dummy/v1",
226+
},
227+
}
228+
229+
BeforeEach(func() {
230+
recorder = record.NewFakeRecorder(100)
231+
})
232+
233+
AfterEach(func() {
234+
close(recorder.Events)
235+
})
236+
237+
Context("Verbosity: EventPerChange", func() {
238+
239+
It("should record an event for each changed condition status", func() {
240+
cons := testConditionSet()
241+
updater := conditions.ConditionUpdater(cons, false).WithEventRecorder(recorder, conditions.EventPerChange)
242+
trueCon1 := conditions.GetCondition(cons, "true")
243+
trueCon2 := conditions.GetCondition(cons, "alsoTrue")
244+
_, changed := updater.
245+
UpdateCondition(trueCon1.Type, invert(trueCon1.Status), trueCon1.ObservedGeneration+1, "newReason", "newMessage").
246+
UpdateCondition(trueCon2.Type, invert(trueCon2.Status), trueCon2.ObservedGeneration+1, "newReason", "newMessage").
247+
Record(dummy).Conditions()
248+
Expect(changed).To(BeTrue())
249+
250+
events := flush(recorder.Events)
251+
Expect(events).To(ConsistOf(
252+
ContainSubstring("Condition '%s' changed from '%s' to '%s'", trueCon1.Type, trueCon1.Status, invert(trueCon1.Status)),
253+
ContainSubstring("Condition '%s' changed from '%s' to '%s'", trueCon2.Type, trueCon2.Status, invert(trueCon2.Status)),
254+
))
255+
})
256+
257+
It("should not record any events if no condition status changed, even if other fields changed", func() {
258+
cons := testConditionSet()
259+
updater := conditions.ConditionUpdater(cons, false).WithEventRecorder(recorder, conditions.EventPerChange)
260+
trueCon1 := conditions.GetCondition(cons, "true")
261+
trueCon2 := conditions.GetCondition(cons, "alsoTrue")
262+
_, changed := updater.
263+
UpdateCondition(trueCon1.Type, trueCon1.Status, trueCon1.ObservedGeneration+1, "newReason", "newMessage").
264+
UpdateCondition(trueCon2.Type, trueCon2.Status, trueCon2.ObservedGeneration+1, "newReason", "newMessage").
265+
Record(dummy).Conditions()
266+
Expect(changed).To(BeTrue())
267+
268+
events := flush(recorder.Events)
269+
Expect(events).To(BeEmpty())
270+
})
271+
272+
It("should record added and lost conditions", func() {
273+
cons := testConditionSet()
274+
updater := conditions.ConditionUpdater(cons, false).WithEventRecorder(recorder, conditions.EventPerChange)
275+
_, changed := updater.
276+
UpdateCondition("new", metav1.ConditionUnknown, 1, "newReason", "newMessage").
277+
RemoveCondition("true").
278+
Record(dummy).Conditions()
279+
Expect(changed).To(BeTrue())
280+
281+
events := flush(recorder.Events)
282+
Expect(events).To(ConsistOf(
283+
ContainSubstring("Condition 'new' added with status '%s'", metav1.ConditionUnknown),
284+
ContainSubstring("Condition 'true' with status '%s' removed", metav1.ConditionTrue),
285+
))
286+
})
287+
288+
})
289+
290+
Context("Verbosity: EventPerNewStatus", func() {
291+
292+
It("should record an event for each new status that any condition has reached", func() {
293+
cons := testConditionSet()
294+
updater := conditions.ConditionUpdater(cons, false).WithEventRecorder(recorder, conditions.EventPerNewStatus)
295+
trueCon1 := conditions.GetCondition(cons, "true")
296+
trueCon2 := conditions.GetCondition(cons, "alsoTrue")
297+
falseCon := conditions.GetCondition(cons, "false")
298+
_, changed := updater.
299+
UpdateCondition(trueCon1.Type, metav1.ConditionUnknown, trueCon1.ObservedGeneration+1, "newReason", "newMessage").
300+
UpdateCondition(trueCon2.Type, metav1.ConditionUnknown, trueCon2.ObservedGeneration+1, "newReason", "newMessage").
301+
UpdateCondition("anotherOne", metav1.ConditionUnknown, 1, "newReason", "newMessage").
302+
UpdateCondition(falseCon.Type, invert(falseCon.Status), falseCon.ObservedGeneration+1, "newReason", "newMessage").
303+
Record(dummy).Conditions()
304+
Expect(changed).To(BeTrue())
305+
306+
events := flush(recorder.Events)
307+
Expect(events).To(ConsistOf(
308+
And(
309+
ContainSubstring("The following conditions changed to '%s'", metav1.ConditionUnknown),
310+
ContainSubstring(trueCon1.Type),
311+
ContainSubstring(trueCon2.Type),
312+
ContainSubstring("anotherOne"),
313+
),
314+
And(
315+
ContainSubstring("The following conditions changed to '%s'", invert(falseCon.Status)),
316+
ContainSubstring(falseCon.Type),
317+
),
318+
))
319+
})
320+
321+
It("should not record any events if no condition status changed, even if other fields changed", func() {
322+
cons := testConditionSet()
323+
updater := conditions.ConditionUpdater(cons, false).WithEventRecorder(recorder, conditions.EventPerNewStatus)
324+
trueCon1 := conditions.GetCondition(cons, "true")
325+
trueCon2 := conditions.GetCondition(cons, "alsoTrue")
326+
_, changed := updater.
327+
UpdateCondition(trueCon1.Type, trueCon1.Status, trueCon1.ObservedGeneration+1, "newReason", "newMessage").
328+
UpdateCondition(trueCon2.Type, trueCon2.Status, trueCon2.ObservedGeneration+1, "newReason", "newMessage").
329+
Record(dummy).Conditions()
330+
Expect(changed).To(BeTrue())
331+
332+
events := flush(recorder.Events)
333+
Expect(events).To(BeEmpty())
334+
})
335+
336+
It("should record lost conditions", func() {
337+
cons := testConditionSet()
338+
updater := conditions.ConditionUpdater(cons, true).WithEventRecorder(recorder, conditions.EventPerNewStatus)
339+
_, changed := updater.Record(dummy).Conditions()
340+
Expect(changed).To(BeTrue())
341+
342+
events := flush(recorder.Events)
343+
Expect(events).To(ConsistOf(
344+
And(
345+
ContainSubstring("The following conditions were removed"),
346+
ContainSubstring("true"),
347+
ContainSubstring("false"),
348+
ContainSubstring("alsoTrue"),
349+
),
350+
))
351+
})
352+
353+
})
354+
355+
Context("Verbosity: EventIfChanged", func() {
356+
357+
It("should only record a single event, no matter how many conditions changed", func() {
358+
cons := testConditionSet()
359+
updater := conditions.ConditionUpdater(cons, false).WithEventRecorder(recorder, conditions.EventIfChanged)
360+
trueCon := conditions.GetCondition(cons, "true")
361+
falseCon := conditions.GetCondition(cons, "false")
362+
_, changed := updater.
363+
UpdateCondition(trueCon.Type, invert(trueCon.Status), trueCon.ObservedGeneration+1, "newReason", "newMessage").
364+
UpdateCondition(falseCon.Type, invert(falseCon.Status), falseCon.ObservedGeneration+1, "newReason", "newMessage").
365+
UpdateCondition("new", metav1.ConditionUnknown, 1, "newReason", "newMessage").
366+
RemoveCondition("alsoTrue").
367+
Record(dummy).Conditions()
368+
Expect(changed).To(BeTrue())
369+
370+
events := flush(recorder.Events)
371+
Expect(events).To(ConsistOf(
372+
And(
373+
ContainSubstring("The following conditions have changed"),
374+
ContainSubstring("true"),
375+
ContainSubstring("false"),
376+
ContainSubstring("alsoTrue"),
377+
ContainSubstring("new"),
378+
),
379+
))
380+
})
381+
382+
It("should not record any events if no condition status changed, even if other fields changed", func() {
383+
cons := testConditionSet()
384+
updater := conditions.ConditionUpdater(cons, false).WithEventRecorder(recorder, conditions.EventIfChanged)
385+
trueCon1 := conditions.GetCondition(cons, "true")
386+
trueCon2 := conditions.GetCondition(cons, "alsoTrue")
387+
_, changed := updater.
388+
UpdateCondition(trueCon1.Type, trueCon1.Status, trueCon1.ObservedGeneration+1, "newReason", "newMessage").
389+
UpdateCondition(trueCon2.Type, trueCon2.Status, trueCon2.ObservedGeneration+1, "newReason", "newMessage").
390+
Record(dummy).Conditions()
391+
Expect(changed).To(BeTrue())
392+
393+
events := flush(recorder.Events)
394+
Expect(events).To(BeEmpty())
395+
})
396+
397+
})
398+
399+
})
400+
216401
})
402+
403+
type dummyObject struct {
404+
metav1.TypeMeta `json:",inline"`
405+
}
406+
407+
func (d *dummyObject) DeepCopyObject() runtime.Object {
408+
return &dummyObject{
409+
TypeMeta: d.TypeMeta,
410+
}
411+
}
412+
413+
func flush(c chan string) []string {
414+
res := []string{}
415+
for len(c) > 0 {
416+
res = append(res, <-c)
417+
}
418+
return res
419+
}

0 commit comments

Comments
 (0)