k8s.io/client-go@v0.22.2/tools/events/event_broadcaster.go (about) 1 /* 2 Copyright 2019 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 events 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "sync" 24 "time" 25 26 corev1 "k8s.io/api/core/v1" 27 eventsv1 "k8s.io/api/events/v1" 28 "k8s.io/apimachinery/pkg/api/errors" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/apimachinery/pkg/types" 32 "k8s.io/apimachinery/pkg/util/clock" 33 "k8s.io/apimachinery/pkg/util/json" 34 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 35 "k8s.io/apimachinery/pkg/util/strategicpatch" 36 "k8s.io/apimachinery/pkg/util/wait" 37 "k8s.io/apimachinery/pkg/watch" 38 clientset "k8s.io/client-go/kubernetes" 39 "k8s.io/client-go/kubernetes/scheme" 40 typedv1core "k8s.io/client-go/kubernetes/typed/core/v1" 41 typedeventsv1 "k8s.io/client-go/kubernetes/typed/events/v1" 42 restclient "k8s.io/client-go/rest" 43 "k8s.io/client-go/tools/record" 44 "k8s.io/client-go/tools/record/util" 45 "k8s.io/klog/v2" 46 ) 47 48 const ( 49 maxTriesPerEvent = 12 50 finishTime = 6 * time.Minute 51 refreshTime = 30 * time.Minute 52 maxQueuedEvents = 1000 53 ) 54 55 var defaultSleepDuration = 10 * time.Second 56 57 // TODO: validate impact of copying and investigate hashing 58 type eventKey struct { 59 action string 60 reason string 61 reportingController string 62 regarding corev1.ObjectReference 63 related corev1.ObjectReference 64 } 65 66 type eventBroadcasterImpl struct { 67 *watch.Broadcaster 68 mu sync.Mutex 69 eventCache map[eventKey]*eventsv1.Event 70 sleepDuration time.Duration 71 sink EventSink 72 } 73 74 // EventSinkImpl wraps EventsV1Interface to implement EventSink. 75 // TODO: this makes it easier for testing purpose and masks the logic of performing API calls. 76 // Note that rollbacking to raw clientset should also be transparent. 77 type EventSinkImpl struct { 78 Interface typedeventsv1.EventsV1Interface 79 } 80 81 // Create takes the representation of a event and creates it. Returns the server's representation of the event, and an error, if there is any. 82 func (e *EventSinkImpl) Create(event *eventsv1.Event) (*eventsv1.Event, error) { 83 if event.Namespace == "" { 84 return nil, fmt.Errorf("can't create an event with empty namespace") 85 } 86 return e.Interface.Events(event.Namespace).Create(context.TODO(), event, metav1.CreateOptions{}) 87 } 88 89 // Update takes the representation of a event and updates it. Returns the server's representation of the event, and an error, if there is any. 90 func (e *EventSinkImpl) Update(event *eventsv1.Event) (*eventsv1.Event, error) { 91 if event.Namespace == "" { 92 return nil, fmt.Errorf("can't update an event with empty namespace") 93 } 94 return e.Interface.Events(event.Namespace).Update(context.TODO(), event, metav1.UpdateOptions{}) 95 } 96 97 // Patch applies the patch and returns the patched event, and an error, if there is any. 98 func (e *EventSinkImpl) Patch(event *eventsv1.Event, data []byte) (*eventsv1.Event, error) { 99 if event.Namespace == "" { 100 return nil, fmt.Errorf("can't patch an event with empty namespace") 101 } 102 return e.Interface.Events(event.Namespace).Patch(context.TODO(), event.Name, types.StrategicMergePatchType, data, metav1.PatchOptions{}) 103 } 104 105 // NewBroadcaster Creates a new event broadcaster. 106 func NewBroadcaster(sink EventSink) EventBroadcaster { 107 return newBroadcaster(sink, defaultSleepDuration, map[eventKey]*eventsv1.Event{}) 108 } 109 110 // NewBroadcasterForTest Creates a new event broadcaster for test purposes. 111 func newBroadcaster(sink EventSink, sleepDuration time.Duration, eventCache map[eventKey]*eventsv1.Event) EventBroadcaster { 112 return &eventBroadcasterImpl{ 113 Broadcaster: watch.NewBroadcaster(maxQueuedEvents, watch.DropIfChannelFull), 114 eventCache: eventCache, 115 sleepDuration: sleepDuration, 116 sink: sink, 117 } 118 } 119 120 func (e *eventBroadcasterImpl) Shutdown() { 121 e.Broadcaster.Shutdown() 122 } 123 124 // refreshExistingEventSeries refresh events TTL 125 func (e *eventBroadcasterImpl) refreshExistingEventSeries() { 126 // TODO: Investigate whether lock contention won't be a problem 127 e.mu.Lock() 128 defer e.mu.Unlock() 129 for isomorphicKey, event := range e.eventCache { 130 if event.Series != nil { 131 if recordedEvent, retry := recordEvent(e.sink, event); !retry { 132 if recordedEvent != nil { 133 e.eventCache[isomorphicKey] = recordedEvent 134 } 135 } 136 } 137 } 138 } 139 140 // finishSeries checks if a series has ended and either: 141 // - write final count to the apiserver 142 // - delete a singleton event (i.e. series field is nil) from the cache 143 func (e *eventBroadcasterImpl) finishSeries() { 144 // TODO: Investigate whether lock contention won't be a problem 145 e.mu.Lock() 146 defer e.mu.Unlock() 147 for isomorphicKey, event := range e.eventCache { 148 eventSerie := event.Series 149 if eventSerie != nil { 150 if eventSerie.LastObservedTime.Time.Before(time.Now().Add(-finishTime)) { 151 if _, retry := recordEvent(e.sink, event); !retry { 152 delete(e.eventCache, isomorphicKey) 153 } 154 } 155 } else if event.EventTime.Time.Before(time.Now().Add(-finishTime)) { 156 delete(e.eventCache, isomorphicKey) 157 } 158 } 159 } 160 161 // NewRecorder returns an EventRecorder that records events with the given event source. 162 func (e *eventBroadcasterImpl) NewRecorder(scheme *runtime.Scheme, reportingController string) EventRecorder { 163 hostname, _ := os.Hostname() 164 reportingInstance := reportingController + "-" + hostname 165 return &recorderImpl{scheme, reportingController, reportingInstance, e.Broadcaster, clock.RealClock{}} 166 } 167 168 func (e *eventBroadcasterImpl) recordToSink(event *eventsv1.Event, clock clock.Clock) { 169 // Make a copy before modification, because there could be multiple listeners. 170 eventCopy := event.DeepCopy() 171 go func() { 172 evToRecord := func() *eventsv1.Event { 173 e.mu.Lock() 174 defer e.mu.Unlock() 175 eventKey := getKey(eventCopy) 176 isomorphicEvent, isIsomorphic := e.eventCache[eventKey] 177 if isIsomorphic { 178 if isomorphicEvent.Series != nil { 179 isomorphicEvent.Series.Count++ 180 isomorphicEvent.Series.LastObservedTime = metav1.MicroTime{Time: clock.Now()} 181 return nil 182 } 183 isomorphicEvent.Series = &eventsv1.EventSeries{ 184 Count: 1, 185 LastObservedTime: metav1.MicroTime{Time: clock.Now()}, 186 } 187 return isomorphicEvent 188 } 189 e.eventCache[eventKey] = eventCopy 190 return eventCopy 191 }() 192 if evToRecord != nil { 193 recordedEvent := e.attemptRecording(evToRecord) 194 if recordedEvent != nil { 195 recordedEventKey := getKey(recordedEvent) 196 e.mu.Lock() 197 defer e.mu.Unlock() 198 e.eventCache[recordedEventKey] = recordedEvent 199 } 200 } 201 }() 202 } 203 204 func (e *eventBroadcasterImpl) attemptRecording(event *eventsv1.Event) *eventsv1.Event { 205 tries := 0 206 for { 207 if recordedEvent, retry := recordEvent(e.sink, event); !retry { 208 return recordedEvent 209 } 210 tries++ 211 if tries >= maxTriesPerEvent { 212 klog.Errorf("Unable to write event '%#v' (retry limit exceeded!)", event) 213 return nil 214 } 215 // Randomize sleep so that various clients won't all be 216 // synced up if the master goes down. 217 time.Sleep(wait.Jitter(e.sleepDuration, 0.25)) 218 } 219 } 220 221 func recordEvent(sink EventSink, event *eventsv1.Event) (*eventsv1.Event, bool) { 222 var newEvent *eventsv1.Event 223 var err error 224 isEventSeries := event.Series != nil 225 if isEventSeries { 226 patch, patchBytesErr := createPatchBytesForSeries(event) 227 if patchBytesErr != nil { 228 klog.Errorf("Unable to calculate diff, no merge is possible: %v", patchBytesErr) 229 return nil, false 230 } 231 newEvent, err = sink.Patch(event, patch) 232 } 233 // Update can fail because the event may have been removed and it no longer exists. 234 if !isEventSeries || (isEventSeries && util.IsKeyNotFoundError(err)) { 235 // Making sure that ResourceVersion is empty on creation 236 event.ResourceVersion = "" 237 newEvent, err = sink.Create(event) 238 } 239 if err == nil { 240 return newEvent, false 241 } 242 // If we can't contact the server, then hold everything while we keep trying. 243 // Otherwise, something about the event is malformed and we should abandon it. 244 switch err.(type) { 245 case *restclient.RequestConstructionError: 246 // We will construct the request the same next time, so don't keep trying. 247 klog.Errorf("Unable to construct event '%#v': '%v' (will not retry!)", event, err) 248 return nil, false 249 case *errors.StatusError: 250 if errors.IsAlreadyExists(err) { 251 klog.V(5).Infof("Server rejected event '%#v': '%v' (will not retry!)", event, err) 252 } else { 253 klog.Errorf("Server rejected event '%#v': '%v' (will not retry!)", event, err) 254 } 255 return nil, false 256 case *errors.UnexpectedObjectError: 257 // We don't expect this; it implies the server's response didn't match a 258 // known pattern. Go ahead and retry. 259 default: 260 // This case includes actual http transport errors. Go ahead and retry. 261 } 262 klog.Errorf("Unable to write event: '%v' (may retry after sleeping)", err) 263 return nil, true 264 } 265 266 func createPatchBytesForSeries(event *eventsv1.Event) ([]byte, error) { 267 oldEvent := event.DeepCopy() 268 oldEvent.Series = nil 269 oldData, err := json.Marshal(oldEvent) 270 if err != nil { 271 return nil, err 272 } 273 newData, err := json.Marshal(event) 274 if err != nil { 275 return nil, err 276 } 277 return strategicpatch.CreateTwoWayMergePatch(oldData, newData, eventsv1.Event{}) 278 } 279 280 func getKey(event *eventsv1.Event) eventKey { 281 key := eventKey{ 282 action: event.Action, 283 reason: event.Reason, 284 reportingController: event.ReportingController, 285 regarding: event.Regarding, 286 } 287 if event.Related != nil { 288 key.related = *event.Related 289 } 290 return key 291 } 292 293 // StartEventWatcher starts sending events received from this EventBroadcaster to the given event handler function. 294 // The return value is used to stop recording 295 func (e *eventBroadcasterImpl) StartEventWatcher(eventHandler func(event runtime.Object)) func() { 296 watcher := e.Watch() 297 go func() { 298 defer utilruntime.HandleCrash() 299 for { 300 watchEvent, ok := <-watcher.ResultChan() 301 if !ok { 302 return 303 } 304 eventHandler(watchEvent.Object) 305 } 306 }() 307 return watcher.Stop 308 } 309 310 func (e *eventBroadcasterImpl) startRecordingEvents(stopCh <-chan struct{}) { 311 eventHandler := func(obj runtime.Object) { 312 event, ok := obj.(*eventsv1.Event) 313 if !ok { 314 klog.Errorf("unexpected type, expected eventsv1.Event") 315 return 316 } 317 e.recordToSink(event, clock.RealClock{}) 318 } 319 stopWatcher := e.StartEventWatcher(eventHandler) 320 go func() { 321 <-stopCh 322 stopWatcher() 323 }() 324 } 325 326 // StartRecordingToSink starts sending events received from the specified eventBroadcaster to the given sink. 327 func (e *eventBroadcasterImpl) StartRecordingToSink(stopCh <-chan struct{}) { 328 go wait.Until(e.refreshExistingEventSeries, refreshTime, stopCh) 329 go wait.Until(e.finishSeries, finishTime, stopCh) 330 e.startRecordingEvents(stopCh) 331 } 332 333 type eventBroadcasterAdapterImpl struct { 334 coreClient typedv1core.EventsGetter 335 coreBroadcaster record.EventBroadcaster 336 eventsv1Client typedeventsv1.EventsV1Interface 337 eventsv1Broadcaster EventBroadcaster 338 } 339 340 // NewEventBroadcasterAdapter creates a wrapper around new and legacy broadcasters to simplify 341 // migration of individual components to the new Event API. 342 func NewEventBroadcasterAdapter(client clientset.Interface) EventBroadcasterAdapter { 343 eventClient := &eventBroadcasterAdapterImpl{} 344 if _, err := client.Discovery().ServerResourcesForGroupVersion(eventsv1.SchemeGroupVersion.String()); err == nil { 345 eventClient.eventsv1Client = client.EventsV1() 346 eventClient.eventsv1Broadcaster = NewBroadcaster(&EventSinkImpl{Interface: eventClient.eventsv1Client}) 347 } 348 // Even though there can soon exist cases when coreBroadcaster won't really be needed, 349 // we create it unconditionally because its overhead is minor and will simplify using usage 350 // patterns of this library in all components. 351 eventClient.coreClient = client.CoreV1() 352 eventClient.coreBroadcaster = record.NewBroadcaster() 353 return eventClient 354 } 355 356 // StartRecordingToSink starts sending events received from the specified eventBroadcaster to the given sink. 357 func (e *eventBroadcasterAdapterImpl) StartRecordingToSink(stopCh <-chan struct{}) { 358 if e.eventsv1Broadcaster != nil && e.eventsv1Client != nil { 359 e.eventsv1Broadcaster.StartRecordingToSink(stopCh) 360 } 361 if e.coreBroadcaster != nil && e.coreClient != nil { 362 e.coreBroadcaster.StartRecordingToSink(&typedv1core.EventSinkImpl{Interface: e.coreClient.Events("")}) 363 } 364 } 365 366 func (e *eventBroadcasterAdapterImpl) NewRecorder(name string) EventRecorder { 367 if e.eventsv1Broadcaster != nil && e.eventsv1Client != nil { 368 return e.eventsv1Broadcaster.NewRecorder(scheme.Scheme, name) 369 } 370 return record.NewEventRecorderAdapter(e.DeprecatedNewLegacyRecorder(name)) 371 } 372 373 func (e *eventBroadcasterAdapterImpl) DeprecatedNewLegacyRecorder(name string) record.EventRecorder { 374 return e.coreBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: name}) 375 } 376 377 func (e *eventBroadcasterAdapterImpl) Shutdown() { 378 if e.coreBroadcaster != nil { 379 e.coreBroadcaster.Shutdown() 380 } 381 if e.eventsv1Broadcaster != nil { 382 e.eventsv1Broadcaster.Shutdown() 383 } 384 }