2016-10-08 00:39:21 +00:00
/ *
Copyright 2016 The Kubernetes Authors .
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 .
* /
package app
import (
2018-01-17 18:41:01 +00:00
"fmt"
2016-10-08 00:39:21 +00:00
"math/rand"
"net"
"os"
2017-07-13 13:08:43 +00:00
"strings"
2016-10-08 00:39:21 +00:00
"time"
2018-01-17 18:41:01 +00:00
"github.com/golang/glog"
"github.com/spf13/cobra"
2018-01-15 19:21:15 +00:00
"k8s.io/apimachinery/pkg/util/uuid"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/util/wait"
2017-06-23 20:56:37 +00:00
"k8s.io/client-go/informers"
2017-07-08 00:19:03 +00:00
"k8s.io/client-go/kubernetes"
2017-01-19 18:27:59 +00:00
restclient "k8s.io/client-go/rest"
2017-07-07 20:59:32 +00:00
"k8s.io/client-go/tools/leaderelection"
"k8s.io/client-go/tools/leaderelection/resourcelock"
2017-01-30 18:39:54 +00:00
"k8s.io/client-go/tools/record"
2018-02-05 06:48:30 +00:00
cloudcontrollerconfig "k8s.io/kubernetes/cmd/cloud-controller-manager/app/config"
2016-10-08 00:39:21 +00:00
"k8s.io/kubernetes/cmd/cloud-controller-manager/app/options"
2018-02-05 06:48:30 +00:00
genericcontrollermanager "k8s.io/kubernetes/cmd/controller-manager/app"
2016-10-08 00:39:21 +00:00
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/controller"
2017-04-17 20:13:55 +00:00
cloudcontrollers "k8s.io/kubernetes/pkg/controller/cloud"
2016-10-08 00:39:21 +00:00
routecontroller "k8s.io/kubernetes/pkg/controller/route"
servicecontroller "k8s.io/kubernetes/pkg/controller/service"
"k8s.io/kubernetes/pkg/util/configz"
2018-02-14 16:17:28 +00:00
utilflag "k8s.io/kubernetes/pkg/util/flag"
2018-01-17 18:41:01 +00:00
"k8s.io/kubernetes/pkg/version/verflag"
2016-10-08 00:39:21 +00:00
)
const (
2017-10-03 05:45:14 +00:00
// ControllerStartJitter is the jitter value used when starting controller managers.
2016-10-08 00:39:21 +00:00
ControllerStartJitter = 1.0
)
2016-12-17 17:27:48 +00:00
// NewCloudControllerManagerCommand creates a *cobra.Command object with default parameters
func NewCloudControllerManagerCommand ( ) * cobra . Command {
2018-02-05 06:48:30 +00:00
s := options . NewCloudControllerManagerOptions ( )
2016-10-08 00:39:21 +00:00
cmd := & cobra . Command {
Use : "cloud-controller-manager" ,
2016-12-17 17:27:48 +00:00
Long : ` The Cloud controller manager is a daemon that embeds
the cloud specific control loops shipped with Kubernetes . ` ,
2016-10-08 00:39:21 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2018-01-17 18:41:01 +00:00
verflag . PrintAndExitIfRequested ( )
2018-02-14 16:17:28 +00:00
utilflag . PrintFlags ( cmd . Flags ( ) )
2018-01-17 18:41:01 +00:00
2018-02-05 06:48:30 +00:00
c , err := s . Config ( )
if err != nil {
fmt . Fprintf ( os . Stderr , "%v\n" , err )
os . Exit ( 1 )
}
if err := Run ( c . Complete ( ) ) ; err != nil {
2018-01-17 18:41:01 +00:00
fmt . Fprintf ( os . Stderr , "%v\n" , err )
os . Exit ( 1 )
}
2016-10-08 00:39:21 +00:00
} ,
}
2018-01-17 18:41:01 +00:00
s . AddFlags ( cmd . Flags ( ) )
2016-10-08 00:39:21 +00:00
return cmd
}
2017-01-03 23:33:01 +00:00
// resyncPeriod computes the time interval a shared informer waits before resyncing with the api server
2018-02-05 06:48:30 +00:00
func resyncPeriod ( c * cloudcontrollerconfig . CompletedConfig ) func ( ) time . Duration {
2016-10-08 00:39:21 +00:00
return func ( ) time . Duration {
factor := rand . Float64 ( ) + 1
2018-02-05 06:48:30 +00:00
return time . Duration ( float64 ( c . Generic . ComponentConfig . MinResyncPeriod . Nanoseconds ( ) ) * factor )
2016-10-08 00:39:21 +00:00
}
}
// Run runs the ExternalCMServer. This should never exit.
2018-02-05 06:48:30 +00:00
func Run ( c * cloudcontrollerconfig . CompletedConfig ) error {
cloud , err := cloudprovider . InitCloudProvider ( c . Generic . ComponentConfig . CloudProvider , c . Generic . ComponentConfig . CloudConfigFile )
2017-10-27 17:55:31 +00:00
if err != nil {
glog . Fatalf ( "Cloud provider could not be initialized: %v" , err )
}
2017-11-27 13:15:10 +00:00
if cloud == nil {
glog . Fatalf ( "cloud provider is nil" )
}
2017-10-27 17:55:31 +00:00
if cloud . HasClusterID ( ) == false {
2018-02-05 06:48:30 +00:00
if c . Generic . ComponentConfig . AllowUntaggedCloud == true {
2017-10-27 17:55:31 +00:00
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 {
glog . Fatalf ( "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" )
}
}
2018-02-05 06:48:30 +00:00
// setup /configz endpoint
if cz , err := configz . New ( "componentconfig" ) ; err == nil {
cz . Set ( c . Generic . ComponentConfig )
2016-10-08 00:39:21 +00:00
} else {
2018-02-05 06:48:30 +00:00
glog . Errorf ( "unable to register configz: %c" , err )
2016-10-08 00:39:21 +00:00
}
2018-02-05 06:48:30 +00:00
// Start the controller manager HTTP server
stopCh := make ( chan struct { } )
2018-02-08 18:28:31 +00:00
if c . Generic . SecureServing != nil {
if err := genericcontrollermanager . Serve ( & c . Generic , c . Generic . SecureServing . Serve , stopCh ) ; err != nil {
return err
}
}
2018-02-05 06:48:30 +00:00
if c . Generic . InsecureServing != nil {
if err := genericcontrollermanager . Serve ( & c . Generic , c . Generic . InsecureServing . Serve , stopCh ) ; err != nil {
return err
}
2016-10-08 00:39:21 +00:00
}
run := func ( stop <- chan struct { } ) {
rootClientBuilder := controller . SimpleControllerClientBuilder {
2018-02-05 06:48:30 +00:00
ClientConfig : c . Generic . Kubeconfig ,
2016-10-08 00:39:21 +00:00
}
var clientBuilder controller . ControllerClientBuilder
2018-02-05 06:48:30 +00:00
if c . Generic . ComponentConfig . UseServiceAccountCredentials {
2016-10-08 00:39:21 +00:00
clientBuilder = controller . SAControllerClientBuilder {
2018-02-05 06:48:30 +00:00
ClientConfig : restclient . AnonymousClientConfig ( c . Generic . Kubeconfig ) ,
CoreClient : c . Generic . Client . CoreV1 ( ) ,
AuthenticationClient : c . Generic . Client . AuthenticationV1 ( ) ,
2017-02-17 20:48:22 +00:00
Namespace : "kube-system" ,
2016-10-08 00:39:21 +00:00
}
} else {
clientBuilder = rootClientBuilder
}
2018-02-05 06:48:30 +00:00
if err := startControllers ( c , c . Generic . Kubeconfig , rootClientBuilder , clientBuilder , stop , c . Generic . EventRecorder , cloud ) ; err != nil {
2017-11-26 03:03:14 +00:00
glog . Fatalf ( "error running controllers: %v" , err )
}
2016-10-08 00:39:21 +00:00
}
2018-02-05 06:48:30 +00:00
if ! c . Generic . ComponentConfig . LeaderElection . LeaderElect {
2016-10-08 00:39:21 +00:00
run ( nil )
panic ( "unreachable" )
}
2016-12-17 17:27:48 +00:00
// Identity used to distinguish between multiple cloud controller manager instances
2016-10-08 00:39:21 +00:00
id , err := os . Hostname ( )
if err != nil {
return err
}
2018-01-15 19:21:15 +00:00
// add a uniquifier so that two processes on the same host don't accidentally both become active
id = id + "_" + string ( uuid . NewUUID ( ) )
2016-10-08 00:39:21 +00:00
2016-12-17 17:27:48 +00:00
// Lock required for leader election
2018-02-05 06:48:30 +00:00
rl , err := resourcelock . New ( c . Generic . ComponentConfig . LeaderElection . ResourceLock ,
2017-11-06 00:06:06 +00:00
"kube-system" ,
"cloud-controller-manager" ,
2018-02-05 06:48:30 +00:00
c . Generic . LeaderElectionClient . CoreV1 ( ) ,
2017-11-06 00:06:06 +00:00
resourcelock . ResourceLockConfig {
2017-12-05 02:48:38 +00:00
Identity : id ,
2018-02-05 06:48:30 +00:00
EventRecorder : c . Generic . EventRecorder ,
2017-11-06 00:06:06 +00:00
} )
if err != nil {
glog . Fatalf ( "error creating lock: %v" , err )
2016-10-08 00:39:21 +00:00
}
2016-12-17 17:27:48 +00:00
// Try and become the leader and start cloud controller manager loops
2016-10-08 00:39:21 +00:00
leaderelection . RunOrDie ( leaderelection . LeaderElectionConfig {
2017-11-06 00:06:06 +00:00
Lock : rl ,
2018-02-05 06:48:30 +00:00
LeaseDuration : c . Generic . ComponentConfig . LeaderElection . LeaseDuration . Duration ,
RenewDeadline : c . Generic . ComponentConfig . LeaderElection . RenewDeadline . Duration ,
RetryPeriod : c . Generic . ComponentConfig . LeaderElection . RetryPeriod . Duration ,
2016-10-08 00:39:21 +00:00
Callbacks : leaderelection . LeaderCallbacks {
OnStartedLeading : run ,
OnStoppedLeading : func ( ) {
glog . Fatalf ( "leaderelection lost" )
} ,
} ,
} )
panic ( "unreachable" )
}
2018-02-05 06:48:30 +00:00
// startControllers starts the cloud specific controller loops.
func startControllers ( c * cloudcontrollerconfig . CompletedConfig , kubeconfig * restclient . Config , rootClientBuilder , clientBuilder controller . ControllerClientBuilder , stop <- chan struct { } , recorder record . EventRecorder , cloud cloudprovider . Interface ) error {
2016-12-17 17:27:48 +00:00
// Function to build the kube client object
2018-01-11 11:15:11 +00:00
client := func ( serviceAccountName string ) kubernetes . Interface {
2017-08-17 05:41:37 +00:00
return clientBuilder . ClientOrDie ( serviceAccountName )
2016-10-08 00:39:21 +00:00
}
2017-05-17 21:38:25 +00:00
if cloud != nil {
// Initialize the cloud provider with a reference to the clientBuilder
cloud . Initialize ( clientBuilder )
}
2018-02-05 06:48:30 +00:00
// TODO: move this setup into Config
2017-09-12 22:47:03 +00:00
versionedClient := rootClientBuilder . ClientOrDie ( "shared-informers" )
2018-02-05 06:48:30 +00:00
sharedInformers := informers . NewSharedInformerFactory ( versionedClient , resyncPeriod ( c ) ( ) )
2016-10-08 00:39:21 +00:00
2016-12-17 17:27:48 +00:00
// Start the CloudNodeController
2017-04-17 20:13:55 +00:00
nodeController := cloudcontrollers . NewCloudNodeController (
2017-02-24 14:52:43 +00:00
sharedInformers . Core ( ) . V1 ( ) . Nodes ( ) ,
2016-10-08 00:39:21 +00:00
client ( "cloud-node-controller" ) , cloud ,
2018-02-05 06:48:30 +00:00
c . Generic . ComponentConfig . NodeMonitorPeriod . Duration ,
c . Extra . NodeStatusUpdateFrequency )
2017-02-06 07:33:27 +00:00
2016-10-08 00:39:21 +00:00
nodeController . Run ( )
2018-02-05 06:48:30 +00:00
time . Sleep ( wait . Jitter ( c . Generic . ComponentConfig . ControllerStartInterval . Duration , ControllerStartJitter ) )
2016-10-08 00:39:21 +00:00
2017-04-17 20:13:55 +00:00
// Start the PersistentVolumeLabelController
pvlController := cloudcontrollers . NewPersistentVolumeLabelController ( client ( "pvl-controller" ) , cloud )
threads := 5
go pvlController . Run ( threads , stop )
2018-02-05 06:48:30 +00:00
time . Sleep ( wait . Jitter ( c . Generic . ComponentConfig . ControllerStartInterval . Duration , ControllerStartJitter ) )
2017-04-17 20:13:55 +00:00
2016-12-17 17:27:48 +00:00
// Start the service controller
2017-02-13 19:28:12 +00:00
serviceController , err := servicecontroller . New (
cloud ,
client ( "service-controller" ) ,
2017-02-24 14:52:43 +00:00
sharedInformers . Core ( ) . V1 ( ) . Services ( ) ,
sharedInformers . Core ( ) . V1 ( ) . Nodes ( ) ,
2018-02-05 06:48:30 +00:00
c . Generic . ComponentConfig . ClusterName ,
2017-02-13 19:28:12 +00:00
)
2016-10-08 00:39:21 +00:00
if err != nil {
glog . Errorf ( "Failed to start service controller: %v" , err )
} else {
2018-02-05 06:48:30 +00:00
go serviceController . Run ( stop , int ( c . Generic . ComponentConfig . ConcurrentServiceSyncs ) )
time . Sleep ( wait . Jitter ( c . Generic . ComponentConfig . ControllerStartInterval . Duration , ControllerStartJitter ) )
2016-10-08 00:39:21 +00:00
}
2016-12-17 17:27:48 +00:00
// If CIDRs should be allocated for pods and set on the CloudProvider, then start the route controller
2018-02-05 06:48:30 +00:00
if c . Generic . ComponentConfig . AllocateNodeCIDRs && c . Generic . ComponentConfig . ConfigureCloudRoutes {
2016-10-08 00:39:21 +00:00
if routes , ok := cloud . Routes ( ) ; ! ok {
glog . Warning ( "configure-cloud-routes is set, but cloud provider does not support routes. Will not configure cloud provider routes." )
} else {
2017-07-13 13:08:43 +00:00
var clusterCIDR * net . IPNet
2018-02-05 06:48:30 +00:00
if len ( strings . TrimSpace ( c . Generic . ComponentConfig . ClusterCIDR ) ) != 0 {
_ , clusterCIDR , err = net . ParseCIDR ( c . Generic . ComponentConfig . ClusterCIDR )
2017-07-13 13:08:43 +00:00
if err != nil {
2018-02-05 06:48:30 +00:00
glog . Warningf ( "Unsuccessful parsing of cluster CIDR %v: %v" , c . Generic . ComponentConfig . ClusterCIDR , err )
2017-07-13 13:08:43 +00:00
}
}
2018-02-05 06:48:30 +00:00
routeController := routecontroller . New ( routes , client ( "route-controller" ) , sharedInformers . Core ( ) . V1 ( ) . Nodes ( ) , c . Generic . ComponentConfig . ClusterName , clusterCIDR )
go routeController . Run ( stop , c . Generic . ComponentConfig . RouteReconciliationPeriod . Duration )
time . Sleep ( wait . Jitter ( c . Generic . ComponentConfig . ControllerStartInterval . Duration , ControllerStartJitter ) )
2016-10-08 00:39:21 +00:00
}
} else {
2018-02-05 06:48:30 +00:00
glog . Infof ( "Will not configure cloud provider routes for allocate-node-cidrs: %v, configure-cloud-routes: %v." , c . Generic . ComponentConfig . AllocateNodeCIDRs , c . Generic . ComponentConfig . ConfigureCloudRoutes )
2016-10-08 00:39:21 +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 ) {
2017-06-14 03:54:23 +00:00
if _ , err = restclient . ServerAPIVersions ( kubeconfig ) ; err == nil {
2016-10-08 00:39:21 +00:00
return true , nil
}
glog . Errorf ( "Failed to get api versions from server: %v" , err )
return false , nil
} )
if err != nil {
glog . Fatalf ( "Failed to get api versions from server: %v" , err )
}
sharedInformers . Start ( stop )
select { }
}