k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/controlplane/controller/legacytokentracking/controller.go (about) 1 /* 2 Copyright 2022 The Kubernetes 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 legacytokentracking 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "golang.org/x/time/rate" 25 26 corev1 "k8s.io/api/core/v1" 27 apierrors "k8s.io/apimachinery/pkg/api/errors" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/fields" 30 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 31 "k8s.io/apimachinery/pkg/util/wait" 32 corev1informers "k8s.io/client-go/informers/core/v1" 33 "k8s.io/client-go/kubernetes" 34 corev1client "k8s.io/client-go/kubernetes/typed/core/v1" 35 "k8s.io/client-go/tools/cache" 36 "k8s.io/client-go/util/workqueue" 37 "k8s.io/klog/v2" 38 "k8s.io/utils/clock" 39 ) 40 41 const ( 42 ConfigMapName = "kube-apiserver-legacy-service-account-token-tracking" 43 ConfigMapDataKey = "since" 44 dateFormat = "2006-01-02" 45 ) 46 47 var ( 48 queueKey = metav1.NamespaceSystem + "/" + ConfigMapName 49 ) 50 51 // Controller maintains a timestamp value configmap `kube-apiserver-legacy-service-account-token-tracking` 52 // in `kube-system` to indicates if the tracking for legacy tokens is enabled in 53 // the cluster. For HA clusters, the configmap will be eventually created after 54 // all controller instances have enabled the feature. When disabling this 55 // feature, existing configmap will be deleted. 56 type Controller struct { 57 configMapClient corev1client.ConfigMapsGetter 58 configMapInformer cache.SharedIndexInformer 59 configMapCache cache.Indexer 60 configMapSynced cache.InformerSynced 61 queue workqueue.TypedRateLimitingInterface[string] 62 63 // rate limiter controls the rate limit of the creation of the configmap. 64 // this is useful in multi-apiserver cluster to prevent config existing in a 65 // cluster with mixed enabled/disabled controllers. otherwise, those 66 // apiservers will fight to create/delete until all apiservers are enabled 67 // or disabled. 68 creationRatelimiter *rate.Limiter 69 clock clock.Clock 70 } 71 72 // NewController returns a Controller struct. 73 func NewController(cs kubernetes.Interface) *Controller { 74 return newController(cs, clock.RealClock{}, rate.NewLimiter(rate.Every(30*time.Minute), 1)) 75 } 76 77 func newController(cs kubernetes.Interface, cl clock.Clock, limiter *rate.Limiter) *Controller { 78 informer := corev1informers.NewFilteredConfigMapInformer(cs, metav1.NamespaceSystem, 12*time.Hour, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, func(options *metav1.ListOptions) { 79 options.FieldSelector = fields.OneTermEqualSelector("metadata.name", ConfigMapName).String() 80 }) 81 82 c := &Controller{ 83 configMapClient: cs.CoreV1(), 84 queue: workqueue.NewTypedRateLimitingQueueWithConfig( 85 workqueue.DefaultTypedControllerRateLimiter[string](), 86 workqueue.TypedRateLimitingQueueConfig[string]{Name: "legacy_token_tracking_controller"}, 87 ), 88 configMapInformer: informer, 89 configMapCache: informer.GetIndexer(), 90 configMapSynced: informer.HasSynced, 91 creationRatelimiter: limiter, 92 clock: cl, 93 } 94 95 informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 96 AddFunc: func(obj interface{}) { 97 c.enqueue() 98 }, 99 UpdateFunc: func(oldObj, newObj interface{}) { 100 c.enqueue() 101 }, 102 DeleteFunc: func(obj interface{}) { 103 c.enqueue() 104 }, 105 }) 106 107 return c 108 } 109 110 func (c *Controller) enqueue() { 111 c.queue.Add(queueKey) 112 } 113 114 // Run starts the controller sync loop. 115 func (c *Controller) Run(stopCh <-chan struct{}) { 116 defer utilruntime.HandleCrash() 117 defer c.queue.ShutDown() 118 119 klog.Info("Starting legacy_token_tracking_controller") 120 defer klog.Infof("Shutting down legacy_token_tracking_controller") 121 122 go c.configMapInformer.Run(stopCh) 123 if !cache.WaitForNamedCacheSync("configmaps", stopCh, c.configMapSynced) { 124 return 125 } 126 127 go wait.Until(c.runWorker, time.Second, stopCh) 128 129 c.queue.Add(queueKey) 130 131 <-stopCh 132 klog.Info("Ending legacy_token_tracking_controller") 133 } 134 135 func (c *Controller) runWorker() { 136 for c.processNext() { 137 } 138 } 139 140 func (c *Controller) processNext() bool { 141 key, quit := c.queue.Get() 142 if quit { 143 return false 144 } 145 defer c.queue.Done(key) 146 147 if err := c.syncConfigMap(); err != nil { 148 utilruntime.HandleError(fmt.Errorf("while syncing ConfigMap %q, err: %w", key, err)) 149 c.queue.AddRateLimited(key) 150 return true 151 } 152 c.queue.Forget(key) 153 return true 154 } 155 156 func (c *Controller) syncConfigMap() error { 157 obj, exists, err := c.configMapCache.GetByKey(queueKey) 158 if err != nil { 159 return err 160 } 161 162 now := c.clock.Now() 163 if !exists { 164 r := c.creationRatelimiter.ReserveN(now, 1) 165 if delay := r.DelayFrom(now); delay > 0 { 166 c.queue.AddAfter(queueKey, delay) 167 r.CancelAt(now) 168 return nil 169 } 170 171 if _, err = c.configMapClient.ConfigMaps(metav1.NamespaceSystem).Create(context.TODO(), &corev1.ConfigMap{ 172 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: ConfigMapName}, 173 Data: map[string]string{ConfigMapDataKey: now.UTC().Format(dateFormat)}, 174 }, metav1.CreateOptions{}); err != nil { 175 if apierrors.IsAlreadyExists(err) { 176 return nil 177 } 178 // don't consume the creationRatelimiter for an unsuccessful attempt 179 r.CancelAt(now) 180 return err 181 } 182 } else { 183 configMap := obj.(*corev1.ConfigMap) 184 if _, err = time.Parse(dateFormat, configMap.Data[ConfigMapDataKey]); err != nil { 185 configMap := configMap.DeepCopy() 186 if configMap.Data == nil { 187 configMap.Data = map[string]string{} 188 } 189 configMap.Data[ConfigMapDataKey] = now.UTC().Format(dateFormat) 190 if _, err = c.configMapClient.ConfigMaps(metav1.NamespaceSystem).Update(context.TODO(), configMap, metav1.UpdateOptions{}); err != nil { 191 if apierrors.IsNotFound(err) || apierrors.IsConflict(err) { 192 return nil 193 } 194 return err 195 } 196 } 197 } 198 199 return nil 200 }