...

Source file src/k8s.io/kubernetes/pkg/controller/serviceaccount/serviceaccounts_controller.go

Documentation: k8s.io/kubernetes/pkg/controller/serviceaccount

     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  }
   220  

View as plain text