github.com/kubewharf/katalyst-core@v0.5.3/pkg/controller/kcc/target/target_accessor.go (about) 1 /* 2 Copyright 2022 The Katalyst 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 target 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 apierrors "k8s.io/apimachinery/pkg/api/errors" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 27 "k8s.io/apimachinery/pkg/labels" 28 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 29 "k8s.io/apimachinery/pkg/util/wait" 30 "k8s.io/client-go/dynamic" 31 "k8s.io/client-go/dynamic/dynamicinformer" 32 "k8s.io/client-go/tools/cache" 33 "k8s.io/client-go/util/workqueue" 34 "k8s.io/klog/v2" 35 36 "github.com/kubewharf/katalyst-core/pkg/util/native" 37 ) 38 39 const ( 40 targetWorkerCount = 1 41 ) 42 43 // KatalystCustomConfigTargetAccessor is to handle creation/update/delete event of target unstructured obj, 44 // and it can trigger obj re-sync by calling Enqueue function 45 type KatalystCustomConfigTargetAccessor interface { 46 // Start to reconcile obj of kcc target type 47 Start() 48 49 // Stop reconcile obj of kcc target type 50 Stop() 51 52 // Enqueue obj of kcc target type to the work queue 53 Enqueue(name string, obj *unstructured.Unstructured) 54 55 // List all obj (with DeepCopy) of kcc target type according selector 56 List(selector labels.Selector) ([]*unstructured.Unstructured, error) 57 58 // Get obj (with DeepCopy) of kcc target type by namespace and name 59 Get(namespace, name string) (*unstructured.Unstructured, error) 60 } 61 62 type DummyKatalystCustomConfigTargetAccessor struct{} 63 64 func (d DummyKatalystCustomConfigTargetAccessor) Start() {} 65 func (d DummyKatalystCustomConfigTargetAccessor) Stop() {} 66 func (d DummyKatalystCustomConfigTargetAccessor) Enqueue(_ *unstructured.Unstructured) {} 67 func (d DummyKatalystCustomConfigTargetAccessor) List(_ labels.Selector) ([]*unstructured.Unstructured, error) { 68 return nil, nil 69 } 70 71 func (d DummyKatalystCustomConfigTargetAccessor) Get(_, _ string) (*unstructured.Unstructured, error) { 72 return nil, nil 73 } 74 75 // KatalystCustomConfigTargetHandlerFunc func to process the obj in the work queue 76 type KatalystCustomConfigTargetHandlerFunc func(gvr metav1.GroupVersionResource, target *unstructured.Unstructured) error 77 78 // targetHandlerFuncWithSyncQueue is used to store the handler and 79 // syncing queue for each kcc-target 80 type targetHandlerFuncWithSyncQueue struct { 81 targetHandlerFunc KatalystCustomConfigTargetHandlerFunc 82 syncQueue workqueue.RateLimitingInterface 83 } 84 85 type RealKatalystCustomConfigTargetAccessor struct { 86 stopCh chan struct{} 87 ctx context.Context 88 89 gvr metav1.GroupVersionResource 90 91 // targetLister can list/get target resource from the targetInformer's store 92 targetLister cache.GenericLister 93 targetInformer cache.SharedIndexInformer 94 95 // targetHandlerFuncWithSyncQueueMap is used to store the handler and syncing 96 // queue for each kcc-target 97 targetHandlerFuncWithSyncQueueMap map[string]targetHandlerFuncWithSyncQueue 98 } 99 100 // NewRealKatalystCustomConfigTargetAccessor returns a new KatalystCustomConfigTargetAccessor 101 // which is used to handle creation/update/delete event of target unstructured obj, and it can 102 // trigger obj re-sync by calling Enqueue function of the returned accessor. 103 func NewRealKatalystCustomConfigTargetAccessor( 104 gvr metav1.GroupVersionResource, 105 client dynamic.Interface, 106 handlerInfos map[string]KatalystCustomConfigTargetHandlerFunc, 107 ) (*RealKatalystCustomConfigTargetAccessor, error) { 108 dynamicInformer := dynamicinformer.NewFilteredDynamicInformer(client, 109 native.ToSchemaGVR(gvr.Group, gvr.Version, gvr.Resource), 110 metav1.NamespaceAll, 111 time.Hour*24, 112 cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, 113 nil) 114 115 k := &RealKatalystCustomConfigTargetAccessor{ 116 stopCh: make(chan struct{}), 117 gvr: gvr, 118 targetLister: dynamicInformer.Lister(), 119 targetInformer: dynamicInformer.Informer(), 120 targetHandlerFuncWithSyncQueueMap: make(map[string]targetHandlerFuncWithSyncQueue), 121 } 122 123 k.targetInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 124 AddFunc: k.addTargetEventHandle, 125 UpdateFunc: k.updateTargetEventHandle, 126 DeleteFunc: k.deleteTargetEventHandle, 127 }) 128 129 for name, info := range handlerInfos { 130 k.targetHandlerFuncWithSyncQueueMap[name] = targetHandlerFuncWithSyncQueue{ 131 targetHandlerFunc: info, 132 syncQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), 133 name+"-"+gvr.Resource), 134 } 135 } 136 137 return k, nil 138 } 139 140 func (k *RealKatalystCustomConfigTargetAccessor) Start() { 141 // run target informer 142 go k.targetInformer.Run(k.stopCh) 143 144 for _, info := range k.targetHandlerFuncWithSyncQueueMap { 145 for i := 0; i < targetWorkerCount; i++ { 146 go wait.Until(k.generateWorker(info), time.Second, k.stopCh) 147 } 148 } 149 150 klog.Infof("target accessor of %s has been started", k.gvr.String()) 151 } 152 153 func (k *RealKatalystCustomConfigTargetAccessor) Stop() { 154 klog.Infof("target accessor of %s is stopping", k.gvr.String()) 155 156 for _, info := range k.targetHandlerFuncWithSyncQueueMap { 157 info.syncQueue.ShutDown() 158 } 159 160 close(k.stopCh) 161 } 162 163 // Enqueue will add the obj to the work queue of the target handler, if name is empty, 164 // it will add the obj to all the work queue of the target handler 165 func (k *RealKatalystCustomConfigTargetAccessor) Enqueue(name string, obj *unstructured.Unstructured) { 166 if len(name) == 0 { 167 k.enqueueTarget(obj) 168 return 169 } 170 171 if obj == nil { 172 klog.Warning("trying to enqueue a nil kcc target") 173 return 174 } 175 176 key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) 177 if err != nil { 178 utilruntime.HandleError(fmt.Errorf("couldn't get key for object %#v: %v", obj, err)) 179 return 180 } 181 182 info, ok := k.targetHandlerFuncWithSyncQueueMap[name] 183 if ok { 184 info.syncQueue.Add(key) 185 } else { 186 klog.Warningf("target handler %s not found", name) 187 } 188 } 189 190 func (k *RealKatalystCustomConfigTargetAccessor) Get(namespace, name string) (*unstructured.Unstructured, error) { 191 if !k.targetInformer.HasSynced() { 192 return nil, fmt.Errorf("target targetInformer for %s not synced", k.gvr) 193 } 194 195 if namespace == "" { 196 obj, err := k.targetLister.Get(name) 197 if err != nil { 198 return nil, err 199 } 200 return obj.(*unstructured.Unstructured), nil 201 } 202 203 obj, err := k.targetLister.ByNamespace(namespace).Get(name) 204 if err != nil { 205 return nil, err 206 } 207 return obj.(*unstructured.Unstructured).DeepCopy(), nil 208 } 209 210 func (k *RealKatalystCustomConfigTargetAccessor) List(selector labels.Selector) ([]*unstructured.Unstructured, error) { 211 if !k.targetInformer.HasSynced() { 212 return nil, fmt.Errorf("target targetInformer for %s not synced", k.gvr) 213 } 214 215 list, err := k.targetLister.List(selector) 216 if err != nil { 217 return nil, err 218 } 219 220 ret := make([]*unstructured.Unstructured, 0, len(list)) 221 for _, o := range list { 222 ret = append(ret, o.(*unstructured.Unstructured).DeepCopy()) 223 } 224 return ret, nil 225 } 226 227 func (k *RealKatalystCustomConfigTargetAccessor) addTargetEventHandle(obj interface{}) { 228 t, ok := obj.(*unstructured.Unstructured) 229 if !ok { 230 klog.Errorf("cannot convert obj to *unstructured.Unstructured: %v", obj) 231 return 232 } 233 234 klog.V(4).Infof("notice addition of %s, %s", k.gvr, native.GenerateUniqObjectNameKey(t)) 235 k.enqueueTarget(t) 236 } 237 238 func (k *RealKatalystCustomConfigTargetAccessor) updateTargetEventHandle(_, new interface{}) { 239 t, ok := new.(*unstructured.Unstructured) 240 if !ok { 241 klog.Errorf("cannot convert obj to *unstructured.Unstructured: %v", new) 242 return 243 } 244 245 klog.V(4).Infof("notice update of %s, %s", k.gvr, native.GenerateUniqObjectNameKey(t)) 246 k.enqueueTarget(t) 247 } 248 249 func (k *RealKatalystCustomConfigTargetAccessor) deleteTargetEventHandle(obj interface{}) { 250 t, ok := obj.(*unstructured.Unstructured) 251 if !ok { 252 klog.Errorf("cannot convert obj to *unstructured.Unstructured: %v", obj) 253 return 254 } 255 256 klog.V(4).Infof("notice delete of %s, %s", k.gvr, native.GenerateUniqObjectNameKey(t)) 257 k.enqueueTarget(t) 258 } 259 260 func (k *RealKatalystCustomConfigTargetAccessor) enqueueTarget(obj *unstructured.Unstructured) { 261 if obj == nil { 262 klog.Warning("trying to enqueue a nil kcc target") 263 return 264 } 265 266 key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) 267 if err != nil { 268 utilruntime.HandleError(fmt.Errorf("couldn't get key for object %#v: %v", obj, err)) 269 return 270 } 271 272 for _, info := range k.targetHandlerFuncWithSyncQueueMap { 273 info.syncQueue.Add(key) 274 } 275 } 276 277 func (k *RealKatalystCustomConfigTargetAccessor) generateWorker(queue targetHandlerFuncWithSyncQueue) func() { 278 return func() { 279 for k.processNextKatalystCustomConfigTargetItem(queue.syncQueue, queue.targetHandlerFunc) { 280 } 281 } 282 } 283 284 func (k *RealKatalystCustomConfigTargetAccessor) processNextKatalystCustomConfigTargetItem(queue workqueue.RateLimitingInterface, handler KatalystCustomConfigTargetHandlerFunc) bool { 285 key, quit := queue.Get() 286 if quit { 287 return false 288 } 289 defer queue.Done(key) 290 291 err := k.syncHandler(key.(string), handler) 292 if err == nil { 293 queue.Forget(key) 294 return true 295 } 296 297 klog.Errorf("sync kcc target %q failed with %v", key, err) 298 queue.AddRateLimited(key) 299 300 return true 301 } 302 303 func (k *RealKatalystCustomConfigTargetAccessor) syncHandler(key string, handlerFunc KatalystCustomConfigTargetHandlerFunc) error { 304 if !k.targetInformer.HasSynced() { 305 return fmt.Errorf("target targetInformer for %s not synced", k.gvr) 306 } 307 308 namespace, name, err := cache.SplitMetaNamespaceKey(key) 309 if err != nil { 310 klog.Errorf("failed to split namespace and name from key %s", key) 311 return err 312 } 313 314 target, err := k.Get(namespace, name) 315 if apierrors.IsNotFound(err) { 316 klog.Warningf("%s resource %s is not found", k.gvr.String(), key) 317 return nil 318 } else if err != nil { 319 klog.Errorf("%s resource %s get error: %v", k.gvr.String(), key, err) 320 return err 321 } 322 323 return handlerFunc(k.gvr, target) 324 }