k8s.io/client-go@v0.22.2/tools/record/event_test.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package record
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"net/http"
    23  	"strconv"
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  
    28  	v1 "k8s.io/api/core/v1"
    29  	"k8s.io/apimachinery/pkg/api/errors"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	k8sruntime "k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/util/clock"
    33  	"k8s.io/apimachinery/pkg/util/strategicpatch"
    34  	"k8s.io/client-go/kubernetes/scheme"
    35  	restclient "k8s.io/client-go/rest"
    36  	ref "k8s.io/client-go/tools/reference"
    37  )
    38  
    39  type testEventSink struct {
    40  	OnCreate func(e *v1.Event) (*v1.Event, error)
    41  	OnUpdate func(e *v1.Event) (*v1.Event, error)
    42  	OnPatch  func(e *v1.Event, p []byte) (*v1.Event, error)
    43  }
    44  
    45  // CreateEvent records the event for testing.
    46  func (t *testEventSink) Create(e *v1.Event) (*v1.Event, error) {
    47  	if t.OnCreate != nil {
    48  		return t.OnCreate(e)
    49  	}
    50  	return e, nil
    51  }
    52  
    53  // UpdateEvent records the event for testing.
    54  func (t *testEventSink) Update(e *v1.Event) (*v1.Event, error) {
    55  	if t.OnUpdate != nil {
    56  		return t.OnUpdate(e)
    57  	}
    58  	return e, nil
    59  }
    60  
    61  // PatchEvent records the event for testing.
    62  func (t *testEventSink) Patch(e *v1.Event, p []byte) (*v1.Event, error) {
    63  	if t.OnPatch != nil {
    64  		return t.OnPatch(e, p)
    65  	}
    66  	return e, nil
    67  }
    68  
    69  type OnCreateFunc func(*v1.Event) (*v1.Event, error)
    70  
    71  func OnCreateFactory(testCache map[string]*v1.Event, createEvent chan<- *v1.Event) OnCreateFunc {
    72  	return func(event *v1.Event) (*v1.Event, error) {
    73  		testCache[getEventKey(event)] = event
    74  		createEvent <- event
    75  		return event, nil
    76  	}
    77  }
    78  
    79  type OnPatchFunc func(*v1.Event, []byte) (*v1.Event, error)
    80  
    81  func OnPatchFactory(testCache map[string]*v1.Event, patchEvent chan<- *v1.Event) OnPatchFunc {
    82  	return func(event *v1.Event, patch []byte) (*v1.Event, error) {
    83  		cachedEvent, found := testCache[getEventKey(event)]
    84  		if !found {
    85  			return nil, fmt.Errorf("unexpected error: couldn't find Event in testCache.")
    86  		}
    87  		originalData, err := json.Marshal(cachedEvent)
    88  		if err != nil {
    89  			return nil, fmt.Errorf("unexpected error: %v", err)
    90  		}
    91  		patched, err := strategicpatch.StrategicMergePatch(originalData, patch, event)
    92  		if err != nil {
    93  			return nil, fmt.Errorf("unexpected error: %v", err)
    94  		}
    95  		patchedObj := &v1.Event{}
    96  		err = json.Unmarshal(patched, patchedObj)
    97  		if err != nil {
    98  			return nil, fmt.Errorf("unexpected error: %v", err)
    99  		}
   100  		patchEvent <- patchedObj
   101  		return patchedObj, nil
   102  	}
   103  }
   104  
   105  func TestNonRacyShutdown(t *testing.T) {
   106  	// Attempt to simulate previously racy conditions, and ensure that no race
   107  	// occurs: Nominally, calling "Eventf" *followed by* shutdown from the same
   108  	// thread should be a safe operation, but it's not if we launch recorder.Action
   109  	// in a goroutine.
   110  
   111  	caster := NewBroadcasterForTests(0)
   112  	clock := clock.NewFakeClock(time.Now())
   113  	recorder := recorderWithFakeClock(v1.EventSource{Component: "eventTest"}, caster, clock)
   114  
   115  	var wg sync.WaitGroup
   116  	wg.Add(100)
   117  	for i := 0; i < 100; i++ {
   118  		go func() {
   119  			defer wg.Done()
   120  			recorder.Eventf(&v1.ObjectReference{}, v1.EventTypeNormal, "Started", "blah")
   121  		}()
   122  	}
   123  
   124  	wg.Wait()
   125  	caster.Shutdown()
   126  }
   127  
   128  func TestEventf(t *testing.T) {
   129  	testPod := &v1.Pod{
   130  		ObjectMeta: metav1.ObjectMeta{
   131  			SelfLink:  "/api/v1/namespaces/baz/pods/foo",
   132  			Name:      "foo",
   133  			Namespace: "baz",
   134  			UID:       "bar",
   135  		},
   136  	}
   137  	testPod2 := &v1.Pod{
   138  		ObjectMeta: metav1.ObjectMeta{
   139  			SelfLink:  "/api/v1/namespaces/baz/pods/foo",
   140  			Name:      "foo",
   141  			Namespace: "baz",
   142  			UID:       "differentUid",
   143  		},
   144  	}
   145  	testRef, err := ref.GetPartialReference(scheme.Scheme, testPod, "spec.containers[2]")
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	testRef2, err := ref.GetPartialReference(scheme.Scheme, testPod2, "spec.containers[3]")
   150  	if err != nil {
   151  		t.Fatal(err)
   152  	}
   153  	table := []struct {
   154  		obj          k8sruntime.Object
   155  		eventtype    string
   156  		reason       string
   157  		messageFmt   string
   158  		elements     []interface{}
   159  		expect       *v1.Event
   160  		expectLog    string
   161  		expectUpdate bool
   162  	}{
   163  		{
   164  			obj:        testRef,
   165  			eventtype:  v1.EventTypeNormal,
   166  			reason:     "Started",
   167  			messageFmt: "some verbose message: %v",
   168  			elements:   []interface{}{1},
   169  			expect: &v1.Event{
   170  				ObjectMeta: metav1.ObjectMeta{
   171  					Name:      "foo",
   172  					Namespace: "baz",
   173  				},
   174  				InvolvedObject: v1.ObjectReference{
   175  					Kind:       "Pod",
   176  					Name:       "foo",
   177  					Namespace:  "baz",
   178  					UID:        "bar",
   179  					APIVersion: "v1",
   180  					FieldPath:  "spec.containers[2]",
   181  				},
   182  				Reason:  "Started",
   183  				Message: "some verbose message: 1",
   184  				Source:  v1.EventSource{Component: "eventTest"},
   185  				Count:   1,
   186  				Type:    v1.EventTypeNormal,
   187  			},
   188  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
   189  			expectUpdate: false,
   190  		},
   191  		{
   192  			obj:        testPod,
   193  			eventtype:  v1.EventTypeNormal,
   194  			reason:     "Killed",
   195  			messageFmt: "some other verbose message: %v",
   196  			elements:   []interface{}{1},
   197  			expect: &v1.Event{
   198  				ObjectMeta: metav1.ObjectMeta{
   199  					Name:      "foo",
   200  					Namespace: "baz",
   201  				},
   202  				InvolvedObject: v1.ObjectReference{
   203  					Kind:       "Pod",
   204  					Name:       "foo",
   205  					Namespace:  "baz",
   206  					UID:        "bar",
   207  					APIVersion: "v1",
   208  				},
   209  				Reason:  "Killed",
   210  				Message: "some other verbose message: 1",
   211  				Source:  v1.EventSource{Component: "eventTest"},
   212  				Count:   1,
   213  				Type:    v1.EventTypeNormal,
   214  			},
   215  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"v1", ResourceVersion:"", FieldPath:""}): type: 'Normal' reason: 'Killed' some other verbose message: 1`,
   216  			expectUpdate: false,
   217  		},
   218  		{
   219  			obj:        testRef,
   220  			eventtype:  v1.EventTypeNormal,
   221  			reason:     "Started",
   222  			messageFmt: "some verbose message: %v",
   223  			elements:   []interface{}{1},
   224  			expect: &v1.Event{
   225  				ObjectMeta: metav1.ObjectMeta{
   226  					Name:      "foo",
   227  					Namespace: "baz",
   228  				},
   229  				InvolvedObject: v1.ObjectReference{
   230  					Kind:       "Pod",
   231  					Name:       "foo",
   232  					Namespace:  "baz",
   233  					UID:        "bar",
   234  					APIVersion: "v1",
   235  					FieldPath:  "spec.containers[2]",
   236  				},
   237  				Reason:  "Started",
   238  				Message: "some verbose message: 1",
   239  				Source:  v1.EventSource{Component: "eventTest"},
   240  				Count:   2,
   241  				Type:    v1.EventTypeNormal,
   242  			},
   243  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
   244  			expectUpdate: true,
   245  		},
   246  		{
   247  			obj:        testRef2,
   248  			eventtype:  v1.EventTypeNormal,
   249  			reason:     "Started",
   250  			messageFmt: "some verbose message: %v",
   251  			elements:   []interface{}{1},
   252  			expect: &v1.Event{
   253  				ObjectMeta: metav1.ObjectMeta{
   254  					Name:      "foo",
   255  					Namespace: "baz",
   256  				},
   257  				InvolvedObject: v1.ObjectReference{
   258  					Kind:       "Pod",
   259  					Name:       "foo",
   260  					Namespace:  "baz",
   261  					UID:        "differentUid",
   262  					APIVersion: "v1",
   263  					FieldPath:  "spec.containers[3]",
   264  				},
   265  				Reason:  "Started",
   266  				Message: "some verbose message: 1",
   267  				Source:  v1.EventSource{Component: "eventTest"},
   268  				Count:   1,
   269  				Type:    v1.EventTypeNormal,
   270  			},
   271  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
   272  			expectUpdate: false,
   273  		},
   274  		{
   275  			obj:        testRef,
   276  			eventtype:  v1.EventTypeNormal,
   277  			reason:     "Started",
   278  			messageFmt: "some verbose message: %v",
   279  			elements:   []interface{}{1},
   280  			expect: &v1.Event{
   281  				ObjectMeta: metav1.ObjectMeta{
   282  					Name:      "foo",
   283  					Namespace: "baz",
   284  				},
   285  				InvolvedObject: v1.ObjectReference{
   286  					Kind:       "Pod",
   287  					Name:       "foo",
   288  					Namespace:  "baz",
   289  					UID:        "bar",
   290  					APIVersion: "v1",
   291  					FieldPath:  "spec.containers[2]",
   292  				},
   293  				Reason:  "Started",
   294  				Message: "some verbose message: 1",
   295  				Source:  v1.EventSource{Component: "eventTest"},
   296  				Count:   3,
   297  				Type:    v1.EventTypeNormal,
   298  			},
   299  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
   300  			expectUpdate: true,
   301  		},
   302  		{
   303  			obj:        testRef2,
   304  			eventtype:  v1.EventTypeNormal,
   305  			reason:     "Stopped",
   306  			messageFmt: "some verbose message: %v",
   307  			elements:   []interface{}{1},
   308  			expect: &v1.Event{
   309  				ObjectMeta: metav1.ObjectMeta{
   310  					Name:      "foo",
   311  					Namespace: "baz",
   312  				},
   313  				InvolvedObject: v1.ObjectReference{
   314  					Kind:       "Pod",
   315  					Name:       "foo",
   316  					Namespace:  "baz",
   317  					UID:        "differentUid",
   318  					APIVersion: "v1",
   319  					FieldPath:  "spec.containers[3]",
   320  				},
   321  				Reason:  "Stopped",
   322  				Message: "some verbose message: 1",
   323  				Source:  v1.EventSource{Component: "eventTest"},
   324  				Count:   1,
   325  				Type:    v1.EventTypeNormal,
   326  			},
   327  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`,
   328  			expectUpdate: false,
   329  		},
   330  		{
   331  			obj:        testRef2,
   332  			eventtype:  v1.EventTypeNormal,
   333  			reason:     "Stopped",
   334  			messageFmt: "some verbose message: %v",
   335  			elements:   []interface{}{1},
   336  			expect: &v1.Event{
   337  				ObjectMeta: metav1.ObjectMeta{
   338  					Name:      "foo",
   339  					Namespace: "baz",
   340  				},
   341  				InvolvedObject: v1.ObjectReference{
   342  					Kind:       "Pod",
   343  					Name:       "foo",
   344  					Namespace:  "baz",
   345  					UID:        "differentUid",
   346  					APIVersion: "v1",
   347  					FieldPath:  "spec.containers[3]",
   348  				},
   349  				Reason:  "Stopped",
   350  				Message: "some verbose message: 1",
   351  				Source:  v1.EventSource{Component: "eventTest"},
   352  				Count:   2,
   353  				Type:    v1.EventTypeNormal,
   354  			},
   355  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`,
   356  			expectUpdate: true,
   357  		},
   358  	}
   359  
   360  	testCache := map[string]*v1.Event{}
   361  	logCalled := make(chan struct{})
   362  	createEvent := make(chan *v1.Event)
   363  	updateEvent := make(chan *v1.Event)
   364  	patchEvent := make(chan *v1.Event)
   365  	testEvents := testEventSink{
   366  		OnCreate: OnCreateFactory(testCache, createEvent),
   367  		OnUpdate: func(event *v1.Event) (*v1.Event, error) {
   368  			updateEvent <- event
   369  			return event, nil
   370  		},
   371  		OnPatch: OnPatchFactory(testCache, patchEvent),
   372  	}
   373  	eventBroadcaster := NewBroadcasterForTests(0)
   374  	sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents)
   375  
   376  	clock := clock.NewFakeClock(time.Now())
   377  	recorder := recorderWithFakeClock(v1.EventSource{Component: "eventTest"}, eventBroadcaster, clock)
   378  	for index, item := range table {
   379  		clock.Step(1 * time.Second)
   380  		logWatcher := eventBroadcaster.StartLogging(func(formatter string, args ...interface{}) {
   381  			if e, a := item.expectLog, fmt.Sprintf(formatter, args...); e != a {
   382  				t.Errorf("Expected '%v', got '%v'", e, a)
   383  			}
   384  			logCalled <- struct{}{}
   385  		})
   386  		recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...)
   387  
   388  		<-logCalled
   389  
   390  		// validate event
   391  		if item.expectUpdate {
   392  			actualEvent := <-patchEvent
   393  			validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
   394  		} else {
   395  			actualEvent := <-createEvent
   396  			validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
   397  		}
   398  		logWatcher.Stop()
   399  	}
   400  	sinkWatcher.Stop()
   401  }
   402  
   403  func recorderWithFakeClock(eventSource v1.EventSource, eventBroadcaster EventBroadcaster, clock clock.Clock) EventRecorder {
   404  	return &recorderImpl{scheme.Scheme, eventSource, eventBroadcaster.(*eventBroadcasterImpl).Broadcaster, clock}
   405  }
   406  
   407  func TestWriteEventError(t *testing.T) {
   408  	type entry struct {
   409  		timesToSendError int
   410  		attemptsWanted   int
   411  		err              error
   412  	}
   413  	table := map[string]*entry{
   414  		"giveUp1": {
   415  			timesToSendError: 1000,
   416  			attemptsWanted:   1,
   417  			err:              &restclient.RequestConstructionError{},
   418  		},
   419  		"giveUp2": {
   420  			timesToSendError: 1000,
   421  			attemptsWanted:   1,
   422  			err:              &errors.StatusError{},
   423  		},
   424  		"retry1": {
   425  			timesToSendError: 1000,
   426  			attemptsWanted:   12,
   427  			err:              &errors.UnexpectedObjectError{},
   428  		},
   429  		"retry2": {
   430  			timesToSendError: 1000,
   431  			attemptsWanted:   12,
   432  			err:              fmt.Errorf("A weird error"),
   433  		},
   434  		"succeedEventually": {
   435  			timesToSendError: 2,
   436  			attemptsWanted:   2,
   437  			err:              fmt.Errorf("A weird error"),
   438  		},
   439  	}
   440  
   441  	clock := clock.IntervalClock{Time: time.Now(), Duration: time.Second}
   442  	eventCorrelator := NewEventCorrelator(&clock)
   443  
   444  	for caseName, ent := range table {
   445  		attempts := 0
   446  		sink := &testEventSink{
   447  			OnCreate: func(event *v1.Event) (*v1.Event, error) {
   448  				attempts++
   449  				if attempts < ent.timesToSendError {
   450  					return nil, ent.err
   451  				}
   452  				return event, nil
   453  			},
   454  		}
   455  		ev := &v1.Event{}
   456  		recordToSink(sink, ev, eventCorrelator, 0)
   457  		if attempts != ent.attemptsWanted {
   458  			t.Errorf("case %v: wanted %d, got %d attempts", caseName, ent.attemptsWanted, attempts)
   459  		}
   460  	}
   461  }
   462  
   463  func TestUpdateExpiredEvent(t *testing.T) {
   464  	clock := clock.IntervalClock{Time: time.Now(), Duration: time.Second}
   465  	eventCorrelator := NewEventCorrelator(&clock)
   466  
   467  	var createdEvent *v1.Event
   468  
   469  	sink := &testEventSink{
   470  		OnPatch: func(*v1.Event, []byte) (*v1.Event, error) {
   471  			return nil, &errors.StatusError{
   472  				ErrStatus: metav1.Status{
   473  					Code:   http.StatusNotFound,
   474  					Reason: metav1.StatusReasonNotFound,
   475  				}}
   476  		},
   477  		OnCreate: func(event *v1.Event) (*v1.Event, error) {
   478  			createdEvent = event
   479  			return event, nil
   480  		},
   481  	}
   482  
   483  	ev := &v1.Event{}
   484  	ev.ResourceVersion = "updated-resource-version"
   485  	ev.Count = 2
   486  	recordToSink(sink, ev, eventCorrelator, 0)
   487  
   488  	if createdEvent == nil {
   489  		t.Error("Event did not get created after patch failed")
   490  		return
   491  	}
   492  
   493  	if createdEvent.ResourceVersion != "" {
   494  		t.Errorf("Event did not have its resource version cleared, was %s", createdEvent.ResourceVersion)
   495  	}
   496  }
   497  
   498  func TestLotsOfEvents(t *testing.T) {
   499  	recorderCalled := make(chan struct{})
   500  	loggerCalled := make(chan struct{})
   501  
   502  	// Fail each event a few times to ensure there's some load on the tested code.
   503  	var counts [1000]int
   504  	testEvents := testEventSink{
   505  		OnCreate: func(event *v1.Event) (*v1.Event, error) {
   506  			num, err := strconv.Atoi(event.Message)
   507  			if err != nil {
   508  				t.Error(err)
   509  				return event, nil
   510  			}
   511  			counts[num]++
   512  			if counts[num] < 5 {
   513  				return nil, fmt.Errorf("fake error")
   514  			}
   515  			recorderCalled <- struct{}{}
   516  			return event, nil
   517  		},
   518  	}
   519  
   520  	eventBroadcaster := NewBroadcasterForTests(0)
   521  	sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents)
   522  	logWatcher := eventBroadcaster.StartLogging(func(formatter string, args ...interface{}) {
   523  		loggerCalled <- struct{}{}
   524  	})
   525  	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "eventTest"})
   526  	for i := 0; i < maxQueuedEvents; i++ {
   527  		// we want a unique object to stop spam filtering
   528  		ref := &v1.ObjectReference{
   529  			Kind:       "Pod",
   530  			Name:       fmt.Sprintf("foo-%v", i),
   531  			Namespace:  "baz",
   532  			UID:        "bar",
   533  			APIVersion: "version",
   534  		}
   535  		// we need to vary the reason to prevent aggregation
   536  		go recorder.Eventf(ref, v1.EventTypeNormal, "Reason-"+strconv.Itoa(i), strconv.Itoa(i))
   537  	}
   538  	// Make sure no events were dropped by either of the listeners.
   539  	for i := 0; i < maxQueuedEvents; i++ {
   540  		<-recorderCalled
   541  		<-loggerCalled
   542  	}
   543  	// Make sure that every event was attempted 5 times
   544  	for i := 0; i < maxQueuedEvents; i++ {
   545  		if counts[i] < 5 {
   546  			t.Errorf("Only attempted to record event '%d' %d times.", i, counts[i])
   547  		}
   548  	}
   549  	sinkWatcher.Stop()
   550  	logWatcher.Stop()
   551  }
   552  
   553  func TestEventfNoNamespace(t *testing.T) {
   554  	testPod := &v1.Pod{
   555  		ObjectMeta: metav1.ObjectMeta{
   556  			SelfLink: "/api/v1/namespaces/default/pods/foo",
   557  			Name:     "foo",
   558  			UID:      "bar",
   559  		},
   560  	}
   561  	testRef, err := ref.GetPartialReference(scheme.Scheme, testPod, "spec.containers[2]")
   562  	if err != nil {
   563  		t.Fatal(err)
   564  	}
   565  	table := []struct {
   566  		obj          k8sruntime.Object
   567  		eventtype    string
   568  		reason       string
   569  		messageFmt   string
   570  		elements     []interface{}
   571  		expect       *v1.Event
   572  		expectLog    string
   573  		expectUpdate bool
   574  	}{
   575  		{
   576  			obj:        testRef,
   577  			eventtype:  v1.EventTypeNormal,
   578  			reason:     "Started",
   579  			messageFmt: "some verbose message: %v",
   580  			elements:   []interface{}{1},
   581  			expect: &v1.Event{
   582  				ObjectMeta: metav1.ObjectMeta{
   583  					Name:      "foo",
   584  					Namespace: "default",
   585  				},
   586  				InvolvedObject: v1.ObjectReference{
   587  					Kind:       "Pod",
   588  					Name:       "foo",
   589  					Namespace:  "",
   590  					UID:        "bar",
   591  					APIVersion: "v1",
   592  					FieldPath:  "spec.containers[2]",
   593  				},
   594  				Reason:  "Started",
   595  				Message: "some verbose message: 1",
   596  				Source:  v1.EventSource{Component: "eventTest"},
   597  				Count:   1,
   598  				Type:    v1.EventTypeNormal,
   599  			},
   600  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"", Name:"foo", UID:"bar", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
   601  			expectUpdate: false,
   602  		},
   603  	}
   604  
   605  	testCache := map[string]*v1.Event{}
   606  	logCalled := make(chan struct{})
   607  	createEvent := make(chan *v1.Event)
   608  	updateEvent := make(chan *v1.Event)
   609  	patchEvent := make(chan *v1.Event)
   610  	testEvents := testEventSink{
   611  		OnCreate: OnCreateFactory(testCache, createEvent),
   612  		OnUpdate: func(event *v1.Event) (*v1.Event, error) {
   613  			updateEvent <- event
   614  			return event, nil
   615  		},
   616  		OnPatch: OnPatchFactory(testCache, patchEvent),
   617  	}
   618  	eventBroadcaster := NewBroadcasterForTests(0)
   619  	sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents)
   620  
   621  	clock := clock.NewFakeClock(time.Now())
   622  	recorder := recorderWithFakeClock(v1.EventSource{Component: "eventTest"}, eventBroadcaster, clock)
   623  
   624  	for index, item := range table {
   625  		clock.Step(1 * time.Second)
   626  		logWatcher := eventBroadcaster.StartLogging(func(formatter string, args ...interface{}) {
   627  			if e, a := item.expectLog, fmt.Sprintf(formatter, args...); e != a {
   628  				t.Errorf("Expected '%v', got '%v'", e, a)
   629  			}
   630  			logCalled <- struct{}{}
   631  		})
   632  		recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...)
   633  
   634  		<-logCalled
   635  
   636  		// validate event
   637  		if item.expectUpdate {
   638  			actualEvent := <-patchEvent
   639  			validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
   640  		} else {
   641  			actualEvent := <-createEvent
   642  			validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
   643  		}
   644  
   645  		logWatcher.Stop()
   646  	}
   647  	sinkWatcher.Stop()
   648  }
   649  
   650  func TestMultiSinkCache(t *testing.T) {
   651  	testPod := &v1.Pod{
   652  		ObjectMeta: metav1.ObjectMeta{
   653  			SelfLink:  "/api/v1/namespaces/baz/pods/foo",
   654  			Name:      "foo",
   655  			Namespace: "baz",
   656  			UID:       "bar",
   657  		},
   658  	}
   659  	testPod2 := &v1.Pod{
   660  		ObjectMeta: metav1.ObjectMeta{
   661  			SelfLink:  "/api/v1/namespaces/baz/pods/foo",
   662  			Name:      "foo",
   663  			Namespace: "baz",
   664  			UID:       "differentUid",
   665  		},
   666  	}
   667  	testRef, err := ref.GetPartialReference(scheme.Scheme, testPod, "spec.containers[2]")
   668  	if err != nil {
   669  		t.Fatal(err)
   670  	}
   671  	testRef2, err := ref.GetPartialReference(scheme.Scheme, testPod2, "spec.containers[3]")
   672  	if err != nil {
   673  		t.Fatal(err)
   674  	}
   675  	table := []struct {
   676  		obj          k8sruntime.Object
   677  		eventtype    string
   678  		reason       string
   679  		messageFmt   string
   680  		elements     []interface{}
   681  		expect       *v1.Event
   682  		expectLog    string
   683  		expectUpdate bool
   684  	}{
   685  		{
   686  			obj:        testRef,
   687  			eventtype:  v1.EventTypeNormal,
   688  			reason:     "Started",
   689  			messageFmt: "some verbose message: %v",
   690  			elements:   []interface{}{1},
   691  			expect: &v1.Event{
   692  				ObjectMeta: metav1.ObjectMeta{
   693  					Name:      "foo",
   694  					Namespace: "baz",
   695  				},
   696  				InvolvedObject: v1.ObjectReference{
   697  					Kind:       "Pod",
   698  					Name:       "foo",
   699  					Namespace:  "baz",
   700  					UID:        "bar",
   701  					APIVersion: "v1",
   702  					FieldPath:  "spec.containers[2]",
   703  				},
   704  				Reason:  "Started",
   705  				Message: "some verbose message: 1",
   706  				Source:  v1.EventSource{Component: "eventTest"},
   707  				Count:   1,
   708  				Type:    v1.EventTypeNormal,
   709  			},
   710  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
   711  			expectUpdate: false,
   712  		},
   713  		{
   714  			obj:        testPod,
   715  			eventtype:  v1.EventTypeNormal,
   716  			reason:     "Killed",
   717  			messageFmt: "some other verbose message: %v",
   718  			elements:   []interface{}{1},
   719  			expect: &v1.Event{
   720  				ObjectMeta: metav1.ObjectMeta{
   721  					Name:      "foo",
   722  					Namespace: "baz",
   723  				},
   724  				InvolvedObject: v1.ObjectReference{
   725  					Kind:       "Pod",
   726  					Name:       "foo",
   727  					Namespace:  "baz",
   728  					UID:        "bar",
   729  					APIVersion: "v1",
   730  				},
   731  				Reason:  "Killed",
   732  				Message: "some other verbose message: 1",
   733  				Source:  v1.EventSource{Component: "eventTest"},
   734  				Count:   1,
   735  				Type:    v1.EventTypeNormal,
   736  			},
   737  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"v1", ResourceVersion:"", FieldPath:""}): type: 'Normal' reason: 'Killed' some other verbose message: 1`,
   738  			expectUpdate: false,
   739  		},
   740  		{
   741  			obj:        testRef,
   742  			eventtype:  v1.EventTypeNormal,
   743  			reason:     "Started",
   744  			messageFmt: "some verbose message: %v",
   745  			elements:   []interface{}{1},
   746  			expect: &v1.Event{
   747  				ObjectMeta: metav1.ObjectMeta{
   748  					Name:      "foo",
   749  					Namespace: "baz",
   750  				},
   751  				InvolvedObject: v1.ObjectReference{
   752  					Kind:       "Pod",
   753  					Name:       "foo",
   754  					Namespace:  "baz",
   755  					UID:        "bar",
   756  					APIVersion: "v1",
   757  					FieldPath:  "spec.containers[2]",
   758  				},
   759  				Reason:  "Started",
   760  				Message: "some verbose message: 1",
   761  				Source:  v1.EventSource{Component: "eventTest"},
   762  				Count:   2,
   763  				Type:    v1.EventTypeNormal,
   764  			},
   765  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
   766  			expectUpdate: true,
   767  		},
   768  		{
   769  			obj:        testRef2,
   770  			eventtype:  v1.EventTypeNormal,
   771  			reason:     "Started",
   772  			messageFmt: "some verbose message: %v",
   773  			elements:   []interface{}{1},
   774  			expect: &v1.Event{
   775  				ObjectMeta: metav1.ObjectMeta{
   776  					Name:      "foo",
   777  					Namespace: "baz",
   778  				},
   779  				InvolvedObject: v1.ObjectReference{
   780  					Kind:       "Pod",
   781  					Name:       "foo",
   782  					Namespace:  "baz",
   783  					UID:        "differentUid",
   784  					APIVersion: "v1",
   785  					FieldPath:  "spec.containers[3]",
   786  				},
   787  				Reason:  "Started",
   788  				Message: "some verbose message: 1",
   789  				Source:  v1.EventSource{Component: "eventTest"},
   790  				Count:   1,
   791  				Type:    v1.EventTypeNormal,
   792  			},
   793  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
   794  			expectUpdate: false,
   795  		},
   796  		{
   797  			obj:        testRef,
   798  			eventtype:  v1.EventTypeNormal,
   799  			reason:     "Started",
   800  			messageFmt: "some verbose message: %v",
   801  			elements:   []interface{}{1},
   802  			expect: &v1.Event{
   803  				ObjectMeta: metav1.ObjectMeta{
   804  					Name:      "foo",
   805  					Namespace: "baz",
   806  				},
   807  				InvolvedObject: v1.ObjectReference{
   808  					Kind:       "Pod",
   809  					Name:       "foo",
   810  					Namespace:  "baz",
   811  					UID:        "bar",
   812  					APIVersion: "v1",
   813  					FieldPath:  "spec.containers[2]",
   814  				},
   815  				Reason:  "Started",
   816  				Message: "some verbose message: 1",
   817  				Source:  v1.EventSource{Component: "eventTest"},
   818  				Count:   3,
   819  				Type:    v1.EventTypeNormal,
   820  			},
   821  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
   822  			expectUpdate: true,
   823  		},
   824  		{
   825  			obj:        testRef2,
   826  			eventtype:  v1.EventTypeNormal,
   827  			reason:     "Stopped",
   828  			messageFmt: "some verbose message: %v",
   829  			elements:   []interface{}{1},
   830  			expect: &v1.Event{
   831  				ObjectMeta: metav1.ObjectMeta{
   832  					Name:      "foo",
   833  					Namespace: "baz",
   834  				},
   835  				InvolvedObject: v1.ObjectReference{
   836  					Kind:       "Pod",
   837  					Name:       "foo",
   838  					Namespace:  "baz",
   839  					UID:        "differentUid",
   840  					APIVersion: "v1",
   841  					FieldPath:  "spec.containers[3]",
   842  				},
   843  				Reason:  "Stopped",
   844  				Message: "some verbose message: 1",
   845  				Source:  v1.EventSource{Component: "eventTest"},
   846  				Count:   1,
   847  				Type:    v1.EventTypeNormal,
   848  			},
   849  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`,
   850  			expectUpdate: false,
   851  		},
   852  		{
   853  			obj:        testRef2,
   854  			eventtype:  v1.EventTypeNormal,
   855  			reason:     "Stopped",
   856  			messageFmt: "some verbose message: %v",
   857  			elements:   []interface{}{1},
   858  			expect: &v1.Event{
   859  				ObjectMeta: metav1.ObjectMeta{
   860  					Name:      "foo",
   861  					Namespace: "baz",
   862  				},
   863  				InvolvedObject: v1.ObjectReference{
   864  					Kind:       "Pod",
   865  					Name:       "foo",
   866  					Namespace:  "baz",
   867  					UID:        "differentUid",
   868  					APIVersion: "v1",
   869  					FieldPath:  "spec.containers[3]",
   870  				},
   871  				Reason:  "Stopped",
   872  				Message: "some verbose message: 1",
   873  				Source:  v1.EventSource{Component: "eventTest"},
   874  				Count:   2,
   875  				Type:    v1.EventTypeNormal,
   876  			},
   877  			expectLog:    `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"v1", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`,
   878  			expectUpdate: true,
   879  		},
   880  	}
   881  
   882  	testCache := map[string]*v1.Event{}
   883  	createEvent := make(chan *v1.Event)
   884  	updateEvent := make(chan *v1.Event)
   885  	patchEvent := make(chan *v1.Event)
   886  	testEvents := testEventSink{
   887  		OnCreate: OnCreateFactory(testCache, createEvent),
   888  		OnUpdate: func(event *v1.Event) (*v1.Event, error) {
   889  			updateEvent <- event
   890  			return event, nil
   891  		},
   892  		OnPatch: OnPatchFactory(testCache, patchEvent),
   893  	}
   894  
   895  	testCache2 := map[string]*v1.Event{}
   896  	createEvent2 := make(chan *v1.Event)
   897  	updateEvent2 := make(chan *v1.Event)
   898  	patchEvent2 := make(chan *v1.Event)
   899  	testEvents2 := testEventSink{
   900  		OnCreate: OnCreateFactory(testCache2, createEvent2),
   901  		OnUpdate: func(event *v1.Event) (*v1.Event, error) {
   902  			updateEvent2 <- event
   903  			return event, nil
   904  		},
   905  		OnPatch: OnPatchFactory(testCache2, patchEvent2),
   906  	}
   907  
   908  	eventBroadcaster := NewBroadcasterForTests(0)
   909  	clock := clock.NewFakeClock(time.Now())
   910  	recorder := recorderWithFakeClock(v1.EventSource{Component: "eventTest"}, eventBroadcaster, clock)
   911  
   912  	sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents)
   913  	for index, item := range table {
   914  		clock.Step(1 * time.Second)
   915  		recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...)
   916  
   917  		// validate event
   918  		if item.expectUpdate {
   919  			actualEvent := <-patchEvent
   920  			validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
   921  		} else {
   922  			actualEvent := <-createEvent
   923  			validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
   924  		}
   925  	}
   926  
   927  	// Another StartRecordingToSink call should start to record events with new clean cache.
   928  	sinkWatcher2 := eventBroadcaster.StartRecordingToSink(&testEvents2)
   929  	for index, item := range table {
   930  		clock.Step(1 * time.Second)
   931  		recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...)
   932  
   933  		// validate event
   934  		if item.expectUpdate {
   935  			actualEvent := <-patchEvent2
   936  			validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
   937  		} else {
   938  			actualEvent := <-createEvent2
   939  			validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
   940  		}
   941  	}
   942  
   943  	sinkWatcher.Stop()
   944  	sinkWatcher2.Stop()
   945  }