github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/container-collection/podinformer.go (about) 1 // Copyright 2017 The Kubernetes Authors. 2 // Copyright 2019-2022 The Inspektor Gadget 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 /* Code based on the official client-go example: 17 * https://github.com/kubernetes/client-go/blob/master/examples/workqueue/main.go 18 */ 19 20 package containercollection 21 22 import ( 23 "errors" 24 "sync" 25 "time" 26 27 log "github.com/sirupsen/logrus" 28 29 v1 "k8s.io/api/core/v1" 30 "k8s.io/apimachinery/pkg/fields" 31 "k8s.io/apimachinery/pkg/util/runtime" 32 "k8s.io/apimachinery/pkg/util/wait" 33 "k8s.io/client-go/kubernetes" 34 "k8s.io/client-go/rest" 35 "k8s.io/client-go/tools/cache" 36 "k8s.io/client-go/util/workqueue" 37 38 _ "k8s.io/client-go/plugin/pkg/client/auth" 39 ) 40 41 type PodInformer struct { 42 indexer cache.Indexer 43 queue workqueue.RateLimitingInterface 44 informer cache.Controller 45 46 stop chan struct{} 47 updatedPodChan chan *v1.Pod 48 deletedPodChan chan string 49 wg sync.WaitGroup 50 } 51 52 func NewPodInformer(node string) (*PodInformer, error) { 53 config, err := rest.InClusterConfig() 54 if err != nil { 55 return nil, err 56 } 57 clientset, err := kubernetes.NewForConfig(config) 58 if err != nil { 59 return nil, err 60 } 61 62 podListWatcher := cache.NewListWatchFromClient(clientset.CoreV1().RESTClient(), "pods", "", fields.OneTermEqualSelector("spec.nodeName", node)) 63 64 // creates the queue 65 queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()) 66 67 indexer, informer := cache.NewIndexerInformer(podListWatcher, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{ 68 AddFunc: func(obj interface{}) { 69 key, err := cache.MetaNamespaceKeyFunc(obj) 70 if err == nil { 71 queue.Add(key) 72 } 73 }, 74 UpdateFunc: func(old interface{}, new interface{}) { 75 key, err := cache.MetaNamespaceKeyFunc(new) 76 if err == nil { 77 queue.Add(key) 78 } 79 }, 80 DeleteFunc: func(obj interface{}) { 81 // IndexerInformer uses a delta queue, therefore for deletes we have to use this 82 // key function. 83 key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) 84 if err == nil { 85 queue.Add(key) 86 } 87 }, 88 }, cache.Indexers{}) 89 90 p := &PodInformer{ 91 indexer: indexer, 92 queue: queue, 93 informer: informer, 94 stop: make(chan struct{}), 95 updatedPodChan: make(chan *v1.Pod), 96 deletedPodChan: make(chan string), 97 } 98 99 // Now let's start the controller 100 go p.Run(1, p.stop) 101 102 return p, nil 103 } 104 105 func (p *PodInformer) Stop() { 106 // tell all workers to end 107 close(p.stop) 108 109 // wait for workers to end before closing channels to avoid 110 // writing to closed channels 111 p.wg.Wait() 112 113 close(p.updatedPodChan) 114 close(p.deletedPodChan) 115 } 116 117 func (p *PodInformer) UpdatedChan() <-chan *v1.Pod { 118 return p.updatedPodChan 119 } 120 121 func (p *PodInformer) DeletedChan() <-chan string { 122 return p.deletedPodChan 123 } 124 125 func (p *PodInformer) processNextItem() bool { 126 // Wait until there is a new item in the working queue 127 key, quit := p.queue.Get() 128 if quit { 129 return false 130 } 131 132 defer p.queue.Done(key) 133 134 p.notifyChans(key.(string)) 135 return true 136 } 137 138 // notifyChans passes the event to the channels configured by the user 139 func (p *PodInformer) notifyChans(key string) error { 140 obj, exists, err := p.indexer.GetByKey(key) 141 if err != nil { 142 log.Errorf("Fetching object with key %s from store failed with %v", key, err) 143 return err 144 } 145 defer p.queue.Forget(key) 146 147 if !exists { 148 p.deletedPodChan <- key 149 return nil 150 } 151 152 p.updatedPodChan <- obj.(*v1.Pod) 153 return nil 154 } 155 156 func (p *PodInformer) Run(threadiness int, stopCh chan struct{}) { 157 defer runtime.HandleCrash() 158 159 // Let the workers stop when we are done 160 defer p.queue.ShutDown() 161 log.Info("Starting Pod controller") 162 163 go p.informer.Run(stopCh) 164 165 // Wait for all involved caches to be synced, before processing items from the queue is started 166 if !cache.WaitForCacheSync(stopCh, p.informer.HasSynced) { 167 runtime.HandleError(errors.New("timed out waiting for caches to sync")) 168 return 169 } 170 171 for i := 0; i < threadiness; i++ { 172 p.wg.Add(1) 173 go wait.Until(p.runWorker, time.Second, stopCh) 174 } 175 176 <-stopCh 177 log.Info("Stopping Pod controller") 178 } 179 180 func (p *PodInformer) runWorker() { 181 defer p.wg.Done() 182 183 for p.processNextItem() { 184 } 185 }