github.com/cilium/cilium@v1.16.2/pkg/k8s/informer/informer.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package informer
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"net/http"
    10  
    11  	k8sRuntime "k8s.io/apimachinery/pkg/runtime"
    12  	utilRuntime "k8s.io/apimachinery/pkg/util/runtime"
    13  	"k8s.io/client-go/tools/cache"
    14  
    15  	"github.com/cilium/cilium/pkg/logging"
    16  	"github.com/cilium/cilium/pkg/logging/logfields"
    17  	"github.com/cilium/cilium/pkg/time"
    18  )
    19  
    20  var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "k8s")
    21  
    22  func init() {
    23  	utilRuntime.PanicHandlers = append(
    24  		utilRuntime.PanicHandlers,
    25  		func(r interface{}) {
    26  			// from k8s library
    27  			if err, ok := r.(error); ok && errors.Is(err, http.ErrAbortHandler) {
    28  				// honor the http.ErrAbortHandler sentinel panic value:
    29  				//   ErrAbortHandler is a sentinel panic value to abort a handler.
    30  				//   While any panic from ServeHTTP aborts the response to the client,
    31  				//   panicking with ErrAbortHandler also suppresses logging of a stack trace to the server's error log.
    32  				return
    33  			}
    34  			log.Fatal("Panic in Kubernetes runtime handler")
    35  		},
    36  	)
    37  }
    38  
    39  type privateRunner struct {
    40  	cache.Controller
    41  	cacheMutationDetector cache.MutationDetector
    42  }
    43  
    44  func (p *privateRunner) Run(stopCh <-chan struct{}) {
    45  	go p.cacheMutationDetector.Run(stopCh)
    46  	p.Controller.Run(stopCh)
    47  }
    48  
    49  // NewInformer is a copy of k8s.io/client-go/tools/cache/NewInformer includes the default cache MutationDetector.
    50  func NewInformer(
    51  	lw cache.ListerWatcher,
    52  	objType k8sRuntime.Object,
    53  	resyncPeriod time.Duration,
    54  	h cache.ResourceEventHandler,
    55  	transformer cache.TransformFunc,
    56  ) (cache.Store, cache.Controller) {
    57  	// This will hold the client state, as we know it.
    58  	clientState := cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc)
    59  
    60  	return clientState, NewInformerWithStore(lw, objType, resyncPeriod, h, transformer, clientState)
    61  }
    62  
    63  // NewIndexerInformer is a copy of k8s.io/client-go/tools/cache/NewIndexerInformer but includes the
    64  // default cache MutationDetector.
    65  func NewIndexerInformer(
    66  	lw cache.ListerWatcher,
    67  	objType k8sRuntime.Object,
    68  	resyncPeriod time.Duration,
    69  	h cache.ResourceEventHandler,
    70  	transformer cache.TransformFunc,
    71  	indexers cache.Indexers,
    72  ) (cache.Indexer, cache.Controller) {
    73  	clientState := cache.NewIndexer(cache.DeletionHandlingMetaNamespaceKeyFunc, indexers)
    74  	return clientState, NewInformerWithStore(lw, objType, resyncPeriod, h, transformer, clientState)
    75  }
    76  
    77  // NewInformerWithStore uses the same arguments as NewInformer for which a caller can also set a
    78  // cache.Store and includes the default cache MutationDetector.
    79  func NewInformerWithStore(
    80  	lw cache.ListerWatcher,
    81  	objType k8sRuntime.Object,
    82  	resyncPeriod time.Duration,
    83  	h cache.ResourceEventHandler,
    84  	transformer cache.TransformFunc,
    85  	clientState cache.Store,
    86  ) cache.Controller {
    87  
    88  	// This will hold incoming changes. Note how we pass clientState in as a
    89  	// KeyLister, that way resync operations will result in the correct set
    90  	// of update/delete deltas.
    91  	opts := cache.DeltaFIFOOptions{KeyFunction: cache.MetaNamespaceKeyFunc, KnownObjects: clientState}
    92  	fifo := cache.NewDeltaFIFOWithOptions(opts)
    93  
    94  	cacheMutationDetector := cache.NewCacheMutationDetector(fmt.Sprintf("%T", objType))
    95  
    96  	cfg := &cache.Config{
    97  		Queue:            fifo,
    98  		ListerWatcher:    lw,
    99  		ObjectType:       objType,
   100  		FullResyncPeriod: resyncPeriod,
   101  		RetryOnError:     false,
   102  
   103  		Process: func(obj interface{}, isInInitialList bool) error {
   104  			// from oldest to newest
   105  			for _, d := range obj.(cache.Deltas) {
   106  
   107  				var obj interface{}
   108  				if transformer != nil {
   109  					var err error
   110  					if obj, err = transformer(d.Object); err != nil {
   111  						return err
   112  					}
   113  				} else {
   114  					obj = d.Object
   115  				}
   116  
   117  				// In CI we detect if the objects were modified and panic
   118  				// this is a no-op in production environments.
   119  				cacheMutationDetector.AddObject(obj)
   120  
   121  				switch d.Type {
   122  				case cache.Sync, cache.Added, cache.Updated:
   123  					if old, exists, err := clientState.Get(obj); err == nil && exists {
   124  						if err := clientState.Update(obj); err != nil {
   125  							return err
   126  						}
   127  						h.OnUpdate(old, obj)
   128  					} else {
   129  						if err := clientState.Add(obj); err != nil {
   130  							return err
   131  						}
   132  						h.OnAdd(obj, isInInitialList)
   133  					}
   134  				case cache.Deleted:
   135  					if err := clientState.Delete(obj); err != nil {
   136  						return err
   137  					}
   138  					h.OnDelete(obj)
   139  				}
   140  			}
   141  			return nil
   142  		},
   143  	}
   144  	return &privateRunner{
   145  		Controller:            cache.New(cfg),
   146  		cacheMutationDetector: cacheMutationDetector,
   147  	}
   148  }