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 }