mirror of https://github.com/k3s-io/k3s
363 lines
13 KiB
Go
363 lines
13 KiB
Go
/*
|
|
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"
|
|
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"
|
|
"k8s.io/client-go/informers"
|
|
clientset "k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/kubernetes/scheme"
|
|
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"
|
|
cliflag "k8s.io/component-base/cli/flag"
|
|
componentbaseconfig "k8s.io/component-base/config"
|
|
configv1alpha1 "k8s.io/component-base/config/v1alpha1"
|
|
"k8s.io/component-base/metrics"
|
|
"k8s.io/klog"
|
|
kubeschedulerconfigv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
|
schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config"
|
|
"k8s.io/kubernetes/pkg/client/leaderelectionconfig"
|
|
"k8s.io/kubernetes/pkg/master/ports"
|
|
"k8s.io/kubernetes/pkg/scheduler"
|
|
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/framework/plugins/interpodaffinity"
|
|
)
|
|
|
|
// 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
|
|
|
|
ShowHiddenMetricsForVersion 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,
|
|
SchedulerName: corev1.DefaultSchedulerName,
|
|
HardPodAffinitySymmetricWeight: interpodaffinity.DefaultHardPodAffinityWeight,
|
|
},
|
|
}
|
|
|
|
o.Authentication.TolerateInClusterLookupFailure = true
|
|
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) {
|
|
versionedCfg := kubeschedulerconfigv1alpha2.KubeSchedulerConfiguration{}
|
|
versionedCfg.DebuggingConfiguration = *configv1alpha1.NewRecommendedDebuggingConfiguration()
|
|
|
|
kubeschedulerscheme.Scheme.Default(&versionedCfg)
|
|
cfg := kubeschedulerconfig.KubeSchedulerConfiguration{}
|
|
if err := kubeschedulerscheme.Scheme.Convert(&versionedCfg, &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 cliflag.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.DefaultMutableFeatureGate.AddFlag(nfs.FlagSet("feature gate"))
|
|
|
|
// TODO(RainbowMango): move it to genericoptions before next flag comes.
|
|
mfs := nfs.FlagSet("metrics")
|
|
mfs.StringVar(&o.ShowHiddenMetricsForVersion, "show-hidden-metrics-for-version", o.ShowHiddenMetricsForVersion,
|
|
"The previous version for which you want to show hidden metrics. "+
|
|
"Only the previous minor version is meaningful, other values will not be allowed. "+
|
|
"Accepted format of version is <major>.<minor>, e.g.: '1.16'. "+
|
|
"The purpose of this format is make sure you have the opportunity to notice if the next release hides additional metrics, "+
|
|
"rather than being surprised when they are permanently removed in the release after that.")
|
|
|
|
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
|
|
}
|
|
if err := validation.ValidateKubeSchedulerConfiguration(cfg).ToAggregate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// use the loaded config file only, with the exception of --address and --port. This means that
|
|
// none of the deprecated 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
|
|
}
|
|
}
|
|
if len(o.ShowHiddenMetricsForVersion) > 0 {
|
|
metrics.SetShowHidden()
|
|
}
|
|
|
|
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()...)
|
|
errs = append(errs, metrics.ValidateShowHiddenMetricsVersion(o.ShowHiddenMetricsForVersion)...)
|
|
|
|
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
|
|
}
|
|
|
|
coreBroadcaster := record.NewBroadcaster()
|
|
|
|
// Set up leader election if enabled.
|
|
var leaderElectionConfig *leaderelection.LeaderElectionConfig
|
|
if c.ComponentConfig.LeaderElection.LeaderElect {
|
|
// Use the scheduler name in the first profile to record leader election.
|
|
coreRecorder := coreBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: c.ComponentConfig.Profiles[0].SchedulerName})
|
|
leaderElectionConfig, err = makeLeaderElectionConfig(c.ComponentConfig.LeaderElection, leaderElectionClient, coreRecorder)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
c.Client = client
|
|
c.InformerFactory = informers.NewSharedInformerFactory(client, 0)
|
|
c.PodInformer = scheduler.NewPodInformer(client, 0)
|
|
c.EventClient = eventClient.EventsV1beta1()
|
|
c.CoreEventClient = eventClient.CoreV1()
|
|
c.CoreBroadcaster = coreBroadcaster
|
|
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.ResourceNamespace,
|
|
config.ResourceName,
|
|
client.CoreV1(),
|
|
client.CoordinationV1(),
|
|
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 componentbaseconfig.ClientConnectionConfiguration, masterOverride string, timeout time.Duration) (clientset.Interface, clientset.Interface, clientset.Interface, 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.DisableCompression = true
|
|
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, nil
|
|
}
|