k8s.io/client-go@v0.22.2/tools/cache/controller.go (about)

     1  /*
     2  Copyright 2015 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 cache
    18  
    19  import (
    20  	"sync"
    21  	"time"
    22  
    23  	"k8s.io/apimachinery/pkg/runtime"
    24  	"k8s.io/apimachinery/pkg/util/clock"
    25  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    26  	"k8s.io/apimachinery/pkg/util/wait"
    27  )
    28  
    29  // This file implements a low-level controller that is used in
    30  // sharedIndexInformer, which is an implementation of
    31  // SharedIndexInformer.  Such informers, in turn, are key components
    32  // in the high level controllers that form the backbone of the
    33  // Kubernetes control plane.  Look at those for examples, or the
    34  // example in
    35  // https://github.com/kubernetes/client-go/tree/master/examples/workqueue
    36  // .
    37  
    38  // Config contains all the settings for one of these low-level controllers.
    39  type Config struct {
    40  	// The queue for your objects - has to be a DeltaFIFO due to
    41  	// assumptions in the implementation. Your Process() function
    42  	// should accept the output of this Queue's Pop() method.
    43  	Queue
    44  
    45  	// Something that can list and watch your objects.
    46  	ListerWatcher
    47  
    48  	// Something that can process a popped Deltas.
    49  	Process ProcessFunc
    50  
    51  	// ObjectType is an example object of the type this controller is
    52  	// expected to handle.  Only the type needs to be right, except
    53  	// that when that is `unstructured.Unstructured` the object's
    54  	// `"apiVersion"` and `"kind"` must also be right.
    55  	ObjectType runtime.Object
    56  
    57  	// FullResyncPeriod is the period at which ShouldResync is considered.
    58  	FullResyncPeriod time.Duration
    59  
    60  	// ShouldResync is periodically used by the reflector to determine
    61  	// whether to Resync the Queue. If ShouldResync is `nil` or
    62  	// returns true, it means the reflector should proceed with the
    63  	// resync.
    64  	ShouldResync ShouldResyncFunc
    65  
    66  	// If true, when Process() returns an error, re-enqueue the object.
    67  	// TODO: add interface to let you inject a delay/backoff or drop
    68  	//       the object completely if desired. Pass the object in
    69  	//       question to this interface as a parameter.  This is probably moot
    70  	//       now that this functionality appears at a higher level.
    71  	RetryOnError bool
    72  
    73  	// Called whenever the ListAndWatch drops the connection with an error.
    74  	WatchErrorHandler WatchErrorHandler
    75  
    76  	// WatchListPageSize is the requested chunk size of initial and relist watch lists.
    77  	WatchListPageSize int64
    78  }
    79  
    80  // ShouldResyncFunc is a type of function that indicates if a reflector should perform a
    81  // resync or not. It can be used by a shared informer to support multiple event handlers with custom
    82  // resync periods.
    83  type ShouldResyncFunc func() bool
    84  
    85  // ProcessFunc processes a single object.
    86  type ProcessFunc func(obj interface{}) error
    87  
    88  // `*controller` implements Controller
    89  type controller struct {
    90  	config         Config
    91  	reflector      *Reflector
    92  	reflectorMutex sync.RWMutex
    93  	clock          clock.Clock
    94  }
    95  
    96  // Controller is a low-level controller that is parameterized by a
    97  // Config and used in sharedIndexInformer.
    98  type Controller interface {
    99  	// Run does two things.  One is to construct and run a Reflector
   100  	// to pump objects/notifications from the Config's ListerWatcher
   101  	// to the Config's Queue and possibly invoke the occasional Resync
   102  	// on that Queue.  The other is to repeatedly Pop from the Queue
   103  	// and process with the Config's ProcessFunc.  Both of these
   104  	// continue until `stopCh` is closed.
   105  	Run(stopCh <-chan struct{})
   106  
   107  	// HasSynced delegates to the Config's Queue
   108  	HasSynced() bool
   109  
   110  	// LastSyncResourceVersion delegates to the Reflector when there
   111  	// is one, otherwise returns the empty string
   112  	LastSyncResourceVersion() string
   113  }
   114  
   115  // New makes a new Controller from the given Config.
   116  func New(c *Config) Controller {
   117  	ctlr := &controller{
   118  		config: *c,
   119  		clock:  &clock.RealClock{},
   120  	}
   121  	return ctlr
   122  }
   123  
   124  // Run begins processing items, and will continue until a value is sent down stopCh or it is closed.
   125  // It's an error to call Run more than once.
   126  // Run blocks; call via go.
   127  func (c *controller) Run(stopCh <-chan struct{}) {
   128  	defer utilruntime.HandleCrash()
   129  	go func() {
   130  		<-stopCh
   131  		c.config.Queue.Close()
   132  	}()
   133  	r := NewReflector(
   134  		c.config.ListerWatcher,
   135  		c.config.ObjectType,
   136  		c.config.Queue,
   137  		c.config.FullResyncPeriod,
   138  	)
   139  	r.ShouldResync = c.config.ShouldResync
   140  	r.WatchListPageSize = c.config.WatchListPageSize
   141  	r.clock = c.clock
   142  	if c.config.WatchErrorHandler != nil {
   143  		r.watchErrorHandler = c.config.WatchErrorHandler
   144  	}
   145  
   146  	c.reflectorMutex.Lock()
   147  	c.reflector = r
   148  	c.reflectorMutex.Unlock()
   149  
   150  	var wg wait.Group
   151  
   152  	wg.StartWithChannel(stopCh, r.Run)
   153  
   154  	wait.Until(c.processLoop, time.Second, stopCh)
   155  	wg.Wait()
   156  }
   157  
   158  // Returns true once this controller has completed an initial resource listing
   159  func (c *controller) HasSynced() bool {
   160  	return c.config.Queue.HasSynced()
   161  }
   162  
   163  func (c *controller) LastSyncResourceVersion() string {
   164  	c.reflectorMutex.RLock()
   165  	defer c.reflectorMutex.RUnlock()
   166  	if c.reflector == nil {
   167  		return ""
   168  	}
   169  	return c.reflector.LastSyncResourceVersion()
   170  }
   171  
   172  // processLoop drains the work queue.
   173  // TODO: Consider doing the processing in parallel. This will require a little thought
   174  // to make sure that we don't end up processing the same object multiple times
   175  // concurrently.
   176  //
   177  // TODO: Plumb through the stopCh here (and down to the queue) so that this can
   178  // actually exit when the controller is stopped. Or just give up on this stuff
   179  // ever being stoppable. Converting this whole package to use Context would
   180  // also be helpful.
   181  func (c *controller) processLoop() {
   182  	for {
   183  		obj, err := c.config.Queue.Pop(PopProcessFunc(c.config.Process))
   184  		if err != nil {
   185  			if err == ErrFIFOClosed {
   186  				return
   187  			}
   188  			if c.config.RetryOnError {
   189  				// This is the safe way to re-enqueue.
   190  				c.config.Queue.AddIfNotPresent(obj)
   191  			}
   192  		}
   193  	}
   194  }
   195  
   196  // ResourceEventHandler can handle notifications for events that
   197  // happen to a resource. The events are informational only, so you
   198  // can't return an error.  The handlers MUST NOT modify the objects
   199  // received; this concerns not only the top level of structure but all
   200  // the data structures reachable from it.
   201  //  * OnAdd is called when an object is added.
   202  //  * OnUpdate is called when an object is modified. Note that oldObj is the
   203  //      last known state of the object-- it is possible that several changes
   204  //      were combined together, so you can't use this to see every single
   205  //      change. OnUpdate is also called when a re-list happens, and it will
   206  //      get called even if nothing changed. This is useful for periodically
   207  //      evaluating or syncing something.
   208  //  * OnDelete will get the final state of the item if it is known, otherwise
   209  //      it will get an object of type DeletedFinalStateUnknown. This can
   210  //      happen if the watch is closed and misses the delete event and we don't
   211  //      notice the deletion until the subsequent re-list.
   212  type ResourceEventHandler interface {
   213  	OnAdd(obj interface{})
   214  	OnUpdate(oldObj, newObj interface{})
   215  	OnDelete(obj interface{})
   216  }
   217  
   218  // ResourceEventHandlerFuncs is an adaptor to let you easily specify as many or
   219  // as few of the notification functions as you want while still implementing
   220  // ResourceEventHandler.  This adapter does not remove the prohibition against
   221  // modifying the objects.
   222  type ResourceEventHandlerFuncs struct {
   223  	AddFunc    func(obj interface{})
   224  	UpdateFunc func(oldObj, newObj interface{})
   225  	DeleteFunc func(obj interface{})
   226  }
   227  
   228  // OnAdd calls AddFunc if it's not nil.
   229  func (r ResourceEventHandlerFuncs) OnAdd(obj interface{}) {
   230  	if r.AddFunc != nil {
   231  		r.AddFunc(obj)
   232  	}
   233  }
   234  
   235  // OnUpdate calls UpdateFunc if it's not nil.
   236  func (r ResourceEventHandlerFuncs) OnUpdate(oldObj, newObj interface{}) {
   237  	if r.UpdateFunc != nil {
   238  		r.UpdateFunc(oldObj, newObj)
   239  	}
   240  }
   241  
   242  // OnDelete calls DeleteFunc if it's not nil.
   243  func (r ResourceEventHandlerFuncs) OnDelete(obj interface{}) {
   244  	if r.DeleteFunc != nil {
   245  		r.DeleteFunc(obj)
   246  	}
   247  }
   248  
   249  // FilteringResourceEventHandler applies the provided filter to all events coming
   250  // in, ensuring the appropriate nested handler method is invoked. An object
   251  // that starts passing the filter after an update is considered an add, and an
   252  // object that stops passing the filter after an update is considered a delete.
   253  // Like the handlers, the filter MUST NOT modify the objects it is given.
   254  type FilteringResourceEventHandler struct {
   255  	FilterFunc func(obj interface{}) bool
   256  	Handler    ResourceEventHandler
   257  }
   258  
   259  // OnAdd calls the nested handler only if the filter succeeds
   260  func (r FilteringResourceEventHandler) OnAdd(obj interface{}) {
   261  	if !r.FilterFunc(obj) {
   262  		return
   263  	}
   264  	r.Handler.OnAdd(obj)
   265  }
   266  
   267  // OnUpdate ensures the proper handler is called depending on whether the filter matches
   268  func (r FilteringResourceEventHandler) OnUpdate(oldObj, newObj interface{}) {
   269  	newer := r.FilterFunc(newObj)
   270  	older := r.FilterFunc(oldObj)
   271  	switch {
   272  	case newer && older:
   273  		r.Handler.OnUpdate(oldObj, newObj)
   274  	case newer && !older:
   275  		r.Handler.OnAdd(newObj)
   276  	case !newer && older:
   277  		r.Handler.OnDelete(oldObj)
   278  	default:
   279  		// do nothing
   280  	}
   281  }
   282  
   283  // OnDelete calls the nested handler only if the filter succeeds
   284  func (r FilteringResourceEventHandler) OnDelete(obj interface{}) {
   285  	if !r.FilterFunc(obj) {
   286  		return
   287  	}
   288  	r.Handler.OnDelete(obj)
   289  }
   290  
   291  // DeletionHandlingMetaNamespaceKeyFunc checks for
   292  // DeletedFinalStateUnknown objects before calling
   293  // MetaNamespaceKeyFunc.
   294  func DeletionHandlingMetaNamespaceKeyFunc(obj interface{}) (string, error) {
   295  	if d, ok := obj.(DeletedFinalStateUnknown); ok {
   296  		return d.Key, nil
   297  	}
   298  	return MetaNamespaceKeyFunc(obj)
   299  }
   300  
   301  // NewInformer returns a Store and a controller for populating the store
   302  // while also providing event notifications. You should only used the returned
   303  // Store for Get/List operations; Add/Modify/Deletes will cause the event
   304  // notifications to be faulty.
   305  //
   306  // Parameters:
   307  //  * lw is list and watch functions for the source of the resource you want to
   308  //    be informed of.
   309  //  * objType is an object of the type that you expect to receive.
   310  //  * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
   311  //    calls, even if nothing changed). Otherwise, re-list will be delayed as
   312  //    long as possible (until the upstream source closes the watch or times out,
   313  //    or you stop the controller).
   314  //  * h is the object you want notifications sent to.
   315  //
   316  func NewInformer(
   317  	lw ListerWatcher,
   318  	objType runtime.Object,
   319  	resyncPeriod time.Duration,
   320  	h ResourceEventHandler,
   321  ) (Store, Controller) {
   322  	// This will hold the client state, as we know it.
   323  	clientState := NewStore(DeletionHandlingMetaNamespaceKeyFunc)
   324  
   325  	return clientState, newInformer(lw, objType, resyncPeriod, h, clientState)
   326  }
   327  
   328  // NewIndexerInformer returns an Indexer and a Controller for populating the index
   329  // while also providing event notifications. You should only used the returned
   330  // Index for Get/List operations; Add/Modify/Deletes will cause the event
   331  // notifications to be faulty.
   332  //
   333  // Parameters:
   334  //  * lw is list and watch functions for the source of the resource you want to
   335  //    be informed of.
   336  //  * objType is an object of the type that you expect to receive.
   337  //  * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
   338  //    calls, even if nothing changed). Otherwise, re-list will be delayed as
   339  //    long as possible (until the upstream source closes the watch or times out,
   340  //    or you stop the controller).
   341  //  * h is the object you want notifications sent to.
   342  //  * indexers is the indexer for the received object type.
   343  //
   344  func NewIndexerInformer(
   345  	lw ListerWatcher,
   346  	objType runtime.Object,
   347  	resyncPeriod time.Duration,
   348  	h ResourceEventHandler,
   349  	indexers Indexers,
   350  ) (Indexer, Controller) {
   351  	// This will hold the client state, as we know it.
   352  	clientState := NewIndexer(DeletionHandlingMetaNamespaceKeyFunc, indexers)
   353  
   354  	return clientState, newInformer(lw, objType, resyncPeriod, h, clientState)
   355  }
   356  
   357  // newInformer returns a controller for populating the store while also
   358  // providing event notifications.
   359  //
   360  // Parameters
   361  //  * lw is list and watch functions for the source of the resource you want to
   362  //    be informed of.
   363  //  * objType is an object of the type that you expect to receive.
   364  //  * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
   365  //    calls, even if nothing changed). Otherwise, re-list will be delayed as
   366  //    long as possible (until the upstream source closes the watch or times out,
   367  //    or you stop the controller).
   368  //  * h is the object you want notifications sent to.
   369  //  * clientState is the store you want to populate
   370  //
   371  func newInformer(
   372  	lw ListerWatcher,
   373  	objType runtime.Object,
   374  	resyncPeriod time.Duration,
   375  	h ResourceEventHandler,
   376  	clientState Store,
   377  ) Controller {
   378  	// This will hold incoming changes. Note how we pass clientState in as a
   379  	// KeyLister, that way resync operations will result in the correct set
   380  	// of update/delete deltas.
   381  	fifo := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   382  		KnownObjects:          clientState,
   383  		EmitDeltaTypeReplaced: true,
   384  	})
   385  
   386  	cfg := &Config{
   387  		Queue:            fifo,
   388  		ListerWatcher:    lw,
   389  		ObjectType:       objType,
   390  		FullResyncPeriod: resyncPeriod,
   391  		RetryOnError:     false,
   392  
   393  		Process: func(obj interface{}) error {
   394  			// from oldest to newest
   395  			for _, d := range obj.(Deltas) {
   396  				switch d.Type {
   397  				case Sync, Replaced, Added, Updated:
   398  					if old, exists, err := clientState.Get(d.Object); err == nil && exists {
   399  						if err := clientState.Update(d.Object); err != nil {
   400  							return err
   401  						}
   402  						h.OnUpdate(old, d.Object)
   403  					} else {
   404  						if err := clientState.Add(d.Object); err != nil {
   405  							return err
   406  						}
   407  						h.OnAdd(d.Object)
   408  					}
   409  				case Deleted:
   410  					if err := clientState.Delete(d.Object); err != nil {
   411  						return err
   412  					}
   413  					h.OnDelete(d.Object)
   414  				}
   415  			}
   416  			return nil
   417  		},
   418  	}
   419  	return New(cfg)
   420  }