mirror of https://github.com/k3s-io/k3s
373 lines
14 KiB
Go
373 lines
14 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"
|
|
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/events"
|
|
"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"
|
|
"k8s.io/component-base/config/options"
|
|
configv1alpha1 "k8s.io/component-base/config/v1alpha1"
|
|
"k8s.io/component-base/logs"
|
|
"k8s.io/component-base/metrics"
|
|
"k8s.io/klog/v2"
|
|
kubeschedulerconfigv1beta1 "k8s.io/kube-scheduler/config/v1beta1"
|
|
schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config"
|
|
"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"
|
|
)
|
|
|
|
// 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
|
|
Metrics *metrics.Options
|
|
Logs *logs.Options
|
|
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,
|
|
SchedulerName: corev1.DefaultSchedulerName,
|
|
HardPodAffinitySymmetricWeight: 1,
|
|
},
|
|
Metrics: metrics.NewOptions(),
|
|
Logs: logs.NewOptions(),
|
|
}
|
|
|
|
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 = kubeschedulerconfig.DefaultKubeSchedulerPort
|
|
|
|
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 := kubeschedulerconfigv1beta1.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. The following flags can overwrite fields in this file:
|
|
--address
|
|
--port
|
|
--use-legacy-policy-config
|
|
--policy-configmap
|
|
--policy-config-file
|
|
--algorithm-provider`)
|
|
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)
|
|
|
|
options.BindLeaderElectionFlags(&o.ComponentConfig.LeaderElection, nfs.FlagSet("leader election"))
|
|
utilfeature.DefaultMutableFeatureGate.AddFlag(nfs.FlagSet("feature gate"))
|
|
o.Metrics.AddFlags(nfs.FlagSet("metrics"))
|
|
o.Logs.AddFlags(nfs.FlagSet("logs"))
|
|
|
|
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
|
|
|
|
// apply deprecated flags if no config file is loaded (this is the old behaviour).
|
|
o.Deprecated.ApplyTo(&c.ComponentConfig)
|
|
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
|
|
}
|
|
|
|
c.ComponentConfig = *cfg
|
|
|
|
// apply any deprecated Policy flags, if applicable
|
|
o.Deprecated.ApplyAlgorithmSourceTo(&c.ComponentConfig)
|
|
|
|
// if the user has set CC profiles and is trying to use a Policy config, error out
|
|
// these configs are no longer merged and they should not be used simultaneously
|
|
if !emptySchedulerProfileConfig(c.ComponentConfig.Profiles) && c.ComponentConfig.AlgorithmSource.Policy != nil {
|
|
return fmt.Errorf("cannot set a Plugin config and Policy config")
|
|
}
|
|
|
|
// use the loaded config file only, with the exception of --address and --port.
|
|
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
|
|
}
|
|
}
|
|
o.Metrics.Apply()
|
|
o.Logs.Apply()
|
|
return nil
|
|
}
|
|
|
|
// emptySchedulerProfileConfig returns true if the list of profiles passed to it contains only
|
|
// the "default-scheduler" profile with no plugins or pluginconfigs registered
|
|
// (this is the default empty profile initialized by defaults.go)
|
|
func emptySchedulerProfileConfig(profiles []kubeschedulerconfig.KubeSchedulerProfile) bool {
|
|
return len(profiles) == 1 &&
|
|
len(profiles[0].PluginConfig) == 0 &&
|
|
profiles[0].Plugins == 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, o.Metrics.Validate()...)
|
|
errs = append(errs, o.Logs.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
|
|
}
|
|
|
|
c.EventBroadcaster = events.NewEventBroadcasterAdapter(eventClient)
|
|
|
|
// 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 := c.EventBroadcaster.DeprecatedNewLegacyRecorder(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.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 componentbaseconfig.LeaderElectionConfiguration, 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
|
|
}
|