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