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  }