2015-01-30 23:31:36 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
2015-01-30 23:31:36 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2015-02-08 04:07:00 +00:00
// Package app implements a server that runs a set of active
2015-01-30 23:31:36 +00:00
// components. This includes replication controllers, service endpoints and
// nodes.
2015-06-11 13:13:19 +00:00
//
2015-02-08 04:07:00 +00:00
package app
2015-01-30 23:31:36 +00:00
import (
2015-06-23 22:43:59 +00:00
"fmt"
"io/ioutil"
2015-10-06 09:12:00 +00:00
"math/rand"
2015-01-30 23:31:36 +00:00
"net"
"net/http"
2015-03-13 15:44:11 +00:00
"net/http/pprof"
2015-11-04 23:22:11 +00:00
"os"
2017-02-28 18:43:08 +00:00
goruntime "runtime"
2015-01-30 23:31:36 +00:00
"strconv"
"time"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/runtime/schema"
2017-07-24 13:56:31 +00:00
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2017-01-10 20:11:25 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/util/wait"
2017-03-08 07:03:57 +00:00
2017-01-17 10:38:25 +00:00
"k8s.io/apiserver/pkg/server/healthz"
2017-03-08 07:03:57 +00:00
2017-07-15 05:25:54 +00:00
"k8s.io/api/core/v1"
2017-01-25 19:00:30 +00:00
"k8s.io/client-go/discovery"
2017-01-30 18:39:54 +00:00
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
2017-01-19 18:27:59 +00:00
restclient "k8s.io/client-go/rest"
2017-01-20 18:06:17 +00:00
"k8s.io/client-go/tools/clientcmd"
2017-01-30 18:39:54 +00:00
"k8s.io/client-go/tools/record"
2017-01-23 18:37:22 +00:00
certutil "k8s.io/client-go/util/cert"
2017-03-08 07:03:57 +00:00
2017-06-23 20:56:37 +00:00
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes"
2017-07-07 20:59:32 +00:00
"k8s.io/client-go/tools/leaderelection"
"k8s.io/client-go/tools/leaderelection/resourcelock"
2015-12-24 23:59:05 +00:00
"k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
2017-10-16 11:41:50 +00:00
"k8s.io/kubernetes/pkg/api/legacyscheme"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/cloudprovider"
2015-11-11 21:19:39 +00:00
"k8s.io/kubernetes/pkg/controller"
2015-12-24 21:54:40 +00:00
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
"k8s.io/kubernetes/pkg/serviceaccount"
2016-02-02 02:09:02 +00:00
"k8s.io/kubernetes/pkg/util/configz"
2017-06-01 21:30:34 +00:00
"k8s.io/kubernetes/pkg/version"
2015-04-18 13:31:24 +00:00
2015-01-30 23:31:36 +00:00
"github.com/golang/glog"
2015-04-22 14:46:03 +00:00
"github.com/prometheus/client_golang/prometheus"
2015-10-12 14:33:39 +00:00
"github.com/spf13/cobra"
2015-01-30 23:31:36 +00:00
"github.com/spf13/pflag"
)
2016-03-30 14:07:30 +00:00
const (
// Jitter used when starting controller managers
ControllerStartJitter = 1.0
)
2015-10-12 14:33:39 +00:00
// NewControllerManagerCommand creates a *cobra.Command object with default parameters
func NewControllerManagerCommand ( ) * cobra . Command {
2015-12-24 23:59:05 +00:00
s := options . NewCMServer ( )
2017-01-10 20:11:25 +00:00
s . AddFlags ( pflag . CommandLine , KnownControllers ( ) , ControllersDisabledByDefault . List ( ) )
2015-10-12 14:33:39 +00:00
cmd := & cobra . Command {
Use : "kube-controller-manager" ,
Long : ` The Kubernetes controller manager is a daemon that embeds
the core control loops shipped with Kubernetes . In applications of robotics and
automation , a control loop is a non - terminating loop that regulates the state of
the system . In Kubernetes , a controller is a control loop that watches the shared
state of the cluster through the apiserver and makes changes attempting to move the
current state towards the desired state . Examples of controllers that ship with
Kubernetes today are the replication controller , endpoints controller , namespace
controller , and serviceaccounts controller . ` ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
} ,
}
return cmd
}
2017-02-09 02:02:55 +00:00
// ResyncPeriod returns a function which generates a duration each time it is
// invoked; this is so that multiple controllers don't get into lock-step and all
// hammer the apiserver with list requests simultaneously.
2015-12-24 23:59:05 +00:00
func ResyncPeriod ( s * options . CMServer ) func ( ) time . Duration {
return func ( ) time . Duration {
factor := rand . Float64 ( ) + 1
return time . Duration ( float64 ( s . MinResyncPeriod . Nanoseconds ( ) ) * factor )
}
2015-10-06 09:12:00 +00:00
}
2015-01-30 23:31:36 +00:00
// Run runs the CMServer. This should never exit.
2015-12-24 23:59:05 +00:00
func Run ( s * options . CMServer ) error {
2017-06-01 21:30:34 +00:00
// To help debugging, immediately log version
glog . Infof ( "Version: %+v" , version . Get ( ) )
2017-01-10 20:11:25 +00:00
if err := s . Validate ( KnownControllers ( ) , ControllersDisabledByDefault . List ( ) ) ; err != nil {
return err
}
2016-02-02 02:09:02 +00:00
if c , err := configz . New ( "componentconfig" ) ; err == nil {
c . Set ( s . KubeControllerManagerConfiguration )
} else {
glog . Errorf ( "unable to register configz: %s" , err )
}
2017-08-28 10:15:49 +00:00
kubeClient , leaderElectionClient , kubeconfig , err := createClients ( s )
2015-04-17 07:18:07 +00:00
if err != nil {
return err
}
2017-08-28 10:15:49 +00:00
go startHTTP ( s )
2015-01-30 23:31:36 +00:00
2017-08-28 10:15:49 +00:00
recorder := createRecorder ( kubeClient )
2016-06-21 16:13:23 +00:00
2015-11-04 23:22:11 +00:00
run := func ( stop <- chan struct { } ) {
2016-09-22 19:53:08 +00:00
rootClientBuilder := controller . SimpleControllerClientBuilder {
ClientConfig : kubeconfig ,
}
var clientBuilder controller . ControllerClientBuilder
2017-08-08 04:48:17 +00:00
if s . UseServiceAccountCredentials {
2017-08-21 17:51:47 +00:00
if len ( s . ServiceAccountKeyFile ) == 0 {
2017-08-08 04:48:17 +00:00
// It's possible another controller process is creating the tokens for us.
// If one isn't, we'll timeout and exit when our client builder is unable to create the tokens.
glog . Warningf ( "--use-service-account-credentials was specified without providing a --service-account-private-key-file" )
}
2016-09-22 19:53:08 +00:00
clientBuilder = controller . SAControllerClientBuilder {
2017-02-17 20:48:22 +00:00
ClientConfig : restclient . AnonymousClientConfig ( kubeconfig ) ,
2017-07-25 04:35:12 +00:00
CoreClient : kubeClient . CoreV1 ( ) ,
2017-02-17 20:48:22 +00:00
AuthenticationClient : kubeClient . Authentication ( ) ,
Namespace : "kube-system" ,
2016-09-22 19:53:08 +00:00
}
} else {
clientBuilder = rootClientBuilder
}
2017-06-01 16:03:18 +00:00
ctx , err := CreateControllerContext ( s , rootClientBuilder , clientBuilder , stop )
if err != nil {
glog . Fatalf ( "error building controller context: %v" , err )
}
saTokenControllerInitFunc := serviceAccountTokenControllerStarter { rootClientBuilder : rootClientBuilder } . startServiceAccountTokenController
2016-09-22 19:53:08 +00:00
2017-06-01 16:03:18 +00:00
if err := StartControllers ( ctx , saTokenControllerInitFunc , NewControllerInitializers ( ) ) ; err != nil {
glog . Fatalf ( "error starting controllers: %v" , err )
}
ctx . InformerFactory . Start ( ctx . Stop )
2017-08-24 16:39:55 +00:00
close ( ctx . InformersStarted )
2017-06-01 16:03:18 +00:00
select { }
2015-11-04 23:22:11 +00:00
}
if ! s . LeaderElection . LeaderElect {
run ( nil )
panic ( "unreachable" )
}
id , err := os . Hostname ( )
if err != nil {
return err
}
2017-05-12 19:13:37 +00:00
rl , err := resourcelock . New ( s . LeaderElection . ResourceLock ,
"kube-system" ,
"kube-controller-manager" ,
2017-07-25 04:35:12 +00:00
leaderElectionClient . CoreV1 ( ) ,
2017-05-12 19:13:37 +00:00
resourcelock . ResourceLockConfig {
2016-09-15 19:17:18 +00:00
Identity : id ,
EventRecorder : recorder ,
2017-05-12 19:13:37 +00:00
} )
2017-05-05 12:01:59 +00:00
if err != nil {
2017-05-12 19:13:37 +00:00
glog . Fatalf ( "error creating lock: %v" , err )
2016-09-15 19:17:18 +00:00
}
leaderelection . RunOrDie ( leaderelection . LeaderElectionConfig {
2017-05-05 12:01:59 +00:00
Lock : rl ,
2016-09-26 07:46:59 +00:00
LeaseDuration : s . LeaderElection . LeaseDuration . Duration ,
RenewDeadline : s . LeaderElection . RenewDeadline . Duration ,
RetryPeriod : s . LeaderElection . RetryPeriod . Duration ,
2015-11-04 23:22:11 +00:00
Callbacks : leaderelection . LeaderCallbacks {
OnStartedLeading : run ,
OnStoppedLeading : func ( ) {
glog . Fatalf ( "leaderelection lost" )
} ,
} ,
} )
panic ( "unreachable" )
}
2017-08-28 10:15:49 +00:00
func startHTTP ( s * options . CMServer ) {
mux := http . NewServeMux ( )
healthz . InstallHandler ( mux )
if s . EnableProfiling {
mux . HandleFunc ( "/debug/pprof/" , pprof . Index )
mux . HandleFunc ( "/debug/pprof/profile" , pprof . Profile )
mux . HandleFunc ( "/debug/pprof/symbol" , pprof . Symbol )
mux . HandleFunc ( "/debug/pprof/trace" , pprof . Trace )
if s . EnableContentionProfiling {
goruntime . SetBlockProfileRate ( 1 )
}
}
configz . InstallHandler ( mux )
mux . Handle ( "/metrics" , prometheus . Handler ( ) )
server := & http . Server {
Addr : net . JoinHostPort ( s . Address , strconv . Itoa ( int ( s . Port ) ) ) ,
Handler : mux ,
}
glog . Fatal ( server . ListenAndServe ( ) )
}
func createRecorder ( kubeClient * clientset . Clientset ) record . EventRecorder {
eventBroadcaster := record . NewBroadcaster ( )
eventBroadcaster . StartLogging ( glog . Infof )
eventBroadcaster . StartRecordingToSink ( & v1core . EventSinkImpl { Interface : v1core . New ( kubeClient . CoreV1 ( ) . RESTClient ( ) ) . Events ( "" ) } )
2017-10-16 11:41:50 +00:00
return eventBroadcaster . NewRecorder ( legacyscheme . Scheme , v1 . EventSource { Component : "controller-manager" } )
2017-08-28 10:15:49 +00:00
}
func createClients ( s * options . CMServer ) ( * clientset . Clientset , * clientset . Clientset , * restclient . Config , error ) {
kubeconfig , err := clientcmd . BuildConfigFromFlags ( s . Master , s . Kubeconfig )
if err != nil {
return nil , nil , nil , err
}
kubeconfig . ContentConfig . ContentType = s . ContentType
// Override kubeconfig qps/burst settings from flags
kubeconfig . QPS = s . KubeAPIQPS
kubeconfig . Burst = int ( s . KubeAPIBurst )
kubeClient , err := clientset . NewForConfig ( restclient . AddUserAgent ( kubeconfig , "controller-manager" ) )
if err != nil {
glog . Fatalf ( "Invalid API configuration: %v" , err )
}
2017-09-13 03:22:50 +00:00
leaderElectionClient := clientset . NewForConfigOrDie ( restclient . AddUserAgent ( kubeconfig , "leader-election" ) )
2017-08-28 10:15:49 +00:00
return kubeClient , leaderElectionClient , kubeconfig , nil
}
2016-12-02 19:18:16 +00:00
type ControllerContext struct {
// ClientBuilder will provide a client for this controller to use
ClientBuilder controller . ControllerClientBuilder
2017-02-06 18:35:50 +00:00
// InformerFactory gives access to informers for the controller.
2016-12-02 19:18:16 +00:00
InformerFactory informers . SharedInformerFactory
// Options provides access to init options for a given controller
Options options . CMServer
// AvailableResources is a map listing currently available resources
AvailableResources map [ schema . GroupVersionResource ] bool
2017-06-01 16:03:18 +00:00
// Cloud is the cloud provider interface for the controllers to use.
// It must be initialized and ready to use.
Cloud cloudprovider . Interface
2016-12-02 19:18:16 +00:00
// Stop is the stop channel
Stop <- chan struct { }
2017-08-24 16:39:55 +00:00
// InformersStarted is closed after all of the controllers have been initialized and are running. After this point it is safe,
// for an individual controller to start the shared informers. Before it is closed, they should not.
InformersStarted chan struct { }
2016-12-02 19:18:16 +00:00
}
2017-01-10 20:11:25 +00:00
func ( c ControllerContext ) IsControllerEnabled ( name string ) bool {
return IsControllerEnabled ( name , ControllersDisabledByDefault , c . Options . Controllers ... )
}
func IsControllerEnabled ( name string , disabledByDefaultControllers sets . String , controllers ... string ) bool {
hasStar := false
2017-03-17 06:25:10 +00:00
for _ , ctrl := range controllers {
if ctrl == name {
2017-01-10 20:11:25 +00:00
return true
}
2017-03-17 06:25:10 +00:00
if ctrl == "-" + name {
2017-01-10 20:11:25 +00:00
return false
}
2017-03-17 06:25:10 +00:00
if ctrl == "*" {
2017-01-10 20:11:25 +00:00
hasStar = true
}
}
// if we get here, there was no explicit choice
if ! hasStar {
// nothing on by default
return false
}
if disabledByDefaultControllers . Has ( name ) {
return false
}
return true
}
2016-12-02 19:18:16 +00:00
// InitFunc is used to launch a particular controller. It may run additional "should I activate checks".
// Any error returned will cause the controller process to `Fatal`
2016-12-05 14:04:32 +00:00
// The bool indicates whether the controller was enabled.
2016-12-02 19:18:16 +00:00
type InitFunc func ( ctx ControllerContext ) ( bool , error )
2017-01-10 20:11:25 +00:00
func KnownControllers ( ) [ ] string {
2017-05-08 18:16:05 +00:00
ret := sets . StringKeySet ( NewControllerInitializers ( ) )
2017-03-06 20:58:08 +00:00
2017-06-01 16:03:18 +00:00
// add "special" controllers that aren't initialized normally. These controllers cannot be initialized
// using a normal function. The only known special case is the SA token controller which *must* be started
// first to ensure that the SA tokens for future controllers will exist. Think very carefully before adding
// to this list.
2017-03-06 20:58:08 +00:00
ret . Insert (
saTokenControllerName ,
)
return ret . List ( )
2017-01-10 20:11:25 +00:00
}
2016-12-12 20:17:26 +00:00
var ControllersDisabledByDefault = sets . NewString (
"bootstrapsigner" ,
"tokencleaner" ,
)
2017-01-10 20:11:25 +00:00
2017-06-01 16:03:18 +00:00
const (
saTokenControllerName = "serviceaccount-token"
)
2017-05-08 18:16:05 +00:00
// NewControllerInitializers is a public map of named controller groups (you can start more than one in an init func)
// paired to their InitFunc. This allows for structured downstream composition and subdivision.
func NewControllerInitializers ( ) map [ string ] InitFunc {
2016-12-02 19:18:16 +00:00
controllers := map [ string ] InitFunc { }
controllers [ "endpoint" ] = startEndpointController
controllers [ "replicationcontroller" ] = startReplicationController
2016-12-05 14:04:32 +00:00
controllers [ "podgc" ] = startPodGCController
2016-12-02 19:18:16 +00:00
controllers [ "resourcequota" ] = startResourceQuotaController
controllers [ "namespace" ] = startNamespaceController
2016-12-05 14:04:32 +00:00
controllers [ "serviceaccount" ] = startServiceAccountController
controllers [ "garbagecollector" ] = startGarbageCollectorController
controllers [ "daemonset" ] = startDaemonSetController
controllers [ "job" ] = startJobController
controllers [ "deployment" ] = startDeploymentController
controllers [ "replicaset" ] = startReplicaSetController
controllers [ "horizontalpodautoscaling" ] = startHPAController
controllers [ "disruption" ] = startDisruptionController
2017-05-09 15:31:06 +00:00
controllers [ "statefulset" ] = startStatefulSetController
2016-12-05 14:04:32 +00:00
controllers [ "cronjob" ] = startCronJobController
2017-05-08 21:44:45 +00:00
controllers [ "csrsigning" ] = startCSRSigningController
controllers [ "csrapproving" ] = startCSRApprovingController
2017-08-30 16:58:04 +00:00
controllers [ "csrcleaner" ] = startCSRCleanerController
2017-01-30 10:48:15 +00:00
controllers [ "ttl" ] = startTTLController
2016-12-12 20:17:26 +00:00
controllers [ "bootstrapsigner" ] = startBootstrapSignerController
controllers [ "tokencleaner" ] = startTokenCleanerController
2017-06-01 16:03:18 +00:00
controllers [ "service" ] = startServiceController
controllers [ "node" ] = startNodeController
controllers [ "route" ] = startRouteController
controllers [ "persistentvolume-binder" ] = startPersistentVolumeBinderController
controllers [ "attachdetach" ] = startAttachDetachController
2017-09-04 07:02:34 +00:00
controllers [ "persistentvolume-expander" ] = startVolumeExpandController
2017-10-20 15:01:41 +00:00
controllers [ "clusterrole-aggregation" ] = startClusterRoleAggregrationController
2017-11-15 22:03:43 +00:00
controllers [ "pvc-protection" ] = startPVCProtectionController
2016-12-02 19:18:16 +00:00
return controllers
}
2016-11-21 20:00:20 +00:00
// TODO: In general, any controller checking this needs to be dynamic so
// users don't have to restart their controller manager if they change the apiserver.
2017-05-08 18:16:05 +00:00
// Until we get there, the structure here needs to be exposed for the construction of a proper ControllerContext.
func GetAvailableResources ( clientBuilder controller . ControllerClientBuilder ) ( map [ schema . GroupVersionResource ] bool , error ) {
2016-11-21 20:00:20 +00:00
var discoveryClient discovery . DiscoveryInterface
2017-08-07 16:55:29 +00:00
var healthzContent string
2016-11-21 20:00:20 +00:00
// If apiserver is not running we should wait for some time and fail only then. This is particularly
// important when we start apiserver and controller manager at the same time.
err := wait . PollImmediate ( time . Second , 10 * time . Second , func ( ) ( bool , error ) {
client , err := clientBuilder . Client ( "controller-discovery" )
if err != nil {
glog . Errorf ( "Failed to get api versions from server: %v" , err )
return false , nil
}
2017-03-16 15:05:03 +00:00
healthStatus := 0
2017-08-07 16:55:29 +00:00
resp := client . Discovery ( ) . RESTClient ( ) . Get ( ) . AbsPath ( "/healthz" ) . Do ( ) . StatusCode ( & healthStatus )
2017-03-16 15:05:03 +00:00
if healthStatus != http . StatusOK {
glog . Errorf ( "Server isn't healthy yet. Waiting a little while." )
return false , nil
}
2017-08-07 16:55:29 +00:00
content , _ := resp . Raw ( )
healthzContent = string ( content )
2017-03-16 15:05:03 +00:00
2016-11-21 20:00:20 +00:00
discoveryClient = client . Discovery ( )
return true , nil
} )
if err != nil {
2017-08-07 16:55:29 +00:00
return nil , fmt . Errorf ( "failed to get api versions from server: %v: %v" , healthzContent , err )
2016-11-21 20:00:20 +00:00
}
resourceMap , err := discoveryClient . ServerResources ( )
if err != nil {
2017-07-24 13:56:31 +00:00
utilruntime . HandleError ( fmt . Errorf ( "unable to get all supported resources from server: %v" , err ) )
}
if len ( resourceMap ) == 0 {
return nil , fmt . Errorf ( "unable to get any supported resources from server" )
2016-11-21 20:00:20 +00:00
}
allResources := map [ schema . GroupVersionResource ] bool { }
for _ , apiResourceList := range resourceMap {
version , err := schema . ParseGroupVersion ( apiResourceList . GroupVersion )
if err != nil {
return nil , err
}
for _ , apiResource := range apiResourceList . APIResources {
allResources [ version . WithResource ( apiResource . Name ) ] = true
}
}
return allResources , nil
}
2017-09-13 00:27:36 +00:00
// CreateControllerContext creates a context struct containing references to resources needed by the
// controllers such as the cloud provider and clientBuilder. rootClientBuilder is only used for
// the shared-informers client and token controller.
2017-06-01 16:03:18 +00:00
func CreateControllerContext ( s * options . CMServer , rootClientBuilder , clientBuilder controller . ControllerClientBuilder , stop <- chan struct { } ) ( ControllerContext , error ) {
2017-02-06 18:35:50 +00:00
versionedClient := rootClientBuilder . ClientOrDie ( "shared-informers" )
2017-02-24 14:52:43 +00:00
sharedInformers := informers . NewSharedInformerFactory ( versionedClient , ResyncPeriod ( s ) ( ) )
2016-08-04 07:06:29 +00:00
2017-06-01 16:03:18 +00:00
availableResources , err := GetAvailableResources ( rootClientBuilder )
if err != nil {
return ControllerContext { } , err
2016-09-22 19:53:08 +00:00
}
2017-06-01 16:03:18 +00:00
cloud , err := cloudprovider . InitCloudProvider ( s . CloudProvider , s . CloudConfigFile )
2016-11-21 20:00:20 +00:00
if err != nil {
2017-06-01 16:03:18 +00:00
return ControllerContext { } , fmt . Errorf ( "cloud provider could not be initialized: %v" , err )
}
2017-09-13 00:27:36 +00:00
if cloud != nil && cloud . HasClusterID ( ) == false {
if s . AllowUntaggedCloud == true {
glog . Warning ( "detected a cluster without a ClusterID. A ClusterID will be required in the future. Please tag your cluster to avoid any future issues" )
} else {
return ControllerContext { } , fmt . Errorf ( "no ClusterID Found. A ClusterID is required for the cloud provider to function properly. This check can be bypassed by setting the allow-untagged-cloud option" )
2017-07-17 04:28:57 +00:00
}
2016-11-21 20:00:20 +00:00
}
2017-09-12 03:20:29 +00:00
if informerUserCloud , ok := cloud . ( cloudprovider . InformerUser ) ; ok {
informerUserCloud . SetInformers ( sharedInformers )
}
2016-12-02 19:18:16 +00:00
ctx := ControllerContext {
ClientBuilder : clientBuilder ,
InformerFactory : sharedInformers ,
Options : * s ,
AvailableResources : availableResources ,
2017-06-01 16:03:18 +00:00
Cloud : cloud ,
2016-12-02 19:18:16 +00:00
Stop : stop ,
2017-08-24 16:39:55 +00:00
InformersStarted : make ( chan struct { } ) ,
2016-12-02 19:18:16 +00:00
}
2017-06-01 16:03:18 +00:00
return ctx , nil
}
func StartControllers ( ctx ControllerContext , startSATokenController InitFunc , controllers map [ string ] InitFunc ) error {
// Always start the SA token controller first using a full-power client, since it needs to mint tokens for the rest
// If this fails, just return here and fail since other controllers won't be able to get credentials.
if _ , err := startSATokenController ( ctx ) ; err != nil {
return err
}
2015-01-30 23:31:36 +00:00
2017-09-13 00:27:36 +00:00
// Initialize the cloud provider with a reference to the clientBuilder only after token controller
// has started in case the cloud provider uses the client builder.
if ctx . Cloud != nil {
ctx . Cloud . Initialize ( ctx . ClientBuilder )
}
2016-12-02 19:18:16 +00:00
for controllerName , initFn := range controllers {
2017-01-10 20:11:25 +00:00
if ! ctx . IsControllerEnabled ( controllerName ) {
glog . Warningf ( "%q is disabled" , controllerName )
continue
}
2017-06-01 16:03:18 +00:00
time . Sleep ( wait . Jitter ( ctx . Options . ControllerStartInterval . Duration , ControllerStartJitter ) )
2015-01-30 23:31:36 +00:00
2016-12-02 19:18:16 +00:00
glog . V ( 1 ) . Infof ( "Starting %q" , controllerName )
started , err := initFn ( ctx )
if err != nil {
glog . Errorf ( "Error starting %q" , controllerName )
return err
}
if ! started {
glog . Warningf ( "Skipping %q" , controllerName )
2017-01-14 00:27:00 +00:00
continue
2016-12-02 19:18:16 +00:00
}
glog . Infof ( "Started %q" , controllerName )
}
2015-09-21 22:51:27 +00:00
2017-06-01 16:03:18 +00:00
return nil
}
2015-02-07 19:53:42 +00:00
2017-06-01 16:03:18 +00:00
// serviceAccountTokenControllerStarter is special because it must run first to set up permissions for other controllers.
// It cannot use the "normal" client builder, so it tracks its own. It must also avoid being included in the "normal"
// init map so that it can always run first.
type serviceAccountTokenControllerStarter struct {
rootClientBuilder controller . ControllerClientBuilder
}
2017-05-17 21:38:25 +00:00
2017-06-01 16:03:18 +00:00
func ( c serviceAccountTokenControllerStarter ) startServiceAccountTokenController ( ctx ControllerContext ) ( bool , error ) {
if ! ctx . IsControllerEnabled ( saTokenControllerName ) {
glog . Warningf ( "%q is disabled" , saTokenControllerName )
return false , nil
2015-03-24 17:32:43 +00:00
}
2017-06-01 16:03:18 +00:00
if len ( ctx . Options . ServiceAccountKeyFile ) == 0 {
glog . Warningf ( "%q is disabled because there is no private key" , saTokenControllerName )
return false , nil
2015-05-15 21:49:26 +00:00
}
2017-07-19 03:06:32 +00:00
privateKey , err := certutil . PrivateKeyFromFile ( ctx . Options . ServiceAccountKeyFile )
2017-06-01 16:03:18 +00:00
if err != nil {
return true , fmt . Errorf ( "error reading key for service account token controller: %v" , err )
2016-09-26 12:15:25 +00:00
}
2015-04-16 17:26:08 +00:00
2017-06-01 16:03:18 +00:00
var rootCA [ ] byte
if ctx . Options . RootCAFile != "" {
rootCA , err = ioutil . ReadFile ( ctx . Options . RootCAFile )
if err != nil {
return true , fmt . Errorf ( "error reading root-ca-file at %s: %v" , ctx . Options . RootCAFile , err )
2017-03-06 20:58:08 +00:00
}
2017-06-01 16:03:18 +00:00
if _ , err := certutil . ParseCertsPEM ( rootCA ) ; err != nil {
return true , fmt . Errorf ( "error parsing root-ca-file at %s: %v" , ctx . Options . RootCAFile , err )
2017-03-13 07:41:59 +00:00
}
2017-03-06 20:58:08 +00:00
} else {
2017-06-01 16:03:18 +00:00
rootCA = c . rootClientBuilder . ConfigOrDie ( "tokens-controller" ) . CAData
2017-01-06 22:24:51 +00:00
}
2017-10-31 14:19:55 +00:00
controller , err := serviceaccountcontroller . NewTokensController (
2017-06-01 16:03:18 +00:00
ctx . InformerFactory . Core ( ) . V1 ( ) . ServiceAccounts ( ) ,
ctx . InformerFactory . Core ( ) . V1 ( ) . Secrets ( ) ,
c . rootClientBuilder . ClientOrDie ( "tokens-controller" ) ,
serviceaccountcontroller . TokensControllerOptions {
TokenGenerator : serviceaccount . JWTTokenGenerator ( privateKey ) ,
RootCA : rootCA ,
} ,
)
2017-10-31 14:19:55 +00:00
if err != nil {
return true , fmt . Errorf ( "error creating Tokens controller: %v" , err )
}
2017-06-01 16:03:18 +00:00
go controller . Run ( int ( ctx . Options . ConcurrentSATokenSyncs ) , ctx . Stop )
2016-04-30 06:36:27 +00:00
2017-06-01 16:03:18 +00:00
// start the first set of informers now so that other controllers can start
ctx . InformerFactory . Start ( ctx . Stop )
2016-04-14 18:00:52 +00:00
2017-06-01 16:03:18 +00:00
return true , nil
2015-01-30 23:31:36 +00:00
}