sched: ensure feature gate is honored when instantiating scheduler (#105915)

* sched: ensure feature gate is honored when instantiating scheduler

* fixup: address comments
This commit is contained in:
Wei Huang 2021-11-02 14:28:06 -07:00 committed by GitHub
parent 5aacb15a19
commit e30f9648cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 187 additions and 114 deletions

View File

@ -22,14 +22,17 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" componentbaseconfig "k8s.io/component-base/config"
) )
// DeprecatedOptions contains deprecated options and their flags. // DeprecatedOptions contains deprecated options and their flags.
// TODO remove these fields once the deprecated flags are removed. // TODO remove these fields once the deprecated flags are removed.
type DeprecatedOptions struct { type DeprecatedOptions struct {
// The fields below here are placeholders for flags that can't be directly componentbaseconfig.DebuggingConfiguration
// mapped into componentconfig.KubeSchedulerConfiguration. componentbaseconfig.ClientConnectionConfiguration
// Note that only the deprecated options (lock-object-name and lock-object-namespace) are populated here.
componentbaseconfig.LeaderElectionConfiguration
Port int Port int
} }
@ -47,21 +50,21 @@ func addDummyInsecureFlags(o *DeprecatedOptions, fs *pflag.FlagSet) {
} }
// AddFlags adds flags for the deprecated options. // AddFlags adds flags for the deprecated options.
func (o *DeprecatedOptions) AddFlags(fs *pflag.FlagSet, cfg *kubeschedulerconfig.KubeSchedulerConfiguration) { func (o *DeprecatedOptions) AddFlags(fs *pflag.FlagSet) {
if o == nil { if o == nil {
return return
} }
addDummyInsecureFlags(o, fs) addDummyInsecureFlags(o, fs)
fs.BoolVar(&cfg.EnableProfiling, "profiling", cfg.EnableProfiling, "DEPRECATED: enable profiling via web interface host:port/debug/pprof/. This parameter is ignored if a config file is specified in --config.") fs.BoolVar(&o.EnableProfiling, "profiling", true, "DEPRECATED: enable profiling via web interface host:port/debug/pprof/. This parameter is ignored if a config file is specified in --config.")
fs.BoolVar(&cfg.EnableContentionProfiling, "contention-profiling", cfg.EnableContentionProfiling, "DEPRECATED: enable lock contention profiling, if profiling is enabled. This parameter is ignored if a config file is specified in --config.") fs.BoolVar(&o.EnableContentionProfiling, "contention-profiling", true, "DEPRECATED: enable lock contention profiling, if profiling is enabled. This parameter is ignored if a config file is specified in --config.")
fs.StringVar(&cfg.ClientConnection.Kubeconfig, "kubeconfig", cfg.ClientConnection.Kubeconfig, "DEPRECATED: path to kubeconfig file with authorization and master location information. This parameter is ignored if a config file is specified in --config.") fs.StringVar(&o.Kubeconfig, "kubeconfig", "", "DEPRECATED: path to kubeconfig file with authorization and master location information. This parameter is ignored if a config file is specified in --config.")
fs.StringVar(&cfg.ClientConnection.ContentType, "kube-api-content-type", cfg.ClientConnection.ContentType, "DEPRECATED: content type of requests sent to apiserver. This parameter is ignored if a config file is specified in --config.") fs.StringVar(&o.ContentType, "kube-api-content-type", "application/vnd.kubernetes.protobuf", "DEPRECATED: content type of requests sent to apiserver. This parameter is ignored if a config file is specified in --config.")
fs.Float32Var(&cfg.ClientConnection.QPS, "kube-api-qps", cfg.ClientConnection.QPS, "DEPRECATED: QPS to use while talking with kubernetes apiserver. This parameter is ignored if a config file is specified in --config.") fs.Float32Var(&o.QPS, "kube-api-qps", 50.0, "DEPRECATED: QPS to use while talking with kubernetes apiserver. This parameter is ignored if a config file is specified in --config.")
fs.Int32Var(&cfg.ClientConnection.Burst, "kube-api-burst", cfg.ClientConnection.Burst, "DEPRECATED: burst to use while talking with kubernetes apiserver. This parameter is ignored if a config file is specified in --config.") fs.Int32Var(&o.Burst, "kube-api-burst", 100, "DEPRECATED: burst to use while talking with kubernetes apiserver. This parameter is ignored if a config file is specified in --config.")
fs.StringVar(&cfg.LeaderElection.ResourceNamespace, "lock-object-namespace", cfg.LeaderElection.ResourceNamespace, "DEPRECATED: define the namespace of the lock object. Will be removed in favor of leader-elect-resource-namespace. This parameter is ignored if a config file is specified in --config.") fs.StringVar(&o.ResourceNamespace, "lock-object-namespace", "kube-system", "DEPRECATED: define the namespace of the lock object. Will be removed in favor of leader-elect-resource-namespace. This parameter is ignored if a config file is specified in --config.")
fs.StringVar(&cfg.LeaderElection.ResourceName, "lock-object-name", cfg.LeaderElection.ResourceName, "DEPRECATED: define the name of the lock object. Will be removed in favor of leader-elect-resource-name. This parameter is ignored if a config file is specified in --config.") fs.StringVar(&o.ResourceName, "lock-object-name", "kube-scheduler", "DEPRECATED: define the name of the lock object. Will be removed in favor of leader-elect-resource-name. This parameter is ignored if a config file is specified in --config.")
} }
// Validate validates the deprecated scheduler options. // Validate validates the deprecated scheduler options.

