| /* |
| Copyright 2018 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 options |
| |
| import ( |
| "fmt" |
| "net" |
| "os" |
| "strconv" |
| "time" |
| |
| corev1 "k8s.io/api/core/v1" |
| apimachineryconfig "k8s.io/apimachinery/pkg/apis/config" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/apimachinery/pkg/util/uuid" |
| apiserveroptions "k8s.io/apiserver/pkg/server/options" |
| utilfeature "k8s.io/apiserver/pkg/util/feature" |
| apiserverflag "k8s.io/apiserver/pkg/util/flag" |
| "k8s.io/client-go/informers" |
| 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/klog" |
| kubeschedulerconfigv1alpha1 "k8s.io/kube-scheduler/config/v1alpha1" |
| schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" |
| "k8s.io/kubernetes/pkg/api/legacyscheme" |
| "k8s.io/kubernetes/pkg/client/leaderelectionconfig" |
| "k8s.io/kubernetes/pkg/master/ports" |
| kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" |
| kubeschedulerscheme "k8s.io/kubernetes/pkg/scheduler/apis/config/scheme" |
| "k8s.io/kubernetes/pkg/scheduler/apis/config/validation" |
| "k8s.io/kubernetes/pkg/scheduler/factory" |
| ) |
| |
| // Options has all the params needed to run a Scheduler |
| type Options struct { |
| // The default values. These are overridden if ConfigFile is set or by values in InsecureServing. |
| ComponentConfig kubeschedulerconfig.KubeSchedulerConfiguration |
| |
| SecureServing *apiserveroptions.SecureServingOptionsWithLoopback |
| CombinedInsecureServing *CombinedInsecureServingOptions |
| Authentication *apiserveroptions.DelegatingAuthenticationOptions |
| Authorization *apiserveroptions.DelegatingAuthorizationOptions |
| Deprecated *DeprecatedOptions |
| |
| // ConfigFile is the location of the scheduler server's configuration file. |
| ConfigFile string |
| |
| // WriteConfigTo is the path where the default configuration will be written. |
| WriteConfigTo string |
| |
| Master string |
| } |
| |
| // NewOptions returns default scheduler app options. |
| func NewOptions() (*Options, error) { |
| cfg, err := newDefaultComponentConfig() |
| if err != nil { |
| return nil, err |
| } |
| |
| hhost, hport, err := splitHostIntPort(cfg.HealthzBindAddress) |
| if err != nil { |
| return nil, err |
| } |
| |
| o := &Options{ |
| ComponentConfig: *cfg, |
| SecureServing: apiserveroptions.NewSecureServingOptions().WithLoopback(), |
| CombinedInsecureServing: &CombinedInsecureServingOptions{ |
| Healthz: (&apiserveroptions.DeprecatedInsecureServingOptions{ |
| BindNetwork: "tcp", |
| }).WithLoopback(), |
| Metrics: (&apiserveroptions.DeprecatedInsecureServingOptions{ |
| BindNetwork: "tcp", |
| }).WithLoopback(), |
| BindPort: hport, |
| BindAddress: hhost, |
| }, |
| Authentication: apiserveroptions.NewDelegatingAuthenticationOptions(), |
| Authorization: apiserveroptions.NewDelegatingAuthorizationOptions(), |
| Deprecated: &DeprecatedOptions{ |
| UseLegacyPolicyConfig: false, |
| PolicyConfigMapNamespace: metav1.NamespaceSystem, |
| }, |
| } |
| |
| o.Authentication.RemoteKubeConfigFileOptional = true |
| o.Authorization.RemoteKubeConfigFileOptional = true |
| o.Authorization.AlwaysAllowPaths = []string{"/healthz"} |
| |
| // Set the PairName but leave certificate directory blank to generate in-memory by default |
| o.SecureServing.ServerCert.CertDirectory = "" |
| o.SecureServing.ServerCert.PairName = "kube-scheduler" |
| o.SecureServing.BindPort = ports.KubeSchedulerPort |
| |
| return o, nil |
| } |
| |
| func splitHostIntPort(s string) (string, int, error) { |
| host, port, err := net.SplitHostPort(s) |
| if err != nil { |
| return "", 0, err |
| } |
| portInt, err := strconv.Atoi(port) |
| if err != nil { |
| return "", 0, err |
| } |
| return host, portInt, err |
| } |
| |
| func newDefaultComponentConfig() (*kubeschedulerconfig.KubeSchedulerConfiguration, error) { |
| cfgv1alpha1 := kubeschedulerconfigv1alpha1.KubeSchedulerConfiguration{} |
| kubeschedulerscheme.Scheme.Default(&cfgv1alpha1) |
| cfg := kubeschedulerconfig.KubeSchedulerConfiguration{} |
| if err := kubeschedulerscheme.Scheme.Convert(&cfgv1alpha1, &cfg, nil); err != nil { |
| return nil, err |
| } |
| return &cfg, nil |
| } |
| |
| // Flags returns flags for a specific scheduler by section name |
| func (o *Options) Flags() (nfs apiserverflag.NamedFlagSets) { |
| fs := nfs.FlagSet("misc") |
| fs.StringVar(&o.ConfigFile, "config", o.ConfigFile, "The path to the configuration file. Flags override values in this file.") |
| fs.StringVar(&o.WriteConfigTo, "write-config-to", o.WriteConfigTo, "If set, write the configuration values to this file and exit.") |
| fs.StringVar(&o.Master, "master", o.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig)") |
| |
| o.SecureServing.AddFlags(nfs.FlagSet("secure serving")) |
| o.CombinedInsecureServing.AddFlags(nfs.FlagSet("insecure serving")) |
| o.Authentication.AddFlags(nfs.FlagSet("authentication")) |
| o.Authorization.AddFlags(nfs.FlagSet("authorization")) |
| o.Deprecated.AddFlags(nfs.FlagSet("deprecated"), &o.ComponentConfig) |
| |
| leaderelectionconfig.BindFlags(&o.ComponentConfig.LeaderElection.LeaderElectionConfiguration, nfs.FlagSet("leader election")) |
| utilfeature.DefaultFeatureGate.AddFlag(nfs.FlagSet("feature gate")) |
| |
| return nfs |
| } |
| |
| // ApplyTo applies the scheduler options to the given scheduler app configuration. |
| func (o *Options) ApplyTo(c *schedulerappconfig.Config) error { |
| if len(o.ConfigFile) == 0 { |
| c.ComponentConfig = o.ComponentConfig |
| |
| // only apply deprecated flags if no config file is loaded (this is the old behaviour). |
| if err := o.Deprecated.ApplyTo(&c.ComponentConfig); err != nil { |
| return err |
| } |
| if err := o.CombinedInsecureServing.ApplyTo(c, &c.ComponentConfig); err != nil { |
| return err |
| } |
| } else { |
| cfg, err := loadConfigFromFile(o.ConfigFile) |
| if err != nil { |
| return err |
| } |
| |
| // use the loaded config file only, with the exception of --address and --port. This means that |
| // none of the deprectated flags in o.Deprecated are taken into consideration. This is the old |
| // behaviour of the flags we have to keep. |
| c.ComponentConfig = *cfg |
| |
| if err := o.CombinedInsecureServing.ApplyToFromLoadedConfig(c, &c.ComponentConfig); err != nil { |
| return err |
| } |
| } |
| |
| if err := o.SecureServing.ApplyTo(&c.SecureServing, &c.LoopbackClientConfig); err != nil { |
| return err |
| } |
| if o.SecureServing != nil && (o.SecureServing.BindPort != 0 || o.SecureServing.Listener != nil) { |
| if err := o.Authentication.ApplyTo(&c.Authentication, c.SecureServing, nil); err != nil { |
| return err |
| } |
| if err := o.Authorization.ApplyTo(&c.Authorization); err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| // Validate validates all the required options. |
| func (o *Options) Validate() []error { |
| var errs []error |
| |
| if err := validation.ValidateKubeSchedulerConfiguration(&o.ComponentConfig).ToAggregate(); err != nil { |
| errs = append(errs, err.Errors()...) |
| } |
| errs = append(errs, o.SecureServing.Validate()...) |
| errs = append(errs, o.CombinedInsecureServing.Validate()...) |
| errs = append(errs, o.Authentication.Validate()...) |
| errs = append(errs, o.Authorization.Validate()...) |
| errs = append(errs, o.Deprecated.Validate()...) |
| |
| return errs |
| } |
| |
| // Config return a scheduler config object |
| func (o *Options) Config() (*schedulerappconfig.Config, error) { |
| if o.SecureServing != nil { |
| if err := o.SecureServing.MaybeDefaultWithSelfSignedCerts("localhost", nil, []net.IP{net.ParseIP("127.0.0.1")}); err != nil { |
| return nil, fmt.Errorf("error creating self-signed certificates: %v", err) |
| } |
| } |
| |
| c := &schedulerappconfig.Config{} |
| if err := o.ApplyTo(c); err != nil { |
| return nil, err |
| } |
| |
| // Prepare kube clients. |
| client, leaderElectionClient, eventClient, err := createClients(c.ComponentConfig.ClientConnection, o.Master, c.ComponentConfig.LeaderElection.RenewDeadline.Duration) |
| if err != nil { |
| return nil, err |
| } |
| |
| // Prepare event clients. |
| eventBroadcaster := record.NewBroadcaster() |
| recorder := eventBroadcaster.NewRecorder(legacyscheme.Scheme, corev1.EventSource{Component: c.ComponentConfig.SchedulerName}) |
| |
| // Set up leader election if enabled. |
| var leaderElectionConfig *leaderelection.LeaderElectionConfig |
| if c.ComponentConfig.LeaderElection.LeaderElect { |
| leaderElectionConfig, err = makeLeaderElectionConfig(c.ComponentConfig.LeaderElection, leaderElectionClient, recorder) |
| if err != nil { |
| return nil, err |
| } |
| } |
| |
| c.Client = client |
| c.InformerFactory = informers.NewSharedInformerFactory(client, 0) |
| c.PodInformer = factory.NewPodInformer(client, 0) |
| c.EventClient = eventClient |
| c.Recorder = recorder |
| c.Broadcaster = eventBroadcaster |
| c.LeaderElection = leaderElectionConfig |
| |
| return c, nil |
| } |
| |
| // makeLeaderElectionConfig builds a leader election configuration. It will |
| // create a new resource lock associated with the configuration. |
| func makeLeaderElectionConfig(config kubeschedulerconfig.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) |
| } |
| // add a uniquifier so that two processes on the same host don't accidentally both become active |
| id := hostname + "_" + string(uuid.NewUUID()) |
| |
| rl, err := resourcelock.New(config.ResourceLock, |
| config.LockObjectNamespace, |
| config.LockObjectName, |
| client.CoreV1(), |
| resourcelock.ResourceLockConfig{ |
| Identity: id, |
| 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, |
| WatchDog: leaderelection.NewLeaderHealthzAdaptor(time.Second * 20), |
| Name: "kube-scheduler", |
| }, nil |
| } |
| |
| // 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 apimachineryconfig.ClientConnectionConfiguration, masterOverride string, timeout time.Duration) (clientset.Interface, clientset.Interface, v1core.EventsGetter, error) { |
| if len(config.Kubeconfig) == 0 && len(masterOverride) == 0 { |
| klog.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.Kubeconfig}, |
| &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 |
| } |
| |
| // shallow copy, do not modify the kubeConfig.Timeout. |
| restConfig := *kubeConfig |
| restConfig.Timeout = timeout |
| leaderElectionClient, err := clientset.NewForConfig(restclient.AddUserAgent(&restConfig, "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 |
| } |