github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/lib/queueinformer/queueinformer.go (about) 1 package queueinformer 2 3 import ( 4 "context" 5 6 "github.com/pkg/errors" 7 "github.com/sirupsen/logrus" 8 "k8s.io/client-go/tools/cache" 9 "k8s.io/client-go/util/workqueue" 10 11 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubestate" 12 "github.com/operator-framework/operator-lifecycle-manager/pkg/metrics" 13 ) 14 15 // KeyFunc returns a key for the given object and a bool which is true if the key was 16 // successfully generated and false otherwise. 17 type KeyFunc func(obj interface{}) (string, bool) 18 19 // QueueInformer ties an informer to a queue in order to process events from the informer 20 // the informer watches objects of interest and adds objects to the queue for processing 21 // the syncHandler is called for all objects on the queue 22 type QueueInformer struct { 23 metrics.MetricsProvider 24 25 logger *logrus.Logger 26 queue workqueue.RateLimitingInterface 27 informer cache.SharedIndexInformer 28 indexer cache.Indexer 29 keyFunc KeyFunc 30 syncer kubestate.Syncer 31 } 32 33 // Sync invokes all registered sync handlers in the QueueInformer's chain 34 func (q *QueueInformer) Sync(ctx context.Context, event kubestate.ResourceEvent) error { 35 return q.syncer.Sync(ctx, event) 36 } 37 38 // Enqueue adds a key to the queue. If obj is a key already it gets added directly. 39 // Otherwise, the key is extracted via keyFunc. 40 func (q *QueueInformer) Enqueue(event kubestate.ResourceEvent) { 41 if event == nil { 42 // Don't enqueue nil events 43 return 44 } 45 46 resource := event.Resource() 47 if event.Type() == kubestate.ResourceDeleted { 48 // Get object from tombstone if possible 49 if tombstone, ok := resource.(cache.DeletedFinalStateUnknown); ok { 50 resource = tombstone 51 } 52 } else { 53 // Extract key for add and update events 54 if key, ok := q.key(resource); ok { 55 resource = key 56 } 57 } 58 59 // Create new resource event and add to queue 60 e := kubestate.NewResourceEvent(event.Type(), resource) 61 q.logger.WithField("event", e).Trace("enqueuing resource event") 62 q.queue.Add(e) 63 } 64 65 // key turns an object into a key for the indexer. 66 func (q *QueueInformer) key(obj interface{}) (string, bool) { 67 return q.keyFunc(obj) 68 } 69 70 // resourceHandlers provides the default implementation for responding to events 71 // these simply Log the event and add the object's key to the queue for later processing. 72 func (q *QueueInformer) resourceHandlers(ctx context.Context) *cache.ResourceEventHandlerFuncs { 73 return &cache.ResourceEventHandlerFuncs{ 74 AddFunc: func(obj interface{}) { 75 q.Enqueue(kubestate.NewResourceEvent(kubestate.ResourceUpdated, obj)) 76 }, 77 UpdateFunc: func(oldObj, newObj interface{}) { 78 q.Enqueue(kubestate.NewResourceEvent(kubestate.ResourceUpdated, newObj)) 79 }, 80 DeleteFunc: func(obj interface{}) { 81 q.Enqueue(kubestate.NewResourceEvent(kubestate.ResourceDeleted, obj)) 82 }, 83 } 84 } 85 86 // metricHandlers provides the default implementation for handling metrics in response to events. 87 func (q *QueueInformer) metricHandlers() *cache.ResourceEventHandlerFuncs { 88 return &cache.ResourceEventHandlerFuncs{ 89 AddFunc: func(obj interface{}) { 90 if err := q.HandleMetrics(); err != nil { 91 q.logger.WithError(err).WithField("key", obj).Warn("error handling metrics on add event") 92 } 93 }, 94 DeleteFunc: func(obj interface{}) { 95 if err := q.HandleMetrics(); err != nil { 96 q.logger.WithError(err).WithField("key", obj).Warn("error handling metrics on delete event") 97 } 98 }, 99 UpdateFunc: func(oldObj, newObj interface{}) { 100 if err := q.HandleMetrics(); err != nil { 101 q.logger.WithError(err).WithField("key", newObj).Warn("error handling metrics on update event") 102 } 103 }, 104 } 105 } 106 107 func NewQueue(ctx context.Context, options ...Option) (*QueueInformer, error) { 108 config := defaultConfig() 109 config.apply(options) 110 111 if err := config.validateQueue(); err != nil { 112 return nil, err 113 } 114 115 queue := &QueueInformer{ 116 MetricsProvider: config.provider, 117 logger: config.logger, 118 queue: config.queue, 119 keyFunc: config.keyFunc, 120 syncer: config.syncer, 121 } 122 123 return queue, nil 124 } 125 126 // NewQueueInformer returns a new QueueInformer configured with options. 127 func NewQueueInformer(ctx context.Context, options ...Option) (*QueueInformer, error) { 128 // Get default config and apply given options 129 config := defaultConfig() 130 config.apply(options) 131 config.complete() 132 133 return newQueueInformerFromConfig(ctx, config) 134 } 135 136 func newQueueInformerFromConfig(ctx context.Context, config *queueInformerConfig) (*QueueInformer, error) { 137 if err := config.validateQueueInformer(); err != nil { 138 return nil, err 139 } 140 141 // Extract config 142 queueInformer := &QueueInformer{ 143 MetricsProvider: config.provider, 144 logger: config.logger, 145 queue: config.queue, 146 indexer: config.indexer, 147 informer: config.informer, 148 keyFunc: config.keyFunc, 149 syncer: config.syncer, 150 } 151 152 // Register event handlers for resource and metrics 153 if queueInformer.informer != nil { 154 queueInformer.informer.AddEventHandler(queueInformer.resourceHandlers(ctx)) 155 queueInformer.informer.AddEventHandler(queueInformer.metricHandlers()) 156 } 157 158 return queueInformer, nil 159 } 160 161 // LegacySyncHandler is a deprecated signature for syncing resources. 162 type LegacySyncHandler func(obj interface{}) error 163 164 // ToSyncer returns the Syncer equivalent of the sync handler. 165 func (l LegacySyncHandler) ToSyncer() kubestate.Syncer { 166 return l.ToSyncerWithDelete(nil) 167 } 168 169 // ToSyncerWithDelete returns the Syncer equivalent of the given sync handler and delete function. 170 func (l LegacySyncHandler) ToSyncerWithDelete(onDelete func(obj interface{})) kubestate.Syncer { 171 var syncer kubestate.SyncFunc = func(ctx context.Context, event kubestate.ResourceEvent) error { 172 switch event.Type() { 173 case kubestate.ResourceDeleted: 174 if onDelete != nil { 175 onDelete(event.Resource()) 176 } 177 case kubestate.ResourceAdded: 178 // Added and updated are treated the same 179 fallthrough 180 case kubestate.ResourceUpdated: 181 return l(event.Resource()) 182 default: 183 return errors.Errorf("unexpected resource event type: %s", event.Type()) 184 } 185 186 return nil 187 } 188 189 return syncer 190 }