2018-01-09 18:57:14 +00:00
/ *
Copyright 2014 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 implements a Server object for running the scheduler.
package app
import (
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"reflect"
goruntime "runtime"
"strconv"
"time"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2018-03-06 22:33:18 +00:00
"k8s.io/apimachinery/pkg/util/uuid"
2018-01-09 18:57:14 +00:00
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/server/healthz"
2018-03-06 22:33:18 +00:00
"k8s.io/apiserver/pkg/server/mux"
"k8s.io/apiserver/pkg/server/routes"
2018-01-09 18:57:14 +00:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/informers"
coreinformers "k8s.io/client-go/informers/core/v1"
storageinformers "k8s.io/client-go/informers/storage/v1"
clientset "k8s.io/client-go/kubernetes"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/tools/leaderelection"
"k8s.io/client-go/tools/leaderelection/resourcelock"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/componentconfig"
componentconfigv1alpha1 "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1"
"k8s.io/kubernetes/pkg/client/leaderelectionconfig"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/features"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/master/ports"
2018-03-06 22:33:18 +00:00
"k8s.io/kubernetes/pkg/scheduler"
"k8s.io/kubernetes/pkg/scheduler/algorithmprovider"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
latestschedulerapi "k8s.io/kubernetes/pkg/scheduler/api/latest"
"k8s.io/kubernetes/pkg/scheduler/factory"
2018-01-09 18:57:14 +00:00
"k8s.io/kubernetes/pkg/util/configz"
2018-03-06 22:33:18 +00:00
utilflag "k8s.io/kubernetes/pkg/util/flag"
2018-01-09 18:57:14 +00:00
"k8s.io/kubernetes/pkg/version"
"k8s.io/kubernetes/pkg/version/verflag"
"github.com/golang/glog"
2018-03-06 22:33:18 +00:00
"github.com/prometheus/client_golang/prometheus"
2018-01-09 18:57:14 +00:00
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// SchedulerServer has all the context and params needed to run a Scheduler
type Options struct {
// ConfigFile is the location of the scheduler server's configuration file.
ConfigFile string
// config is the scheduler server's configuration object.
config * componentconfig . KubeSchedulerConfiguration
scheme * runtime . Scheme
codecs serializer . CodecFactory
// The fields below here are placeholders for flags that can't be directly
// mapped into componentconfig.KubeSchedulerConfiguration.
//
// TODO remove these fields once the deprecated flags are removed.
// master is the address of the Kubernetes API server (overrides any
// value in kubeconfig).
master string
healthzAddress string
healthzPort int32
policyConfigFile string
policyConfigMapName string
policyConfigMapNamespace string
useLegacyPolicyConfig bool
algorithmProvider string
}
// AddFlags adds flags for a specific SchedulerServer to the specified FlagSet
2018-03-06 22:33:18 +00:00
func ( o * Options ) AddFlags ( fs * pflag . FlagSet ) {
fs . StringVar ( & o . ConfigFile , "config" , o . ConfigFile , "The path to the configuration file." )
2018-01-09 18:57:14 +00:00
// All flags below here are deprecated and will eventually be removed.
2018-03-06 22:33:18 +00:00
fs . Int32Var ( & o . healthzPort , "port" , ports . SchedulerPort , "The port that the scheduler's http service runs on" )
fs . StringVar ( & o . healthzAddress , "address" , o . healthzAddress , "The IP address to serve on (set to 0.0.0.0 for all IPv4 interfaces and :: for all IPv6 interfaces)." )
fs . StringVar ( & o . algorithmProvider , "algorithm-provider" , o . algorithmProvider , "The scheduling algorithm provider to use, one of: " + factory . ListAlgorithmProviders ( ) )
fs . StringVar ( & o . policyConfigFile , "policy-config-file" , o . policyConfigFile , "File with scheduler policy configuration. This file is used if policy ConfigMap is not provided or --use-legacy-policy-config==true" )
2018-01-09 18:57:14 +00:00
usage := fmt . Sprintf ( "Name of the ConfigMap object that contains scheduler's policy configuration. It must exist in the system namespace before scheduler initialization if --use-legacy-policy-config==false. The config must be provided as the value of an element in 'Data' map with the key='%v'" , componentconfig . SchedulerPolicyConfigMapKey )
2018-03-06 22:33:18 +00:00
fs . StringVar ( & o . policyConfigMapName , "policy-configmap" , o . policyConfigMapName , usage )
fs . StringVar ( & o . policyConfigMapNamespace , "policy-configmap-namespace" , o . policyConfigMapNamespace , "The namespace where policy ConfigMap is located. The system namespace will be used if this is not provided or is empty." )
fs . BoolVar ( & o . useLegacyPolicyConfig , "use-legacy-policy-config" , false , "When set to true, scheduler will ignore policy ConfigMap and uses policy config file" )
fs . BoolVar ( & o . config . EnableProfiling , "profiling" , o . config . EnableProfiling , "Enable profiling via web interface host:port/debug/pprof/" )
fs . BoolVar ( & o . config . EnableContentionProfiling , "contention-profiling" , o . config . EnableContentionProfiling , "Enable lock contention profiling, if profiling is enabled" )
fs . StringVar ( & o . master , "master" , o . master , "The address of the Kubernetes API server (overrides any value in kubeconfig)" )
fs . StringVar ( & o . config . ClientConnection . KubeConfigFile , "kubeconfig" , o . config . ClientConnection . KubeConfigFile , "Path to kubeconfig file with authorization and master location information." )
fs . StringVar ( & o . config . ClientConnection . ContentType , "kube-api-content-type" , o . config . ClientConnection . ContentType , "Content type of requests sent to apiserver." )
fs . Float32Var ( & o . config . ClientConnection . QPS , "kube-api-qps" , o . config . ClientConnection . QPS , "QPS to use while talking with kubernetes apiserver" )
fs . Int32Var ( & o . config . ClientConnection . Burst , "kube-api-burst" , o . config . ClientConnection . Burst , "Burst to use while talking with kubernetes apiserver" )
fs . StringVar ( & o . config . SchedulerName , "scheduler-name" , o . config . SchedulerName , "Name of the scheduler, used to select which pods will be processed by this scheduler, based on pod's \"spec.SchedulerName\"." )
fs . StringVar ( & o . config . LeaderElection . LockObjectNamespace , "lock-object-namespace" , o . config . LeaderElection . LockObjectNamespace , "Define the namespace of the lock object." )
fs . StringVar ( & o . config . LeaderElection . LockObjectName , "lock-object-name" , o . config . LeaderElection . LockObjectName , "Define the name of the lock object." )
fs . Int32Var ( & o . config . HardPodAffinitySymmetricWeight , "hard-pod-affinity-symmetric-weight" , o . config . HardPodAffinitySymmetricWeight ,
2018-01-09 18:57:14 +00:00
"RequiredDuringScheduling affinity is not symmetric, but there is an implicit PreferredDuringScheduling affinity rule corresponding " +
"to every RequiredDuringScheduling affinity rule. --hard-pod-affinity-symmetric-weight represents the weight of implicit PreferredDuringScheduling affinity rule." )
fs . MarkDeprecated ( "hard-pod-affinity-symmetric-weight" , "This option was moved to the policy configuration file" )
2018-03-06 22:33:18 +00:00
fs . StringVar ( & o . config . FailureDomains , "failure-domains" , o . config . FailureDomains , "Indicate the \"all topologies\" set for an empty topologyKey when it's used for PreferredDuringScheduling pod anti-affinity." )
2018-01-09 18:57:14 +00:00
fs . MarkDeprecated ( "failure-domains" , "Doesn't have any effect. Will be removed in future version." )
2018-03-06 22:33:18 +00:00
leaderelectionconfig . BindFlags ( & o . config . LeaderElection . LeaderElectionConfiguration , fs )
2018-01-09 18:57:14 +00:00
utilfeature . DefaultFeatureGate . AddFlag ( fs )
}
func NewOptions ( ) ( * Options , error ) {
o := & Options {
config : new ( componentconfig . KubeSchedulerConfiguration ) ,
}
o . scheme = runtime . NewScheme ( )
o . codecs = serializer . NewCodecFactory ( o . scheme )
if err := componentconfig . AddToScheme ( o . scheme ) ; err != nil {
return nil , err
}
if err := componentconfigv1alpha1 . AddToScheme ( o . scheme ) ; err != nil {
return nil , err
}
return o , nil
}
func ( o * Options ) Complete ( ) error {
if len ( o . ConfigFile ) == 0 {
2018-03-06 22:33:18 +00:00
glog . Warning ( "WARNING: all flags other than --config are deprecated. Please begin using a config file ASAP." )
2018-01-09 18:57:14 +00:00
o . applyDeprecatedHealthzAddressToConfig ( )
o . applyDeprecatedHealthzPortToConfig ( )
o . applyDeprecatedAlgorithmSourceOptionsToConfig ( )
}
return nil
}
// applyDeprecatedHealthzAddressToConfig sets o.config.HealthzBindAddress and
// o.config.MetricsBindAddress from flags passed on the command line based on
// the following rules:
//
// 1. If --address is empty, leave the config as-is.
// 2. Otherwise, use the value of --address for the address portion of
// o.config.HealthzBindAddress
func ( o * Options ) applyDeprecatedHealthzAddressToConfig ( ) {
if len ( o . healthzAddress ) == 0 {
return
}
_ , port , err := net . SplitHostPort ( o . config . HealthzBindAddress )
if err != nil {
glog . Fatalf ( "invalid healthz bind address %q: %v" , o . config . HealthzBindAddress , err )
}
o . config . HealthzBindAddress = net . JoinHostPort ( o . healthzAddress , port )
o . config . MetricsBindAddress = net . JoinHostPort ( o . healthzAddress , port )
}
// applyDeprecatedHealthzPortToConfig sets o.config.HealthzBindAddress and
// o.config.MetricsBindAddress from flags passed on the command line based on
// the following rules:
//
// 1. If --port is -1, disable the healthz server.
// 2. Otherwise, use the value of --port for the port portion of
// o.config.HealthzBindAddress
func ( o * Options ) applyDeprecatedHealthzPortToConfig ( ) {
if o . healthzPort == - 1 {
o . config . HealthzBindAddress = ""
2018-03-06 22:33:18 +00:00
o . config . MetricsBindAddress = ""
2018-01-09 18:57:14 +00:00
return
}
host , _ , err := net . SplitHostPort ( o . config . HealthzBindAddress )
if err != nil {
glog . Fatalf ( "invalid healthz bind address %q: %v" , o . config . HealthzBindAddress , err )
}
o . config . HealthzBindAddress = net . JoinHostPort ( host , strconv . Itoa ( int ( o . healthzPort ) ) )
o . config . MetricsBindAddress = net . JoinHostPort ( host , strconv . Itoa ( int ( o . healthzPort ) ) )
}
// applyDeprecatedAlgorithmSourceOptionsToConfig sets o.config.AlgorithmSource from
// flags passed on the command line in the following precedence order:
//
// 1. --use-legacy-policy-config to use a policy file.
// 2. --policy-configmap to use a policy config map value.
// 3. --algorithm-provider to use a named algorithm provider.
func ( o * Options ) applyDeprecatedAlgorithmSourceOptionsToConfig ( ) {
switch {
2018-03-06 22:33:18 +00:00
case o . useLegacyPolicyConfig || ( len ( o . policyConfigFile ) > 0 && o . policyConfigMapName == "" ) :
2018-01-09 18:57:14 +00:00
o . config . AlgorithmSource = componentconfig . SchedulerAlgorithmSource {
Policy : & componentconfig . SchedulerPolicySource {
File : & componentconfig . SchedulerPolicyFileSource {
Path : o . policyConfigFile ,
} ,
} ,
}
case len ( o . policyConfigMapName ) > 0 :
o . config . AlgorithmSource = componentconfig . SchedulerAlgorithmSource {
Policy : & componentconfig . SchedulerPolicySource {
ConfigMap : & componentconfig . SchedulerPolicyConfigMapSource {
Name : o . policyConfigMapName ,
Namespace : o . policyConfigMapNamespace ,
} ,
} ,
}
case len ( o . algorithmProvider ) > 0 :
o . config . AlgorithmSource = componentconfig . SchedulerAlgorithmSource {
Provider : & o . algorithmProvider ,
}
}
}
// Validate validates all the required options.
func ( o * Options ) Validate ( args [ ] string ) error {
if len ( args ) != 0 {
return errors . New ( "no arguments are supported" )
}
return nil
}
// loadConfigFromFile loads the contents of file and decodes it as a
// KubeSchedulerConfiguration object.
func ( o * Options ) loadConfigFromFile ( file string ) ( * componentconfig . KubeSchedulerConfiguration , error ) {
data , err := ioutil . ReadFile ( file )
if err != nil {
return nil , err
}
return o . loadConfig ( data )
}
// loadConfig decodes data as a KubeSchedulerConfiguration object.
func ( o * Options ) loadConfig ( data [ ] byte ) ( * componentconfig . KubeSchedulerConfiguration , error ) {
configObj , gvk , err := o . codecs . UniversalDecoder ( ) . Decode ( data , nil , nil )
if err != nil {
return nil , err
}
config , ok := configObj . ( * componentconfig . KubeSchedulerConfiguration )
if ! ok {
return nil , fmt . Errorf ( "got unexpected config type: %v" , gvk )
}
return config , nil
}
func ( o * Options ) ApplyDefaults ( in * componentconfig . KubeSchedulerConfiguration ) ( * componentconfig . KubeSchedulerConfiguration , error ) {
external , err := o . scheme . ConvertToVersion ( in , componentconfigv1alpha1 . SchemeGroupVersion )
if err != nil {
return nil , err
}
o . scheme . Default ( external )
internal , err := o . scheme . ConvertToVersion ( external , componentconfig . SchemeGroupVersion )
if err != nil {
return nil , err
}
out := internal . ( * componentconfig . KubeSchedulerConfiguration )
return out , nil
}
func ( o * Options ) Run ( ) error {
config := o . config
if len ( o . ConfigFile ) > 0 {
if c , err := o . loadConfigFromFile ( o . ConfigFile ) ; err != nil {
return err
} else {
config = c
}
}
// Apply algorithms based on feature gates.
// TODO: make configurable?
algorithmprovider . ApplyFeatureGates ( )
server , err := NewSchedulerServer ( config , o . master )
if err != nil {
return err
}
stop := make ( chan struct { } )
return server . Run ( stop )
}
// NewSchedulerCommand creates a *cobra.Command object with default parameters
func NewSchedulerCommand ( ) * cobra . Command {
opts , err := NewOptions ( )
if err != nil {
glog . Fatalf ( "unable to initialize command options: %v" , err )
}
cmd := & cobra . Command {
Use : "kube-scheduler" ,
Long : ` The Kubernetes scheduler is a policy - rich , topology - aware ,
workload - specific function that significantly impacts availability , performance ,
and capacity . The scheduler needs to take into account individual and collective
resource requirements , quality of service requirements , hardware / software / policy
constraints , affinity and anti - affinity specifications , data locality , inter - workload
interference , deadlines , and so on . Workload - specific requirements will be exposed
through the API as necessary . ` ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
verflag . PrintAndExitIfRequested ( )
2018-03-06 22:33:18 +00:00
utilflag . PrintFlags ( cmd . Flags ( ) )
2018-01-09 18:57:14 +00:00
cmdutil . CheckErr ( opts . Complete ( ) )
cmdutil . CheckErr ( opts . Validate ( args ) )
cmdutil . CheckErr ( opts . Run ( ) )
} ,
}
opts . config , err = opts . ApplyDefaults ( opts . config )
if err != nil {
glog . Fatalf ( "unable to apply config defaults: %v" , err )
}
2018-03-06 22:33:18 +00:00
opts . AddFlags ( cmd . Flags ( ) )
2018-01-09 18:57:14 +00:00
cmd . MarkFlagFilename ( "config" , "yaml" , "yml" , "json" )
return cmd
}
// SchedulerServer represents all the parameters required to start the
// Kubernetes scheduler server.
type SchedulerServer struct {
SchedulerName string
Client clientset . Interface
InformerFactory informers . SharedInformerFactory
PodInformer coreinformers . PodInformer
AlgorithmSource componentconfig . SchedulerAlgorithmSource
HardPodAffinitySymmetricWeight int32
EventClient v1core . EventsGetter
Recorder record . EventRecorder
Broadcaster record . EventBroadcaster
// LeaderElection is optional.
LeaderElection * leaderelection . LeaderElectionConfig
// HealthzServer is optional.
HealthzServer * http . Server
// MetricsServer is optional.
MetricsServer * http . Server
}
// NewSchedulerServer creates a runnable SchedulerServer from configuration.
func NewSchedulerServer ( config * componentconfig . KubeSchedulerConfiguration , master string ) ( * SchedulerServer , error ) {
if config == nil {
return nil , errors . New ( "config is required" )
}
// Configz registration.
2018-03-06 22:33:18 +00:00
// only register if we're actually exposing it somewhere
if len ( config . MetricsBindAddress ) > 0 || len ( config . HealthzBindAddress ) > 0 {
if c , err := configz . New ( "componentconfig" ) ; err == nil {
c . Set ( config )
} else {
return nil , fmt . Errorf ( "unable to register configz: %s" , err )
}
2018-01-09 18:57:14 +00:00
}
// Prepare some Kube clients.
client , leaderElectionClient , eventClient , err := createClients ( config . ClientConnection , master )
if err != nil {
return nil , err
}
// Prepare event clients.
eventBroadcaster := record . NewBroadcaster ( )
recorder := eventBroadcaster . NewRecorder ( legacyscheme . Scheme , v1 . EventSource { Component : config . SchedulerName } )
// Set up leader election if enabled.
var leaderElectionConfig * leaderelection . LeaderElectionConfig
if config . LeaderElection . LeaderElect {
leaderElectionConfig , err = makeLeaderElectionConfig ( config . LeaderElection , leaderElectionClient , recorder )
if err != nil {
return nil , err
}
}
// Prepare a healthz server. If the metrics bind address is the same as the
// healthz bind address, consolidate the servers into one.
var healthzServer * http . Server
2018-03-06 22:33:18 +00:00
if len ( config . HealthzBindAddress ) > 0 {
2018-01-09 18:57:14 +00:00
healthzServer = makeHealthzServer ( config )
}
// Prepare a separate metrics server only if the bind address differs from the
// healthz bind address.
var metricsServer * http . Server
if len ( config . MetricsBindAddress ) > 0 && config . HealthzBindAddress != config . MetricsBindAddress {
metricsServer = makeMetricsServer ( config )
}
return & SchedulerServer {
SchedulerName : config . SchedulerName ,
Client : client ,
InformerFactory : informers . NewSharedInformerFactory ( client , 0 ) ,
PodInformer : factory . NewPodInformer ( client , 0 , config . SchedulerName ) ,
AlgorithmSource : config . AlgorithmSource ,
HardPodAffinitySymmetricWeight : config . HardPodAffinitySymmetricWeight ,
EventClient : eventClient ,
Recorder : recorder ,
Broadcaster : eventBroadcaster ,
LeaderElection : leaderElectionConfig ,
HealthzServer : healthzServer ,
MetricsServer : metricsServer ,
} , nil
}
// makeLeaderElectionConfig builds a leader election configuration. It will
// create a new resource lock associated with the configuration.
func makeLeaderElectionConfig ( config componentconfig . KubeSchedulerLeaderElectionConfiguration , client clientset . Interface , recorder record . EventRecorder ) ( * leaderelection . LeaderElectionConfig , error ) {
hostname , err := os . Hostname ( )
if err != nil {
return nil , fmt . Errorf ( "unable to get hostname: %v" , 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 := hostname + "_" + string ( uuid . NewUUID ( ) )
2018-01-09 18:57:14 +00:00
rl , err := resourcelock . New ( config . ResourceLock ,
config . LockObjectNamespace ,
config . LockObjectName ,
client . CoreV1 ( ) ,
resourcelock . ResourceLockConfig {
2018-03-06 22:33:18 +00:00
Identity : id ,
2018-01-09 18:57:14 +00:00
EventRecorder : recorder ,
} )
if err != nil {
return nil , fmt . Errorf ( "couldn't create resource lock: %v" , err )
}
return & leaderelection . LeaderElectionConfig {
Lock : rl ,
LeaseDuration : config . LeaseDuration . Duration ,
RenewDeadline : config . RenewDeadline . Duration ,
RetryPeriod : config . RetryPeriod . Duration ,
} , nil
}
// makeHealthzServer creates a healthz server from the config, and will also
// embed the metrics handler if the healthz and metrics address configurations
// are the same.
func makeHealthzServer ( config * componentconfig . KubeSchedulerConfiguration ) * http . Server {
2018-03-06 22:33:18 +00:00
mux := mux . NewPathRecorderMux ( "kube-scheduler" )
2018-01-09 18:57:14 +00:00
healthz . InstallHandler ( mux )
if config . HealthzBindAddress == config . MetricsBindAddress {
configz . InstallHandler ( mux )
mux . Handle ( "/metrics" , prometheus . Handler ( ) )
}
if config . EnableProfiling {
2018-03-06 22:33:18 +00:00
routes . Profiling { } . Install ( mux )
2018-01-09 18:57:14 +00:00
if config . EnableContentionProfiling {
goruntime . SetBlockProfileRate ( 1 )
}
}
return & http . Server {
Addr : config . HealthzBindAddress ,
Handler : mux ,
}
}
// makeMetricsServer builds a metrics server from the config.
func makeMetricsServer ( config * componentconfig . KubeSchedulerConfiguration ) * http . Server {
2018-03-06 22:33:18 +00:00
mux := mux . NewPathRecorderMux ( "kube-scheduler" )
2018-01-09 18:57:14 +00:00
configz . InstallHandler ( mux )
mux . Handle ( "/metrics" , prometheus . Handler ( ) )
if config . EnableProfiling {
2018-03-06 22:33:18 +00:00
routes . Profiling { } . Install ( mux )
2018-01-09 18:57:14 +00:00
if config . EnableContentionProfiling {
goruntime . SetBlockProfileRate ( 1 )
}
}
return & http . Server {
Addr : config . MetricsBindAddress ,
Handler : mux ,
}
}
// createClients creates a kube client and an event client from the given config and masterOverride.
// TODO remove masterOverride when CLI flags are removed.
func createClients ( config componentconfig . ClientConnectionConfiguration , masterOverride string ) ( clientset . Interface , clientset . Interface , v1core . EventsGetter , error ) {
if len ( config . KubeConfigFile ) == 0 && len ( masterOverride ) == 0 {
glog . Warningf ( "Neither --kubeconfig nor --master was specified. Using default API client. This might not work." )
}
// This creates a client, first loading any specified kubeconfig
// file, and then overriding the Master flag, if non-empty.
kubeConfig , err := clientcmd . NewNonInteractiveDeferredLoadingClientConfig (
& clientcmd . ClientConfigLoadingRules { ExplicitPath : config . KubeConfigFile } ,
& clientcmd . ConfigOverrides { ClusterInfo : clientcmdapi . Cluster { Server : masterOverride } } ) . ClientConfig ( )
if err != nil {
return nil , nil , nil , err
}
kubeConfig . AcceptContentTypes = config . AcceptContentTypes
kubeConfig . ContentType = config . ContentType
kubeConfig . QPS = config . QPS
//TODO make config struct use int instead of int32?
kubeConfig . Burst = int ( config . Burst )
client , err := clientset . NewForConfig ( restclient . AddUserAgent ( kubeConfig , "scheduler" ) )
if err != nil {
return nil , nil , nil , err
}
leaderElectionClient , err := clientset . NewForConfig ( restclient . AddUserAgent ( kubeConfig , "leader-election" ) )
if err != nil {
return nil , nil , nil , err
}
eventClient , err := clientset . NewForConfig ( kubeConfig )
if err != nil {
return nil , nil , nil , err
}
return client , leaderElectionClient , eventClient . CoreV1 ( ) , nil
}
// Run runs the SchedulerServer. This should never exit.
func ( s * SchedulerServer ) Run ( stop chan struct { } ) error {
// To help debugging, immediately log version
glog . Infof ( "Version: %+v" , version . Get ( ) )
// Build a scheduler config from the provided algorithm source.
schedulerConfig , err := s . SchedulerConfig ( )
if err != nil {
return err
}
// Create the scheduler.
sched := scheduler . NewFromConfig ( schedulerConfig )
// Prepare the event broadcaster.
if ! reflect . ValueOf ( s . Broadcaster ) . IsNil ( ) && ! reflect . ValueOf ( s . EventClient ) . IsNil ( ) {
s . Broadcaster . StartRecordingToSink ( & v1core . EventSinkImpl { Interface : s . EventClient . Events ( "" ) } )
}
// Start up the healthz server.
if s . HealthzServer != nil {
go wait . Until ( func ( ) {
glog . Infof ( "starting healthz server on %v" , s . HealthzServer . Addr )
err := s . HealthzServer . ListenAndServe ( )
if err != nil {
utilruntime . HandleError ( fmt . Errorf ( "failed to start healthz server: %v" , err ) )
}
} , 5 * time . Second , stop )
}
// Start up the metrics server.
if s . MetricsServer != nil {
go wait . Until ( func ( ) {
glog . Infof ( "starting metrics server on %v" , s . MetricsServer . Addr )
err := s . MetricsServer . ListenAndServe ( )
if err != nil {
utilruntime . HandleError ( fmt . Errorf ( "failed to start metrics server: %v" , err ) )
}
} , 5 * time . Second , stop )
}
// Start all informers.
go s . PodInformer . Informer ( ) . Run ( stop )
s . InformerFactory . Start ( stop )
// Wait for all caches to sync before scheduling.
s . InformerFactory . WaitForCacheSync ( stop )
controller . WaitForCacheSync ( "scheduler" , stop , s . PodInformer . Informer ( ) . HasSynced )
// Prepare a reusable run function.
run := func ( stopCh <- chan struct { } ) {
sched . Run ( )
<- stopCh
}
// If leader election is enabled, run via LeaderElector until done and exit.
if s . LeaderElection != nil {
s . LeaderElection . Callbacks = leaderelection . LeaderCallbacks {
OnStartedLeading : run ,
OnStoppedLeading : func ( ) {
utilruntime . HandleError ( fmt . Errorf ( "lost master" ) )
} ,
}
leaderElector , err := leaderelection . NewLeaderElector ( * s . LeaderElection )
if err != nil {
return fmt . Errorf ( "couldn't create leader elector: %v" , err )
}
leaderElector . Run ( )
return fmt . Errorf ( "lost lease" )
}
// Leader election is disabled, so run inline until done.
run ( stop )
return fmt . Errorf ( "finished without leader elect" )
}
// SchedulerConfig creates the scheduler configuration. This is exposed for use
// by tests.
func ( s * SchedulerServer ) SchedulerConfig ( ) ( * scheduler . Config , error ) {
var storageClassInformer storageinformers . StorageClassInformer
if utilfeature . DefaultFeatureGate . Enabled ( features . VolumeScheduling ) {
storageClassInformer = s . InformerFactory . Storage ( ) . V1 ( ) . StorageClasses ( )
}
// Set up the configurator which can create schedulers from configs.
configurator := factory . NewConfigFactory (
s . SchedulerName ,
s . Client ,
s . InformerFactory . Core ( ) . V1 ( ) . Nodes ( ) ,
s . PodInformer ,
s . InformerFactory . Core ( ) . V1 ( ) . PersistentVolumes ( ) ,
s . InformerFactory . Core ( ) . V1 ( ) . PersistentVolumeClaims ( ) ,
s . InformerFactory . Core ( ) . V1 ( ) . ReplicationControllers ( ) ,
s . InformerFactory . Extensions ( ) . V1beta1 ( ) . ReplicaSets ( ) ,
s . InformerFactory . Apps ( ) . V1beta1 ( ) . StatefulSets ( ) ,
s . InformerFactory . Core ( ) . V1 ( ) . Services ( ) ,
s . InformerFactory . Policy ( ) . V1beta1 ( ) . PodDisruptionBudgets ( ) ,
storageClassInformer ,
s . HardPodAffinitySymmetricWeight ,
utilfeature . DefaultFeatureGate . Enabled ( features . EnableEquivalenceClassCache ) ,
)
source := s . AlgorithmSource
var config * scheduler . Config
switch {
case source . Provider != nil :
// Create the config from a named algorithm provider.
sc , err := configurator . CreateFromProvider ( * source . Provider )
if err != nil {
return nil , fmt . Errorf ( "couldn't create scheduler using provider %q: %v" , * source . Provider , err )
}
config = sc
case source . Policy != nil :
// Create the config from a user specified policy source.
policy := & schedulerapi . Policy { }
switch {
case source . Policy . File != nil :
// Use a policy serialized in a file.
policyFile := source . Policy . File . Path
_ , err := os . Stat ( policyFile )
if err != nil {
return nil , fmt . Errorf ( "missing policy config file %s" , policyFile )
}
data , err := ioutil . ReadFile ( policyFile )
if err != nil {
return nil , fmt . Errorf ( "couldn't read policy config: %v" , err )
}
err = runtime . DecodeInto ( latestschedulerapi . Codec , [ ] byte ( data ) , policy )
if err != nil {
return nil , fmt . Errorf ( "invalid policy: %v" , err )
}
case source . Policy . ConfigMap != nil :
// Use a policy serialized in a config map value.
policyRef := source . Policy . ConfigMap
policyConfigMap , err := s . Client . CoreV1 ( ) . ConfigMaps ( policyRef . Namespace ) . Get ( policyRef . Name , metav1 . GetOptions { } )
if err != nil {
return nil , fmt . Errorf ( "couldn't get policy config map %s/%s: %v" , policyRef . Namespace , policyRef . Name , err )
}
data , found := policyConfigMap . Data [ componentconfig . SchedulerPolicyConfigMapKey ]
if ! found {
return nil , fmt . Errorf ( "missing policy config map value at key %q" , componentconfig . SchedulerPolicyConfigMapKey )
}
err = runtime . DecodeInto ( latestschedulerapi . Codec , [ ] byte ( data ) , policy )
if err != nil {
return nil , fmt . Errorf ( "invalid policy: %v" , err )
}
}
sc , err := configurator . CreateFromConfig ( * policy )
if err != nil {
return nil , fmt . Errorf ( "couldn't create scheduler from policy: %v" , err )
}
config = sc
default :
return nil , fmt . Errorf ( "unsupported algorithm source: %v" , source )
}
// Additional tweaks to the config produced by the configurator.
config . Recorder = s . Recorder
return config , nil
}