k8s.io/kubernetes@v1.29.3/pkg/controller/serviceaccount/serviceaccounts_controller.go (about) 1 /* 2 Copyright 2014 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 serviceaccount 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 v1 "k8s.io/api/core/v1" 25 apierrors "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 utilerrors "k8s.io/apimachinery/pkg/util/errors" 28 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 29 "k8s.io/apimachinery/pkg/util/wait" 30 coreinformers "k8s.io/client-go/informers/core/v1" 31 clientset "k8s.io/client-go/kubernetes" 32 corelisters "k8s.io/client-go/listers/core/v1" 33 "k8s.io/client-go/tools/cache" 34 "k8s.io/client-go/util/workqueue" 35 "k8s.io/klog/v2" 36 ) 37 38 // ServiceAccountsControllerOptions contains options for running a ServiceAccountsController 39 type ServiceAccountsControllerOptions struct { 40 // ServiceAccounts is the list of service accounts to ensure exist in every namespace 41 ServiceAccounts []v1.ServiceAccount 42 43 // ServiceAccountResync is the interval between full resyncs of ServiceAccounts. 44 // If non-zero, all service accounts will be re-listed this often. 45 // Otherwise, re-list will be delayed as long as possible (until the watch is closed or times out). 46 ServiceAccountResync time.Duration 47 48 // NamespaceResync is the interval between full resyncs of Namespaces. 49 // If non-zero, all namespaces will be re-listed this often. 50 // Otherwise, re-list will be delayed as long as possible (until the watch is closed or times out). 51 NamespaceResync time.Duration 52 } 53 54 // DefaultServiceAccountsControllerOptions returns the default options for creating a ServiceAccountsController. 55 func DefaultServiceAccountsControllerOptions() ServiceAccountsControllerOptions { 56 return ServiceAccountsControllerOptions{ 57 ServiceAccounts: []v1.ServiceAccount{ 58 {ObjectMeta: metav1.ObjectMeta{Name: "default"}}, 59 }, 60 } 61 } 62 63 // NewServiceAccountsController returns a new *ServiceAccountsController. 64 func NewServiceAccountsController(saInformer coreinformers.ServiceAccountInformer, nsInformer coreinformers.NamespaceInformer, cl clientset.Interface, options ServiceAccountsControllerOptions) (*ServiceAccountsController, error) { 65 e := &ServiceAccountsController{ 66 client: cl, 67 serviceAccountsToEnsure: options.ServiceAccounts, 68 queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "serviceaccount"), 69 } 70 71 saHandler, _ := saInformer.Informer().AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{ 72 DeleteFunc: e.serviceAccountDeleted, 73 }, options.ServiceAccountResync) 74 e.saLister = saInformer.Lister() 75 e.saListerSynced = saHandler.HasSynced 76 77 nsHandler, _ := nsInformer.Informer().AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{ 78 AddFunc: e.namespaceAdded, 79 UpdateFunc: e.namespaceUpdated, 80 }, options.NamespaceResync) 81 e.nsLister = nsInformer.Lister() 82 e.nsListerSynced = nsHandler.HasSynced 83 84 e.syncHandler = e.syncNamespace 85 86 return e, nil 87 } 88 89 // ServiceAccountsController manages ServiceAccount objects inside Namespaces 90 type ServiceAccountsController struct { 91 client clientset.Interface 92 serviceAccountsToEnsure []v1.ServiceAccount 93 94 // To allow injection for testing. 95 syncHandler func(ctx context.Context, key string) error 96 97 saLister corelisters.ServiceAccountLister 98 saListerSynced cache.InformerSynced 99 100 nsLister corelisters.NamespaceLister 101 nsListerSynced cache.InformerSynced 102 103 queue workqueue.RateLimitingInterface 104 } 105 106 // Run runs the ServiceAccountsController blocks until receiving signal from stopCh. 107 func (c *ServiceAccountsController) Run(ctx context.Context, workers int) { 108 defer utilruntime.HandleCrash() 109 defer c.queue.ShutDown() 110 111 klog.FromContext(ctx).Info("Starting service account controller") 112 defer klog.FromContext(ctx).Info("Shutting down service account controller") 113 114 if !cache.WaitForNamedCacheSync("service account", ctx.Done(), c.saListerSynced, c.nsListerSynced) { 115 return 116 } 117 118 for i := 0; i < workers; i++ { 119 go wait.UntilWithContext(ctx, c.runWorker, time.Second) 120 } 121 122 <-ctx.Done() 123 } 124 125 // serviceAccountDeleted reacts to a ServiceAccount deletion by recreating a default ServiceAccount in the namespace if needed 126 func (c *ServiceAccountsController) serviceAccountDeleted(obj interface{}) { 127 sa, ok := obj.(*v1.ServiceAccount) 128 if !ok { 129 tombstone, ok := obj.(cache.DeletedFinalStateUnknown) 130 if !ok { 131 utilruntime.HandleError(fmt.Errorf("Couldn't get object from tombstone %#v", obj)) 132 return 133 } 134 sa, ok = tombstone.Obj.(*v1.ServiceAccount) 135 if !ok { 136 utilruntime.HandleError(fmt.Errorf("Tombstone contained object that is not a ServiceAccount %#v", obj)) 137 return 138 } 139 } 140 c.queue.Add(sa.Namespace) 141 } 142 143 // namespaceAdded reacts to a Namespace creation by creating a default ServiceAccount object 144 func (c *ServiceAccountsController) namespaceAdded(obj interface{}) { 145 namespace := obj.(*v1.Namespace) 146 c.queue.Add(namespace.Name) 147 } 148 149 // namespaceUpdated reacts to a Namespace update (or re-list) by creating a default ServiceAccount in the namespace if needed 150 func (c *ServiceAccountsController) namespaceUpdated(oldObj interface{}, newObj interface{}) { 151 newNamespace := newObj.(*v1.Namespace) 152 c.queue.Add(newNamespace.Name) 153 } 154 155 func (c *ServiceAccountsController) runWorker(ctx context.Context) { 156 for c.processNextWorkItem(ctx) { 157 } 158 } 159 160 // processNextWorkItem deals with one key off the queue. It returns false when it's time to quit. 161 func (c *ServiceAccountsController) processNextWorkItem(ctx context.Context) bool { 162 key, quit := c.queue.Get() 163 if quit { 164 return false 165 } 166 defer c.queue.Done(key) 167 168 err := c.syncHandler(ctx, key.(string)) 169 if err == nil { 170 c.queue.Forget(key) 171 return true 172 } 173 174 utilruntime.HandleError(fmt.Errorf("%v failed with : %v", key, err)) 175 c.queue.AddRateLimited(key) 176 177 return true 178 } 179 func (c *ServiceAccountsController) syncNamespace(ctx context.Context, key string) error { 180 startTime := time.Now() 181 defer func() { 182 klog.FromContext(ctx).V(4).Info("Finished syncing namespace", "namespace", key, "duration", time.Since(startTime)) 183 }() 184 185 ns, err := c.nsLister.Get(key) 186 if apierrors.IsNotFound(err) { 187 return nil 188 } 189 if err != nil { 190 return err 191 } 192 if ns.Status.Phase != v1.NamespaceActive { 193 // If namespace is not active, we shouldn't try to create anything 194 return nil 195 } 196 197 createFailures := []error{} 198 for _, sa := range c.serviceAccountsToEnsure { 199 switch _, err := c.saLister.ServiceAccounts(ns.Name).Get(sa.Name); { 200 case err == nil: 201 continue 202 case apierrors.IsNotFound(err): 203 case err != nil: 204 return err 205 } 206 // this is only safe because we never read it and we always write it 207 // TODO eliminate this once the fake client can handle creation without NS 208 sa.Namespace = ns.Name 209 210 if _, err := c.client.CoreV1().ServiceAccounts(ns.Name).Create(ctx, &sa, metav1.CreateOptions{}); err != nil && !apierrors.IsAlreadyExists(err) { 211 // we can safely ignore terminating namespace errors 212 if !apierrors.HasStatusCause(err, v1.NamespaceTerminatingCause) { 213 createFailures = append(createFailures, err) 214 } 215 } 216 } 217 218 return utilerrors.Flatten(utilerrors.NewAggregate(createFailures)) 219 }