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 }