Change:
What happened: kube-scheduler panics when client-go tries to finish the existing event series, see: What you expected to happen: kube-scheduler should not panic How to reproduce it (as minimally and precisely as possible): Anything else we need to know?: Environment: Kubernetes version (use ): Cloud provider or hardware configuration: OS (e.g: ): Kernel (e.g. ): Install tools: Network plugin and version (if this is a network-related bug): Others: /kind bug /assign /priority critical-urgent /sig scheduling

Discussion:
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library", <ins> "//staging/src/k8s.io/client-go/rest:go_default_library", </ins> "//staging/src/k8s.io/client-go/tools/reference:go_default_library", ], )

for isomorphicKey, event := range e.eventCache { if event.Series != nil { if recordedEvent, retry := recordEvent(e.sink, event); !retry { <del> e.eventCache[isomorphicKey] = recordedEvent </del> <ins> if recordedEvent != nil { e.eventCache[isomorphicKey] = recordedEvent } </ins> } } }

k8sruntime "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes/scheme" <ins> restclient "k8s.io/client-go/rest" </ins> ref "k8s.io/client-go/tools/reference" )

t.Fatal(err) } LastObservedTime := metav1.MicroTime{Time: time.Now().Add(-9 * time.Minute)} createEvent := make(chan *v1beta1.Event, 10) updateEvent := make(chan *v1beta1.Event, 10) patchEvent := make(chan *v1beta1.Event, 10) <del> testEvents := testEventSeriesSink{ OnCreate: func(event *v1beta1.Event) (*v1beta1.Event, error) { createEvent <- event return event, nil }, OnUpdate: func(event *v1beta1.Event) (*v1beta1.Event, error) { updateEvent <- event return event, nil </del> <ins> table := []struct { patchFunc func(event *v1beta1.Event, patch []byte) (*v1beta1.Event, error) }{ { patchFunc: func(event *v1beta1.Event, patch []byte) (*v1beta1.Event, error) { // event we receive is already patched, usually the sink uses it //only to retrieve the name and namespace, here we'll use it directly. patchEvent <- event return event, nil }, </ins> }, <del> OnPatch: func(event *v1beta1.Event, patch []byte) (*v1beta1.Event, error) { // event we receive is already patched, usually the sink uses it //only to retrieve the name and namespace, here we'll use it directly. patchEvent <- event return event, nil </del> <ins> { patchFunc: func(event *v1beta1.Event, patch []byte) (*v1beta1.Event, error) { // we simulate an apiserver error here patchEvent <- nil return nil, &restclient.RequestConstructionError{} }, </ins> }, } <del> cache := map[eventKey]*v1beta1.Event{} eventBroadcaster := newBroadcaster(&testEvents, 0, cache).(*eventBroadcasterImpl) recorder := eventBroadcaster.NewRecorder(scheme.Scheme, "k8s.io/kube-foo").(*recorderImpl) cachedEvent := recorder.makeEvent(regarding, related, metav1.MicroTime{time.Now()}, v1.EventTypeNormal, "test", "some verbose message: 1", "eventTest", "eventTest-"+hostname, "started") cachedEvent.Series = &v1beta1.EventSeries{ Count:            10, LastObservedTime: LastObservedTime, } cacheKey := getKey(cachedEvent) cache[cacheKey] = cachedEvent eventBroadcaster.refreshExistingEventSeries() select { case <-patchEvent: t.Logf("validating event affected by patch request") eventBroadcaster.mu.Lock() defer eventBroadcaster.mu.Unlock() if len(cache) != 1 { t.Errorf("cache should be with same size, but instead got a size of %v", len(cache)) </del> <ins> for _, item := range table { testEvents := testEventSeriesSink{ OnCreate: func(event *v1beta1.Event) (*v1beta1.Event, error) { createEvent <- event return event, nil }, OnUpdate: func(event *v1beta1.Event) (*v1beta1.Event, error) { updateEvent <- event return event, nil }, OnPatch: item.patchFunc, </ins> } <del> // check that we emitted only one event if len(patchEvent) != 0 || len(createEvent) != 0 || len(updateEvent) != 0 { t.Errorf("exactly one event should be emitted, but got %v", len(patchEvent)) </del> <ins> cache := map[eventKey]*v1beta1.Event{} eventBroadcaster := newBroadcaster(&testEvents, 0, cache).(*eventBroadcasterImpl) recorder := eventBroadcaster.NewRecorder(scheme.Scheme, "k8s.io/kube-foo").(*recorderImpl) cachedEvent := recorder.makeEvent(regarding, related, metav1.MicroTime{time.Now()}, v1.EventTypeNormal, "test", "some verbose message: 1", "eventTest", "eventTest-"+hostname, "started") cachedEvent.Series = &v1beta1.EventSeries{ Count:            10, LastObservedTime: LastObservedTime, } cacheKey := getKey(cachedEvent) cache[cacheKey] = cachedEvent eventBroadcaster.refreshExistingEventSeries() select { case <-patchEvent: t.Logf("validating event affected by patch request") eventBroadcaster.mu.Lock() defer eventBroadcaster.mu.Unlock() if len(cache) != 1 { t.Errorf("cache should be with same size, but instead got a size of %v", len(cache)) } // check that we emitted only one event if len(patchEvent) != 0 || len(createEvent) != 0 || len(updateEvent) != 0 { t.Errorf("exactly one event should be emitted, but got %v", len(patchEvent)) } cacheEvent, exists := cache[cacheKey] if cacheEvent == nil || !exists { t.Errorf("expected event to exist and not being nil, but instead event: %v and exists: %v", cacheEvent, exists) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) </ins> } <del> case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) </del> } }