View File

@ -23,6 +23,7 @@ import (
"time" "time"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/uuid"
apiserveroptions "k8s.io/apiserver/pkg/server/options" apiserveroptions "k8s.io/apiserver/pkg/server/options"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
@ -43,7 +44,6 @@ import (
schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config"
"k8s.io/kubernetes/pkg/scheduler" "k8s.io/kubernetes/pkg/scheduler"
kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
"k8s.io/kubernetes/pkg/scheduler/apis/config/latest"
"k8s.io/kubernetes/pkg/scheduler/apis/config/validation" "k8s.io/kubernetes/pkg/scheduler/apis/config/validation"
netutils "k8s.io/utils/net" netutils "k8s.io/utils/net"
) )
@ -51,7 +51,7 @@ import (
// Options has all the params needed to run a Scheduler // Options has all the params needed to run a Scheduler
type Options struct { type Options struct {
// The default values. // The default values.
ComponentConfig kubeschedulerconfig.KubeSchedulerConfiguration ComponentConfig *kubeschedulerconfig.KubeSchedulerConfiguration
SecureServing *apiserveroptions.SecureServingOptionsWithLoopback SecureServing *apiserveroptions.SecureServingOptionsWithLoopback
Authentication *apiserveroptions.DelegatingAuthenticationOptions Authentication *apiserveroptions.DelegatingAuthenticationOptions
@ -59,6 +59,7 @@ type Options struct {
Metrics *metrics.Options Metrics *metrics.Options
Logs *logs.Options Logs *logs.Options
Deprecated *DeprecatedOptions Deprecated *DeprecatedOptions
LeaderElection *componentbaseconfig.LeaderElectionConfiguration
// ConfigFile is the location of the scheduler server's configuration file. // ConfigFile is the location of the scheduler server's configuration file.
ConfigFile string ConfigFile string
@ -67,17 +68,29 @@ type Options struct {
WriteConfigTo string WriteConfigTo string
Master string Master string
// Flags hold the parsed CLI flags.
Flags *cliflag.NamedFlagSets
} }
// NewOptions returns default scheduler app options. // NewOptions returns default scheduler app options.
func NewOptions() (*Options, error) { func NewOptions() *Options {
o := &Options{ o := &Options{
SecureServing: apiserveroptions.NewSecureServingOptions().WithLoopback(), SecureServing: apiserveroptions.NewSecureServingOptions().WithLoopback(),
Authentication: apiserveroptions.NewDelegatingAuthenticationOptions(), Authentication: apiserveroptions.NewDelegatingAuthenticationOptions(),
Authorization: apiserveroptions.NewDelegatingAuthorizationOptions(), Authorization: apiserveroptions.NewDelegatingAuthorizationOptions(),
Deprecated: &DeprecatedOptions{}, Deprecated: &DeprecatedOptions{},
Metrics: metrics.NewOptions(), LeaderElection: &componentbaseconfig.LeaderElectionConfiguration{
Logs: logs.NewOptions(), LeaderElect: true,
LeaseDuration: metav1.Duration{Duration: 15 * time.Second},
RenewDeadline: metav1.Duration{Duration: 10 * time.Second},
RetryPeriod: metav1.Duration{Duration: 2 * time.Second},
ResourceLock: "leases",
ResourceName: "kube-scheduler",
ResourceNamespace: "kube-system",
},
Metrics: metrics.NewOptions(),
Logs: logs.NewOptions(),
} }
o.Authentication.TolerateInClusterLookupFailure = true o.Authentication.TolerateInClusterLookupFailure = true
@ -89,73 +102,84 @@ func NewOptions() (*Options, error) {
o.SecureServing.ServerCert.PairName = "kube-scheduler" o.SecureServing.ServerCert.PairName = "kube-scheduler"
o.SecureServing.BindPort = kubeschedulerconfig.DefaultKubeSchedulerPort o.SecureServing.BindPort = kubeschedulerconfig.DefaultKubeSchedulerPort
return o, nil o.initFlags()
return o
} }
// Complete completes the remaining instantiation of the options obj. // ApplyDeprecated obtains the deprecated CLI args and set them to `o.ComponentConfig` if specified.
// In particular, it injects the latest internal versioned ComponentConfig. func (o *Options) ApplyDeprecated() {
func (o *Options) Complete(nfs *cliflag.NamedFlagSets) error { if o.Flags == nil {
cfg, err := latest.Default() return
if err != nil {
return err
} }
// Obtain deprecated CLI args. Set them to cfg if specified in command line. // Obtain deprecated CLI args. Set them to cfg if specified in command line.
deprecated := nfs.FlagSet("deprecated") deprecated := o.Flags.FlagSet("deprecated")
if deprecated.Changed("profiling") { if deprecated.Changed("profiling") {
cfg.EnableProfiling = o.ComponentConfig.EnableProfiling o.ComponentConfig.EnableProfiling = o.Deprecated.EnableProfiling
} }
if deprecated.Changed("contention-profiling") { if deprecated.Changed("contention-profiling") {
cfg.EnableContentionProfiling = o.ComponentConfig.EnableContentionProfiling o.ComponentConfig.EnableContentionProfiling = o.Deprecated.EnableContentionProfiling
} }
if deprecated.Changed("kubeconfig") { if deprecated.Changed("kubeconfig") {
cfg.ClientConnection.Kubeconfig = o.ComponentConfig.ClientConnection.Kubeconfig o.ComponentConfig.ClientConnection.Kubeconfig = o.Deprecated.Kubeconfig
} }
if deprecated.Changed("kube-api-content-type") { if deprecated.Changed("kube-api-content-type") {
cfg.ClientConnection.ContentType = o.ComponentConfig.ClientConnection.ContentType o.ComponentConfig.ClientConnection.ContentType = o.Deprecated.ContentType
} }
if deprecated.Changed("kube-api-qps") { if deprecated.Changed("kube-api-qps") {
cfg.ClientConnection.QPS = o.ComponentConfig.ClientConnection.QPS o.ComponentConfig.ClientConnection.QPS = o.Deprecated.QPS
} }
if deprecated.Changed("kube-api-burst") { if deprecated.Changed("kube-api-burst") {
cfg.ClientConnection.Burst = o.ComponentConfig.ClientConnection.Burst o.ComponentConfig.ClientConnection.Burst = o.Deprecated.Burst
} }
if deprecated.Changed("lock-object-namespace") { if deprecated.Changed("lock-object-namespace") {
cfg.LeaderElection.ResourceNamespace = o.ComponentConfig.LeaderElection.ResourceNamespace o.ComponentConfig.LeaderElection.ResourceNamespace = o.Deprecated.ResourceNamespace
} }
if deprecated.Changed("lock-object-name") { if deprecated.Changed("lock-object-name") {
cfg.LeaderElection.ResourceName = o.ComponentConfig.LeaderElection.ResourceName o.ComponentConfig.LeaderElection.ResourceName = o.Deprecated.ResourceName
} }
// Obtain CLI args related with leaderelection. Set them to cfg if specified in command line.
leaderelection := nfs.FlagSet("leader election")
if leaderelection.Changed("leader-elect") {
cfg.LeaderElection.LeaderElect = o.ComponentConfig.LeaderElection.LeaderElect
}
if leaderelection.Changed("leader-elect-lease-duration") {
cfg.LeaderElection.LeaseDuration = o.ComponentConfig.LeaderElection.LeaseDuration
}
if leaderelection.Changed("leader-elect-renew-deadline") {
cfg.LeaderElection.RenewDeadline = o.ComponentConfig.LeaderElection.RenewDeadline
}
if leaderelection.Changed("leader-elect-retry-period") {
cfg.LeaderElection.RetryPeriod = o.ComponentConfig.LeaderElection.RetryPeriod
}
if leaderelection.Changed("leader-elect-resource-lock") {
cfg.LeaderElection.ResourceLock = o.ComponentConfig.LeaderElection.ResourceLock
}
if leaderelection.Changed("leader-elect-resource-name") {
cfg.LeaderElection.ResourceName = o.ComponentConfig.LeaderElection.ResourceName
}
if leaderelection.Changed("leader-elect-resource-namespace") {
cfg.LeaderElection.ResourceNamespace = o.ComponentConfig.LeaderElection.ResourceNamespace
}
o.ComponentConfig = *cfg
return nil
} }
// Flags returns flags for a specific scheduler by section name // ApplyLeaderElectionTo obtains the CLI args related with leaderelection, and override the values in `cfg`.
func (o *Options) Flags() (nfs cliflag.NamedFlagSets) { // Then the `cfg` object is injected into the `options` object.
func (o *Options) ApplyLeaderElectionTo(cfg *kubeschedulerconfig.KubeSchedulerConfiguration) {
if o.Flags == nil {
return
}
// Obtain CLI args related with leaderelection. Set them to `cfg` if specified in command line.
leaderelection := o.Flags.FlagSet("leader election")
if leaderelection.Changed("leader-elect") {
cfg.LeaderElection.LeaderElect = o.LeaderElection.LeaderElect
}
if leaderelection.Changed("leader-elect-lease-duration") {
cfg.LeaderElection.LeaseDuration = o.LeaderElection.LeaseDuration
}
if leaderelection.Changed("leader-elect-renew-deadline") {
cfg.LeaderElection.RenewDeadline = o.LeaderElection.RenewDeadline
}
if leaderelection.Changed("leader-elect-retry-period") {
cfg.LeaderElection.RetryPeriod = o.LeaderElection.RetryPeriod
}
if leaderelection.Changed("leader-elect-resource-lock") {
cfg.LeaderElection.ResourceLock = o.LeaderElection.ResourceLock
}
if leaderelection.Changed("leader-elect-resource-name") {
cfg.LeaderElection.ResourceName = o.LeaderElection.ResourceName
}
if leaderelection.Changed("leader-elect-resource-namespace") {
cfg.LeaderElection.ResourceNamespace = o.LeaderElection.ResourceNamespace
}
o.ComponentConfig = cfg
}
// initFlags initializes flags by section name.
func (o *Options) initFlags() {
if o.Flags != nil {
return
}
nfs := cliflag.NamedFlagSets{}
fs := nfs.FlagSet("misc") fs := nfs.FlagSet("misc")
fs.StringVar(&o.ConfigFile, "config", o.ConfigFile, "The path to the configuration file.") fs.StringVar(&o.ConfigFile, "config", o.ConfigFile, "The path to the configuration file.")
fs.StringVar(&o.WriteConfigTo, "write-config-to", o.WriteConfigTo, "If set, write the configuration values to this file and exit.") fs.StringVar(&o.WriteConfigTo, "write-config-to", o.WriteConfigTo, "If set, write the configuration values to this file and exit.")
@ -164,25 +188,30 @@ func (o *Options) Flags() (nfs cliflag.NamedFlagSets) {
o.SecureServing.AddFlags(nfs.FlagSet("secure serving")) o.SecureServing.AddFlags(nfs.FlagSet("secure serving"))
o.Authentication.AddFlags(nfs.FlagSet("authentication")) o.Authentication.AddFlags(nfs.FlagSet("authentication"))
o.Authorization.AddFlags(nfs.FlagSet("authorization")) o.Authorization.AddFlags(nfs.FlagSet("authorization"))
o.Deprecated.AddFlags(nfs.FlagSet("deprecated"), &o.ComponentConfig) o.Deprecated.AddFlags(nfs.FlagSet("deprecated"))
options.BindLeaderElectionFlags(o.LeaderElection, nfs.FlagSet("leader election"))
options.BindLeaderElectionFlags(&o.ComponentConfig.LeaderElection, nfs.FlagSet("leader election"))
utilfeature.DefaultMutableFeatureGate.AddFlag(nfs.FlagSet("feature gate")) utilfeature.DefaultMutableFeatureGate.AddFlag(nfs.FlagSet("feature gate"))
o.Metrics.AddFlags(nfs.FlagSet("metrics")) o.Metrics.AddFlags(nfs.FlagSet("metrics"))
o.Logs.AddFlags(nfs.FlagSet("logs")) o.Logs.AddFlags(nfs.FlagSet("logs"))
return nfs o.Flags = &nfs
} }
// ApplyTo applies the scheduler options to the given scheduler app configuration. // ApplyTo applies the scheduler options to the given scheduler app configuration.
func (o *Options) ApplyTo(c *schedulerappconfig.Config) error { func (o *Options) ApplyTo(c *schedulerappconfig.Config) error {
if len(o.ConfigFile) == 0 { if len(o.ConfigFile) == 0 {
c.ComponentConfig = o.ComponentConfig // If the --config arg is not specified, honor the deprecated as well as leader election CLI args.
o.ApplyDeprecated()
o.ApplyLeaderElectionTo(o.ComponentConfig)
c.ComponentConfig = *o.ComponentConfig
} else { } else {
cfg, err := loadConfigFromFile(o.ConfigFile) cfg, err := loadConfigFromFile(o.ConfigFile)
if err != nil { if err != nil {
return err return err
} }
// If the --config arg is specified, honor the leader election CLI args only.
o.ApplyLeaderElectionTo(cfg)
if err := validation.ValidateKubeSchedulerConfiguration(cfg); err != nil { if err := validation.ValidateKubeSchedulerConfiguration(cfg); err != nil {
return err return err
} }
@ -210,7 +239,7 @@ func (o *Options) ApplyTo(c *schedulerappconfig.Config) error {
func (o *Options) Validate() []error { func (o *Options) Validate() []error {
var errs []error var errs []error
if err := validation.ValidateKubeSchedulerConfiguration(&o.ComponentConfig); err != nil { if err := validation.ValidateKubeSchedulerConfiguration(o.ComponentConfig); err != nil {
errs = append(errs, err.Errors()...) errs = append(errs, err.Errors()...)
} }
errs = append(errs, o.SecureServing.Validate()...) errs = append(errs, o.SecureServing.Validate()...)

View File

@ -340,9 +340,9 @@ profiles:
name: "v1beta3 config file", name: "v1beta3 config file",
options: &Options{ options: &Options{
ConfigFile: configFile, ConfigFile: configFile,
ComponentConfig: func() kubeschedulerconfig.KubeSchedulerConfiguration { ComponentConfig: func() *kubeschedulerconfig.KubeSchedulerConfiguration {
cfg := configtesting.V1beta3ToInternalWithDefaults(t, v1beta3.KubeSchedulerConfiguration{}) cfg := configtesting.V1beta3ToInternalWithDefaults(t, v1beta3.KubeSchedulerConfiguration{})
return *cfg return cfg
}(), }(),
SecureServing: (&apiserveroptions.SecureServingOptions{ SecureServing: (&apiserveroptions.SecureServingOptions{
ServerCert: apiserveroptions.GeneratableKeyCert{ ServerCert: apiserveroptions.GeneratableKeyCert{
@ -412,9 +412,9 @@ profiles:
name: "v1beta2 config file", name: "v1beta2 config file",
options: &Options{ options: &Options{
ConfigFile: v1beta2VersionConfig, ConfigFile: v1beta2VersionConfig,
ComponentConfig: func() kubeschedulerconfig.KubeSchedulerConfiguration { ComponentConfig: func() *kubeschedulerconfig.KubeSchedulerConfiguration {
cfg := configtesting.V1beta2ToInternalWithDefaults(t, v1beta2.KubeSchedulerConfiguration{}) cfg := configtesting.V1beta2ToInternalWithDefaults(t, v1beta2.KubeSchedulerConfiguration{})
return *cfg return cfg
}(), }(),
SecureServing: (&apiserveroptions.SecureServingOptions{ SecureServing: (&apiserveroptions.SecureServingOptions{
ServerCert: apiserveroptions.GeneratableKeyCert{ ServerCert: apiserveroptions.GeneratableKeyCert{
@ -483,12 +483,12 @@ profiles:
name: "config file in componentconfig/v1alpha1", name: "config file in componentconfig/v1alpha1",
options: &Options{ options: &Options{
ConfigFile: oldConfigFile, ConfigFile: oldConfigFile,
ComponentConfig: func() kubeschedulerconfig.KubeSchedulerConfiguration { ComponentConfig: func() *kubeschedulerconfig.KubeSchedulerConfiguration {
cfg, err := latest.Default() cfg, err := latest.Default()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
return *cfg return cfg
}(), }(),
Logs: logs.NewOptions(), Logs: logs.NewOptions(),
}, },
@ -513,10 +513,10 @@ profiles:
{ {
name: "kubeconfig flag", name: "kubeconfig flag",
options: &Options{ options: &Options{
ComponentConfig: func() kubeschedulerconfig.KubeSchedulerConfiguration { ComponentConfig: func() *kubeschedulerconfig.KubeSchedulerConfiguration {
cfg, _ := latest.Default() cfg, _ := latest.Default()
cfg.ClientConnection.Kubeconfig = flagKubeconfig cfg.ClientConnection.Kubeconfig = flagKubeconfig
return *cfg return cfg
}(), }(),
SecureServing: (&apiserveroptions.SecureServingOptions{ SecureServing: (&apiserveroptions.SecureServingOptions{
ServerCert: apiserveroptions.GeneratableKeyCert{ ServerCert: apiserveroptions.GeneratableKeyCert{
@ -584,10 +584,10 @@ profiles:
{ {
name: "overridden master", name: "overridden master",
options: &Options{ options: &Options{
ComponentConfig: func() kubeschedulerconfig.KubeSchedulerConfiguration { ComponentConfig: func() *kubeschedulerconfig.KubeSchedulerConfiguration {
cfg, _ := latest.Default() cfg, _ := latest.Default()
cfg.ClientConnection.Kubeconfig = flagKubeconfig cfg.ClientConnection.Kubeconfig = flagKubeconfig
return *cfg return cfg
}(), }(),
Master: insecureserver.URL, Master: insecureserver.URL,
SecureServing: (&apiserveroptions.SecureServingOptions{ SecureServing: (&apiserveroptions.SecureServingOptions{
@ -1149,6 +1149,13 @@ profiles:
for _, tc := range testcases { for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
if tc.options.ComponentConfig == nil {
if cfg, err := latest.Default(); err != nil {
t.Fatal(err)
} else {
tc.options.ComponentConfig = cfg
}
}
// create the config // create the config
config, err := tc.options.Config() config, err := tc.options.Config()

View File

@ -53,6 +53,7 @@ import (
"k8s.io/kubernetes/cmd/kube-scheduler/app/options" "k8s.io/kubernetes/cmd/kube-scheduler/app/options"
"k8s.io/kubernetes/pkg/scheduler" "k8s.io/kubernetes/pkg/scheduler"
kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
"k8s.io/kubernetes/pkg/scheduler/apis/config/latest"
"k8s.io/kubernetes/pkg/scheduler/framework/runtime" "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
"k8s.io/kubernetes/pkg/scheduler/metrics/resources" "k8s.io/kubernetes/pkg/scheduler/metrics/resources"
"k8s.io/kubernetes/pkg/scheduler/profile" "k8s.io/kubernetes/pkg/scheduler/profile"
@ -63,13 +64,8 @@ type Option func(runtime.Registry) error
// NewSchedulerCommand creates a *cobra.Command object with default parameters and registryOptions // NewSchedulerCommand creates a *cobra.Command object with default parameters and registryOptions
func NewSchedulerCommand(registryOptions ...Option) *cobra.Command { func NewSchedulerCommand(registryOptions ...Option) *cobra.Command {
opts, err := options.NewOptions() opts := options.NewOptions()
if err != nil {
klog.ErrorS(err, "Unable to initialize command options")
os.Exit(1)
}
namedFlagSets := opts.Flags()
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "kube-scheduler", Use: "kube-scheduler",
Long: `The Kubernetes scheduler is a control plane process which assigns Long: `The Kubernetes scheduler is a control plane process which assigns
@ -81,9 +77,6 @@ kube-scheduler is the reference implementation.
See [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/) See [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/)
for more information about scheduling and the kube-scheduler component.`, for more information about scheduling and the kube-scheduler component.`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if err := opts.Complete(&namedFlagSets); err != nil {
return err
}
return runCommand(cmd, opts, registryOptions...) return runCommand(cmd, opts, registryOptions...)
}, },
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
@ -96,15 +89,16 @@ for more information about scheduling and the kube-scheduler component.`,
}, },
} }
nfs := opts.Flags
verflag.AddFlags(nfs.FlagSet("global"))
globalflag.AddGlobalFlags(nfs.FlagSet("global"), cmd.Name())
fs := cmd.Flags() fs := cmd.Flags()
verflag.AddFlags(namedFlagSets.FlagSet("global")) for _, f := range nfs.FlagSets {
globalflag.AddGlobalFlags(namedFlagSets.FlagSet("global"), cmd.Name())
for _, f := range namedFlagSets.FlagSets {
fs.AddFlagSet(f) fs.AddFlagSet(f)
} }
cols, _, _ := term.TerminalSize(cmd.OutOrStdout()) cols, _, _ := term.TerminalSize(cmd.OutOrStdout())
cliflag.SetUsageAndHelpFunc(cmd, namedFlagSets, cols) cliflag.SetUsageAndHelpFunc(cmd, *nfs, cols)
cmd.MarkFlagFilename("config", "yaml", "yml", "json") cmd.MarkFlagFilename("config", "yaml", "yml", "json")
@ -284,6 +278,12 @@ func WithPlugin(name string, factory runtime.PluginFactory) Option {
// Setup creates a completed config and a scheduler based on the command args and options // Setup creates a completed config and a scheduler based on the command args and options
func Setup(ctx context.Context, opts *options.Options, outOfTreeRegistryOptions ...Option) (*schedulerserverconfig.CompletedConfig, *scheduler.Scheduler, error) { func Setup(ctx context.Context, opts *options.Options, outOfTreeRegistryOptions ...Option) (*schedulerserverconfig.CompletedConfig, *scheduler.Scheduler, error) {
if cfg, err := latest.Default(); err != nil {
return nil, nil, err
} else {
opts.ComponentConfig = cfg
}
if errs := opts.Validate(); len(errs) > 0 { if errs := opts.Validate(); len(errs) > 0 {
return nil, nil, utilerrors.NewAggregate(errs) return nil, nil, utilerrors.NewAggregate(errs)
} }

View File

@ -29,9 +29,15 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/apiserver/pkg/util/feature"
componentbaseconfig "k8s.io/component-base/config"
"k8s.io/component-base/featuregate"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/cmd/kube-scheduler/app/options" "k8s.io/kubernetes/cmd/kube-scheduler/app/options"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/scheduler/apis/config" "k8s.io/kubernetes/pkg/scheduler/apis/config"
"k8s.io/kubernetes/pkg/scheduler/apis/config/testing/defaults" "k8s.io/kubernetes/pkg/scheduler/apis/config/testing/defaults"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/names"
) )
func TestSetup(t *testing.T) { func TestSetup(t *testing.T) {
@ -140,10 +146,45 @@ profiles:
} }
testcases := []struct { testcases := []struct {
name string name string
flags []string flags []string
wantPlugins map[string]*config.Plugins restoreFeatures map[featuregate.Feature]bool
wantPlugins map[string]*config.Plugins
wantLeaderElection *componentbaseconfig.LeaderElectionConfiguration
}{ }{
{
name: "default config with an alpha feature enabled and an beta feature disabled",
flags: []string{
"--kubeconfig", configKubeconfig,
"--feature-gates=VolumeCapacityPriority=true,DefaultPodTopologySpread=false",
},
wantPlugins: map[string]*config.Plugins{
"default-scheduler": func() *config.Plugins {
plugins := &config.Plugins{
QueueSort: defaults.PluginsV1beta3.QueueSort,
PreFilter: defaults.PluginsV1beta3.PreFilter,
Filter: defaults.PluginsV1beta3.Filter,
PostFilter: defaults.PluginsV1beta3.PostFilter,
PreScore: defaults.PluginsV1beta3.PreScore,
Score: defaults.PluginsV1beta3.Score,
Bind: defaults.PluginsV1beta3.Bind,
PreBind: defaults.PluginsV1beta3.PreBind,
Reserve: defaults.PluginsV1beta3.Reserve,
}
plugins.PreScore.Enabled = append(plugins.PreScore.Enabled, config.Plugin{Name: names.SelectorSpread, Weight: 0})
plugins.Score.Enabled = append(
plugins.Score.Enabled,
config.Plugin{Name: names.VolumeBinding, Weight: 1},
config.Plugin{Name: names.SelectorSpread, Weight: 1},
)
return plugins
}(),
},
restoreFeatures: map[featuregate.Feature]bool{
features.VolumeCapacityPriority: false,
features.DefaultPodTopologySpread: true,
},
},
{ {
name: "default config", name: "default config",
flags: []string{ flags: []string{
@ -206,13 +247,18 @@ profiles:
for _, tc := range testcases { for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
fs := pflag.NewFlagSet("test", pflag.PanicOnError) for k, v := range tc.restoreFeatures {
opts, err := options.NewOptions() defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, k, v)()
if err != nil {
t.Fatal(err)
} }
nfs := opts.Flags() fs := pflag.NewFlagSet("test", pflag.PanicOnError)
opts := options.NewOptions()
// use listeners instead of static ports so parallel test runs don't conflict
opts.SecureServing.Listener = makeListener(t)
defer opts.SecureServing.Listener.Close()
nfs := opts.Flags
for _, f := range nfs.FlagSets { for _, f := range nfs.FlagSets {
fs.AddFlagSet(f) fs.AddFlagSet(f)
} }
@ -220,10 +266,6 @@ profiles:
t.Fatal(err) t.Fatal(err)
} }
if err := opts.Complete(&nfs); err != nil {
t.Fatal(err)
}
// use listeners instead of static ports so parallel test runs don't conflict // use listeners instead of static ports so parallel test runs don't conflict
opts.SecureServing.Listener = makeListener(t) opts.SecureServing.Listener = makeListener(t)
defer opts.SecureServing.Listener.Close() defer opts.SecureServing.Listener.Close()

View File

@ -82,21 +82,13 @@ func StartTestServer(t Logger, customFlags []string) (result TestServer, err err
fs := pflag.NewFlagSet("test", pflag.PanicOnError) fs := pflag.NewFlagSet("test", pflag.PanicOnError)
opts, err := options.NewOptions() opts := options.NewOptions()
if err != nil { nfs := opts.Flags
return TestServer{}, err for _, f := range nfs.FlagSets {
}
namedFlagSets := opts.Flags()
for _, f := range namedFlagSets.FlagSets {
fs.AddFlagSet(f) fs.AddFlagSet(f)
} }
fs.Parse(customFlags) fs.Parse(customFlags)
if err := opts.Complete(&namedFlagSets); err != nil {
return TestServer{}, err
}
if opts.SecureServing.BindPort != 0 { if opts.SecureServing.BindPort != 0 {
opts.SecureServing.Listener, opts.SecureServing.BindPort, err = createListenerOnFreePort() opts.SecureServing.Listener, opts.SecureServing.BindPort, err = createListenerOnFreePort()
if err != nil { if err != nil {