github.com/kubewharf/katalyst-core@v0.5.3/pkg/util/native/informer_dynamic.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 native 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "k8s.io/apimachinery/pkg/runtime/schema" 25 "k8s.io/apimachinery/pkg/util/errors" 26 "k8s.io/apimachinery/pkg/util/wait" 27 "k8s.io/client-go/dynamic/dynamicinformer" 28 "k8s.io/client-go/informers" 29 "k8s.io/client-go/util/retry" 30 "k8s.io/klog/v2" 31 "sigs.k8s.io/custom-metrics-apiserver/pkg/dynamicmapper" 32 ) 33 34 // DynamicInformer keeps the informer-related contents for each workload 35 type DynamicInformer struct { 36 GVK schema.GroupVersionKind 37 GVR schema.GroupVersionResource 38 Informer informers.GenericInformer 39 } 40 41 type DynamicResourcesManager struct { 42 // dynamicGVRs and dynamicInformers initialize only once 43 dynamicGVRs map[string]schema.GroupVersionResource 44 dynamicInformers map[string]DynamicInformer 45 46 mapper *dynamicmapper.RegeneratingDiscoveryRESTMapper 47 dynamicInformerFactory dynamicinformer.DynamicSharedInformerFactory 48 } 49 50 // NewDynamicResourcesManager initializes a dynamic resources manger to manage dynamic informers 51 func NewDynamicResourcesManager( 52 dynamicResources []string, 53 mapper *dynamicmapper.RegeneratingDiscoveryRESTMapper, 54 dynamicInformerFactory dynamicinformer.DynamicSharedInformerFactory, 55 ) (*DynamicResourcesManager, error) { 56 m := &DynamicResourcesManager{ 57 dynamicInformers: make(map[string]DynamicInformer), 58 mapper: mapper, 59 dynamicInformerFactory: dynamicInformerFactory, 60 } 61 62 dynamicGVRs, err := getDynamicResourcesGVRMap(dynamicResources) 63 if err != nil { 64 return nil, fmt.Errorf("new dynamic resource manager failed: %v", err) 65 } 66 m.dynamicGVRs = dynamicGVRs 67 68 m.initDynamicInformers() 69 return m, nil 70 } 71 72 // Run start mapper to refresh starts a goroutine to check if it has new gvr support available, 73 // and if so, panics to restart to make sure all caches are correct 74 func (m *DynamicResourcesManager) Run(ctx context.Context) { 75 // run RegeneratingDiscoveryRESTMapper to refresh gvr to gvk map 76 m.mapper.RunUntil(ctx.Done()) 77 78 // try to check whether new resources need to support, and if so, 79 // panics to restart 80 go wait.UntilWithContext(ctx, func(ctx context.Context) { 81 for resource, gvr := range m.dynamicGVRs { 82 gvk, err := m.mapper.KindFor(gvr) 83 if err != nil { 84 klog.Errorf("find for %v failed, err %v", gvr.String(), err) 85 continue 86 } 87 88 // check whether current gvk is equal to old one 89 if informer, ok := m.dynamicInformers[resource]; ok && gvk == informer.GVK { 90 continue 91 } 92 93 panic(fmt.Sprintf("gvk %s for gvr %s found, try to restart", gvk.String(), gvr.String())) 94 } 95 }, 1*time.Minute) 96 } 97 98 // GetDynamicInformers gets current dynamic informers 99 func (m *DynamicResourcesManager) GetDynamicInformers() map[string]DynamicInformer { 100 return m.dynamicInformers 101 } 102 103 // initDynamicInformers initializes dynamic informers map 104 func (m *DynamicResourcesManager) initDynamicInformers() { 105 _ = wait.ExponentialBackoff(retry.DefaultRetry, func() (bool, error) { 106 err := m.syncDynamicInformers() 107 if err == nil { 108 return true, nil 109 } 110 111 klog.Errorf("sync DynamicInformers failed: %v, try to refresh mapper", err) 112 113 err = m.mapper.RegenerateMappings() 114 if err != nil { 115 klog.Errorf("regenerate DiscoveryRESTMapper failed: %v", err) 116 } 117 return false, nil 118 }) 119 } 120 121 // syncDynamicInformers sync dynamic informers by current dynamic resources 122 func (m *DynamicResourcesManager) syncDynamicInformers() error { 123 var errList []error 124 for resource, gvr := range m.dynamicGVRs { 125 if _, ok := m.dynamicInformers[resource]; ok { 126 continue 127 } 128 129 gvk, err := m.mapper.KindFor(gvr) 130 if err != nil { 131 errList = append(errList, fmt.Errorf("find for %v failed, err %v", gvr.String(), err)) 132 continue 133 } 134 135 m.dynamicInformers[resource] = DynamicInformer{ 136 GVK: gvk, 137 GVR: gvr, 138 Informer: m.dynamicInformerFactory.ForResource(gvr), 139 } 140 } 141 if len(errList) > 0 { 142 return errors.NewAggregate(errList) 143 } 144 145 return nil 146 } 147 148 func getDynamicResourcesGVRMap(dynamicResources []string) (map[string]schema.GroupVersionResource, error) { 149 dynamicGVRs := make(map[string]schema.GroupVersionResource, len(dynamicResources)) 150 var errList []error 151 for _, resource := range dynamicResources { 152 if _, ok := dynamicGVRs[resource]; ok { 153 continue 154 } 155 156 gvr, _ := schema.ParseResourceArg(resource) 157 if gvr == nil { 158 return nil, fmt.Errorf("ParseResourceArg resource %v failed", resource) 159 } 160 161 dynamicGVRs[resource] = *gvr 162 } 163 if len(errList) > 0 { 164 return nil, errors.NewAggregate(errList) 165 } 166 167 return dynamicGVRs, nil 168 }