github.com/kubewharf/katalyst-core@v0.5.3/pkg/metaserver/kcc/config.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 config is the package that gets centralized configurations periodically 18 // and dynamically for a given node. 19 package kcc // import "github.com/kubewharf/katalyst-core/pkg/metaserver/config" 20 21 import ( 22 "context" 23 "fmt" 24 "sync" 25 "time" 26 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/client-go/dynamic" 29 "k8s.io/klog/v2" 30 31 "github.com/kubewharf/katalyst-api/pkg/apis/config/v1alpha1" 32 "github.com/kubewharf/katalyst-core/pkg/client" 33 "github.com/kubewharf/katalyst-core/pkg/metaserver/agent/cnc" 34 "github.com/kubewharf/katalyst-core/pkg/util" 35 "github.com/kubewharf/katalyst-core/pkg/util/native" 36 ) 37 38 // ConfigurationLoader is used to load configurations from centralized server. 39 type ConfigurationLoader interface { 40 LoadConfig(ctx context.Context, gvr metav1.GroupVersionResource, conf interface{}) error 41 } 42 43 // configCache keeps a local in-memory cache for each configuration CRD. 44 // each time when users want to get the latest configuration, return from 45 // cache firstly (it still valid); otherwise, trigger a client getting action. 46 type configCache struct { 47 // targetConfigHash records the config hash for matched configurations in CNC. 48 targetConfigHash string 49 // targetConfigContent records the contents for matched configurations in CNC. 50 targetConfigContent util.KCCTargetResource 51 } 52 53 type katalystCustomConfigLoader struct { 54 client *client.GenericClientSet 55 cncFetcher cnc.CNCFetcher 56 57 ttl time.Duration 58 59 mux sync.RWMutex 60 61 // lastFetchConfigTime is to limit the rate of getting each configuration, 62 // this is to avoid getting some configurations frequently when it always 63 // fails 64 lastFetchConfigTime map[metav1.GroupVersionResource]time.Time 65 66 // configCache is a cache of gvr to current target config meta-info 67 // and its latest object 68 configCache map[metav1.GroupVersionResource]configCache 69 } 70 71 // NewKatalystCustomConfigLoader create a new configManager to fetch KatalystCustomConfig. 72 // every LoadConfig() call tries to fetch the value from local cache; if it is 73 // not there, invalidated or too old, we fetch it from api-server and refresh the 74 // value in cache; otherwise it is just fetched from cache. 75 // defaultGVRList s the list of default gvr fetched from remote api-server, if 76 // LoadConfig() fetches a new gvr, it will be automatically added to. 77 func NewKatalystCustomConfigLoader(clientSet *client.GenericClientSet, ttl time.Duration, 78 cncFetcher cnc.CNCFetcher, 79 ) ConfigurationLoader { 80 return &katalystCustomConfigLoader{ 81 cncFetcher: cncFetcher, 82 client: clientSet, 83 ttl: ttl, 84 lastFetchConfigTime: make(map[metav1.GroupVersionResource]time.Time), 85 configCache: make(map[metav1.GroupVersionResource]configCache), 86 } 87 } 88 89 func (c *katalystCustomConfigLoader) LoadConfig(ctx context.Context, gvr metav1.GroupVersionResource, conf interface{}) error { 90 // get target config from updated cnc 91 targetConfig, err := c.getCNCTargetConfig(ctx, gvr) 92 if err != nil { 93 return fmt.Errorf("get cnc target cache failed: %v", err) 94 } 95 96 // update current target config according to its hash 97 err = c.updateConfigCacheIfNeed(ctx, targetConfig) 98 if err != nil { 99 klog.Errorf("[kcc-sdk] failed update config cache from remote: %s, use local cache instead", err) 100 } 101 102 c.mux.RLock() 103 cache, ok := c.configCache[gvr] 104 c.mux.RUnlock() 105 106 if ok { 107 return cache.targetConfigContent.Unmarshal(conf) 108 } 109 110 return fmt.Errorf("get config cache for %s not found", gvr) 111 } 112 113 // getCNCTargetConfig get cnc target from cnc fetcher 114 func (c *katalystCustomConfigLoader) getCNCTargetConfig(ctx context.Context, gvr metav1.GroupVersionResource) (*v1alpha1.TargetConfig, error) { 115 currentCNC, err := c.cncFetcher.GetCNC(ctx) 116 if err != nil { 117 return nil, err 118 } 119 120 for _, target := range currentCNC.Status.KatalystCustomConfigList { 121 if target.ConfigType == gvr { 122 return &target, nil 123 } 124 } 125 126 return nil, fmt.Errorf("get target config %s not found", gvr) 127 } 128 129 // updateConfigCacheIfNeed checks if the previous configuration has changed, and 130 // re-get from APIServer if the previous is out-of date. 131 func (c *katalystCustomConfigLoader) updateConfigCacheIfNeed(ctx context.Context, targetConfig *v1alpha1.TargetConfig) error { 132 c.mux.Lock() 133 defer c.mux.Unlock() 134 135 if targetConfig == nil { 136 return nil 137 } 138 139 gvr := targetConfig.ConfigType 140 if cache, ok := c.configCache[gvr]; !ok || targetConfig.Hash != cache.targetConfigHash { 141 // update last fetch config timestamp first 142 if lastFetchTime, ok := c.lastFetchConfigTime[gvr]; ok && lastFetchTime.Add(c.ttl).After(time.Now()) { 143 return nil 144 } else { 145 c.lastFetchConfigTime[gvr] = time.Now() 146 } 147 148 schemaGVR := native.ToSchemaGVR(gvr.Group, gvr.Version, gvr.Resource) 149 var dynamicClient dynamic.ResourceInterface 150 if targetConfig.ConfigNamespace != "" { 151 dynamicClient = c.client.DynamicClient.Resource(schemaGVR).Namespace(targetConfig.ConfigNamespace) 152 } else { 153 dynamicClient = c.client.DynamicClient.Resource(schemaGVR) 154 } 155 156 // todo: emit metrics if fail to get latest dynamic config from APIServer 157 klog.Infof("[kcc-sdk] %s targetConfigMeta hash is changed to %s", gvr, targetConfig.Hash) 158 conf, err := dynamicClient.Get(ctx, targetConfig.ConfigName, metav1.GetOptions{ResourceVersion: "0"}) 159 if err != nil { 160 return err 161 } 162 163 c.configCache[gvr] = configCache{ 164 targetConfigHash: targetConfig.Hash, 165 targetConfigContent: util.ToKCCTargetResource(conf), 166 } 167 168 klog.Infof("[kcc-sdk] %s config cache has been updated to %v", gvr.String(), conf) 169 } 170 171 return nil 172 }