func TestGC(t *testing.T) { c, clock := newTestCache() // When empty c.gc() assertCacheSize(t, c, 0) tok1, err := c.Insert(nextRequest()) require.NoError(t, err) assertCacheSize(t, c, 1) clock.Step(10 * time.Second) tok2, err := c.Insert(nextRequest()) require.NoError(t, err) assertCacheSize(t, c, 2) // expired: tok1, tok2 // non-expired: tok3, tok4 clock.Step(2 * CacheTTL) tok3, err := c.Insert(nextRequest()) require.NoError(t, err) assertCacheSize(t, c, 1) clock.Step(10 * time.Second) tok4, err := c.Insert(nextRequest()) require.NoError(t, err) assertCacheSize(t, c, 2) _, ok := c.Consume(tok1) assert.False(t, ok) _, ok = c.Consume(tok2) assert.False(t, ok) _, ok = c.Consume(tok3) assert.True(t, ok) _, ok = c.Consume(tok4) assert.True(t, ok) // When full, nothing is expired. for i := 0; i < MaxInFlight; i++ { _, err := c.Insert(nextRequest()) require.NoError(t, err) } assertCacheSize(t, c, MaxInFlight) // When everything is expired clock.Step(2 * CacheTTL) _, err = c.Insert(nextRequest()) require.NoError(t, err) assertCacheSize(t, c, 1) }
func TestHealthy(t *testing.T) { testPleg := newTestGenericPLEG() pleg, _, clock := testPleg.pleg, testPleg.runtime, testPleg.clock ok, _ := pleg.Healthy() assert.True(t, ok, "pleg should be healthy") // Advance the clock without any relisting. clock.Step(time.Minute * 10) ok, _ = pleg.Healthy() assert.False(t, ok, "pleg should be unhealthy") // Relist and than advance the time by 1 minute. pleg should be healthy // because this is within the allowed limit. pleg.relist() clock.Step(time.Minute * 1) ok, _ = pleg.Healthy() assert.True(t, ok, "pleg should be healthy") }
func TestGetWork(t *testing.T) { q, clock := newTestBasicWorkQueue() q.Enqueue(types.UID("foo1"), -1*time.Minute) q.Enqueue(types.UID("foo2"), -1*time.Minute) q.Enqueue(types.UID("foo3"), 1*time.Minute) q.Enqueue(types.UID("foo4"), 1*time.Minute) expected := []types.UID{types.UID("foo1"), types.UID("foo2")} compareResults(t, expected, q.GetWork()) compareResults(t, []types.UID{}, q.GetWork()) // Dial the time to 1 hour ahead. clock.Step(time.Hour) expected = []types.UID{types.UID("foo3"), types.UID("foo4")} compareResults(t, expected, q.GetWork()) compareResults(t, []types.UID{}, q.GetWork()) }
func TestMultiSinkCache(t *testing.T) { testPod := &v1.Pod{ ObjectMeta: v1.ObjectMeta{ SelfLink: "/api/version/pods/foo", Name: "foo", Namespace: "baz", UID: "bar", }, } testPod2 := &v1.Pod{ ObjectMeta: v1.ObjectMeta{ SelfLink: "/api/version/pods/foo", Name: "foo", Namespace: "baz", UID: "differentUid", }, } testRef, err := v1.GetPartialReference(testPod, "spec.containers[2]") testRef2, err := v1.GetPartialReference(testPod2, "spec.containers[3]") if err != nil { t.Fatal(err) } table := []struct { obj k8sruntime.Object eventtype string reason string messageFmt string elements []interface{} expect *v1.Event expectLog string expectUpdate bool }{ { obj: testRef, eventtype: v1.EventTypeNormal, reason: "Started", messageFmt: "some verbose message: %v", elements: []interface{}{1}, expect: &v1.Event{ ObjectMeta: v1.ObjectMeta{ Name: "foo", Namespace: "baz", }, InvolvedObject: v1.ObjectReference{ Kind: "Pod", Name: "foo", Namespace: "baz", UID: "bar", APIVersion: "version", FieldPath: "spec.containers[2]", }, Reason: "Started", Message: "some verbose message: 1", Source: v1.EventSource{Component: "eventTest"}, Count: 1, Type: v1.EventTypeNormal, }, expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, expectUpdate: false, }, { obj: testPod, eventtype: v1.EventTypeNormal, reason: "Killed", messageFmt: "some other verbose message: %v", elements: []interface{}{1}, expect: &v1.Event{ ObjectMeta: v1.ObjectMeta{ Name: "foo", Namespace: "baz", }, InvolvedObject: v1.ObjectReference{ Kind: "Pod", Name: "foo", Namespace: "baz", UID: "bar", APIVersion: "version", }, Reason: "Killed", Message: "some other verbose message: 1", Source: v1.EventSource{Component: "eventTest"}, Count: 1, Type: v1.EventTypeNormal, }, expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:""}): type: 'Normal' reason: 'Killed' some other verbose message: 1`, expectUpdate: false, }, { obj: testRef, eventtype: v1.EventTypeNormal, reason: "Started", messageFmt: "some verbose message: %v", elements: []interface{}{1}, expect: &v1.Event{ ObjectMeta: v1.ObjectMeta{ Name: "foo", Namespace: "baz", }, InvolvedObject: v1.ObjectReference{ Kind: "Pod", Name: "foo", Namespace: "baz", UID: "bar", APIVersion: "version", FieldPath: "spec.containers[2]", }, Reason: "Started", Message: "some verbose message: 1", Source: v1.EventSource{Component: "eventTest"}, Count: 2, Type: v1.EventTypeNormal, }, expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, expectUpdate: true, }, { obj: testRef2, eventtype: v1.EventTypeNormal, reason: "Started", messageFmt: "some verbose message: %v", elements: []interface{}{1}, expect: &v1.Event{ ObjectMeta: v1.ObjectMeta{ Name: "foo", Namespace: "baz", }, InvolvedObject: v1.ObjectReference{ Kind: "Pod", Name: "foo", Namespace: "baz", UID: "differentUid", APIVersion: "version", FieldPath: "spec.containers[3]", }, Reason: "Started", Message: "some verbose message: 1", Source: v1.EventSource{Component: "eventTest"}, Count: 1, Type: v1.EventTypeNormal, }, expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, expectUpdate: false, }, { obj: testRef, eventtype: v1.EventTypeNormal, reason: "Started", messageFmt: "some verbose message: %v", elements: []interface{}{1}, expect: &v1.Event{ ObjectMeta: v1.ObjectMeta{ Name: "foo", Namespace: "baz", }, InvolvedObject: v1.ObjectReference{ Kind: "Pod", Name: "foo", Namespace: "baz", UID: "bar", APIVersion: "version", FieldPath: "spec.containers[2]", }, Reason: "Started", Message: "some verbose message: 1", Source: v1.EventSource{Component: "eventTest"}, Count: 3, Type: v1.EventTypeNormal, }, expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, expectUpdate: true, }, { obj: testRef2, eventtype: v1.EventTypeNormal, reason: "Stopped", messageFmt: "some verbose message: %v", elements: []interface{}{1}, expect: &v1.Event{ ObjectMeta: v1.ObjectMeta{ Name: "foo", Namespace: "baz", }, InvolvedObject: v1.ObjectReference{ Kind: "Pod", Name: "foo", Namespace: "baz", UID: "differentUid", APIVersion: "version", FieldPath: "spec.containers[3]", }, Reason: "Stopped", Message: "some verbose message: 1", Source: v1.EventSource{Component: "eventTest"}, Count: 1, Type: v1.EventTypeNormal, }, expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`, expectUpdate: false, }, { obj: testRef2, eventtype: v1.EventTypeNormal, reason: "Stopped", messageFmt: "some verbose message: %v", elements: []interface{}{1}, expect: &v1.Event{ ObjectMeta: v1.ObjectMeta{ Name: "foo", Namespace: "baz", }, InvolvedObject: v1.ObjectReference{ Kind: "Pod", Name: "foo", Namespace: "baz", UID: "differentUid", APIVersion: "version", FieldPath: "spec.containers[3]", }, Reason: "Stopped", Message: "some verbose message: 1", Source: v1.EventSource{Component: "eventTest"}, Count: 2, Type: v1.EventTypeNormal, }, expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`, expectUpdate: true, }, } testCache := map[string]*v1.Event{} createEvent := make(chan *v1.Event) updateEvent := make(chan *v1.Event) patchEvent := make(chan *v1.Event) testEvents := testEventSink{ OnCreate: OnCreateFactory(testCache, createEvent), OnUpdate: func(event *v1.Event) (*v1.Event, error) { updateEvent <- event return event, nil }, OnPatch: OnPatchFactory(testCache, patchEvent), } testCache2 := map[string]*v1.Event{} createEvent2 := make(chan *v1.Event) updateEvent2 := make(chan *v1.Event) patchEvent2 := make(chan *v1.Event) testEvents2 := testEventSink{ OnCreate: OnCreateFactory(testCache2, createEvent2), OnUpdate: func(event *v1.Event) (*v1.Event, error) { updateEvent2 <- event return event, nil }, OnPatch: OnPatchFactory(testCache2, patchEvent2), } eventBroadcaster := NewBroadcasterForTests(0) clock := clock.NewFakeClock(time.Now()) recorder := recorderWithFakeClock(v1.EventSource{Component: "eventTest"}, eventBroadcaster, clock) sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents) for index, item := range table { clock.Step(1 * time.Second) recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...) // validate event if item.expectUpdate { actualEvent := <-patchEvent validateEvent(strconv.Itoa(index), actualEvent, item.expect, t) } else { actualEvent := <-createEvent validateEvent(strconv.Itoa(index), actualEvent, item.expect, t) } } // Another StartRecordingToSink call should start to record events with new clean cache. sinkWatcher2 := eventBroadcaster.StartRecordingToSink(&testEvents2) for index, item := range table { clock.Step(1 * time.Second) recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...) // validate event if item.expectUpdate { actualEvent := <-patchEvent2 validateEvent(strconv.Itoa(index), actualEvent, item.expect, t) } else { actualEvent := <-createEvent2 validateEvent(strconv.Itoa(index), actualEvent, item.expect, t) } } sinkWatcher.Stop() sinkWatcher2.Stop() }
func TestEventfNoNamespace(t *testing.T) { testPod := &v1.Pod{ ObjectMeta: v1.ObjectMeta{ SelfLink: "/api/version/pods/foo", Name: "foo", UID: "bar", }, } testRef, err := v1.GetPartialReference(testPod, "spec.containers[2]") if err != nil { t.Fatal(err) } table := []struct { obj k8sruntime.Object eventtype string reason string messageFmt string elements []interface{} expect *v1.Event expectLog string expectUpdate bool }{ { obj: testRef, eventtype: v1.EventTypeNormal, reason: "Started", messageFmt: "some verbose message: %v", elements: []interface{}{1}, expect: &v1.Event{ ObjectMeta: v1.ObjectMeta{ Name: "foo", Namespace: "default", }, InvolvedObject: v1.ObjectReference{ Kind: "Pod", Name: "foo", Namespace: "", UID: "bar", APIVersion: "version", FieldPath: "spec.containers[2]", }, Reason: "Started", Message: "some verbose message: 1", Source: v1.EventSource{Component: "eventTest"}, Count: 1, Type: v1.EventTypeNormal, }, expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, expectUpdate: false, }, } testCache := map[string]*v1.Event{} logCalled := make(chan struct{}) createEvent := make(chan *v1.Event) updateEvent := make(chan *v1.Event) patchEvent := make(chan *v1.Event) testEvents := testEventSink{ OnCreate: OnCreateFactory(testCache, createEvent), OnUpdate: func(event *v1.Event) (*v1.Event, error) { updateEvent <- event return event, nil }, OnPatch: OnPatchFactory(testCache, patchEvent), } eventBroadcaster := NewBroadcasterForTests(0) sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents) clock := clock.NewFakeClock(time.Now()) recorder := recorderWithFakeClock(v1.EventSource{Component: "eventTest"}, eventBroadcaster, clock) for index, item := range table { clock.Step(1 * time.Second) logWatcher := eventBroadcaster.StartLogging(func(formatter string, args ...interface{}) { if e, a := item.expectLog, fmt.Sprintf(formatter, args...); e != a { t.Errorf("Expected '%v', got '%v'", e, a) } logCalled <- struct{}{} }) recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...) <-logCalled // validate event if item.expectUpdate { actualEvent := <-patchEvent validateEvent(strconv.Itoa(index), actualEvent, item.expect, t) } else { actualEvent := <-createEvent validateEvent(strconv.Itoa(index), actualEvent, item.expect, t) } logWatcher.Stop() } sinkWatcher.Stop() }
func TestConsume(t *testing.T) { c, clock := newTestCache() { // Insert & consume. req := nextRequest() tok, err := c.Insert(req) require.NoError(t, err) assertCacheSize(t, c, 1) cachedReq, ok := c.Consume(tok) assert.True(t, ok) assert.Equal(t, req, cachedReq) assertCacheSize(t, c, 0) } { // Insert & consume out of order req1 := nextRequest() tok1, err := c.Insert(req1) require.NoError(t, err) assertCacheSize(t, c, 1) req2 := nextRequest() tok2, err := c.Insert(req2) require.NoError(t, err) assertCacheSize(t, c, 2) cachedReq2, ok := c.Consume(tok2) assert.True(t, ok) assert.Equal(t, req2, cachedReq2) assertCacheSize(t, c, 1) cachedReq1, ok := c.Consume(tok1) assert.True(t, ok) assert.Equal(t, req1, cachedReq1) assertCacheSize(t, c, 0) } { // Consume a second time req := nextRequest() tok, err := c.Insert(req) require.NoError(t, err) assertCacheSize(t, c, 1) cachedReq, ok := c.Consume(tok) assert.True(t, ok) assert.Equal(t, req, cachedReq) assertCacheSize(t, c, 0) _, ok = c.Consume(tok) assert.False(t, ok) assertCacheSize(t, c, 0) } { // Consume without insert _, ok := c.Consume("fooBAR") assert.False(t, ok) assertCacheSize(t, c, 0) } { // Consume expired tok, err := c.Insert(nextRequest()) require.NoError(t, err) assertCacheSize(t, c, 1) clock.Step(2 * CacheTTL) _, ok := c.Consume(tok) assert.False(t, ok) assertCacheSize(t, c, 0) } }