Feat: ga component config in kube-scheduler

Signed-off-by: kerthcet <kerthcet@gmail.com>
This commit is contained in:
kerthcet
2022-07-29 08:47:48 +08:00
parent 7b9a20b587
commit 02f77a1b84
33 changed files with 6872 additions and 130 deletions

View File

@@ -17,13 +17,14 @@ limitations under the License.
package validation
import (
"fmt"
"strings"
"testing"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
componentbaseconfig "k8s.io/component-base/config"
"k8s.io/kubernetes/pkg/scheduler/apis/config"
configv1 "k8s.io/kubernetes/pkg/scheduler/apis/config/v1"
"k8s.io/kubernetes/pkg/scheduler/apis/config/v1beta2"
"k8s.io/kubernetes/pkg/scheduler/apis/config/v1beta3"
)
@@ -196,10 +197,11 @@ func TestValidateKubeSchedulerConfigurationV1beta2(t *testing.T) {
extenderDuplicateBind.Extenders = append(extenderDuplicateBind.Extenders, config.Extender{
PrioritizeVerb: "prioritize",
BindVerb: "bar",
Weight: 1,
})
goodInvalidPlugins := validConfig.DeepCopy()
goodInvalidPlugins.Profiles[0].Plugins.Score.Enabled = append(goodInvalidPlugins.Profiles[0].Plugins.Score.Enabled, config.Plugin{Name: "PodTopologySpread", Weight: 2})
validPlugins := validConfig.DeepCopy()
validPlugins.Profiles[0].Plugins.Score.Enabled = append(validPlugins.Profiles[0].Plugins.Score.Enabled, config.Plugin{Name: "PodTopologySpread", Weight: 2})
scenarios := map[string]struct {
expectedToFail bool
@@ -213,74 +215,91 @@ func TestValidateKubeSchedulerConfigurationV1beta2(t *testing.T) {
"bad-parallelism-invalid-value": {
expectedToFail: true,
config: invalidParallelismValue,
errorString: "should be an integer value greater than zero",
},
"bad-resource-name-not-set": {
expectedToFail: true,
config: resourceNameNotSet,
errorString: "resourceName is required",
},
"bad-resource-namespace-not-set": {
expectedToFail: true,
config: resourceNamespaceNotSet,
errorString: "resourceNamespace is required",
},
"non-empty-metrics-bind-addr": {
expectedToFail: true,
config: metricsBindAddrInvalid,
errorString: "must be empty or with an explicit 0 port",
},
"non-empty-healthz-bind-addr": {
expectedToFail: true,
config: healthzBindAddrInvalid,
errorString: "must be empty or with an explicit 0 port",
},
"bad-percentage-of-nodes-to-score": {
expectedToFail: true,
config: percentageOfNodesToScore101,
errorString: "not in valid range [0-100]",
},
"scheduler-name-not-set": {
expectedToFail: true,
config: schedulerNameNotSet,
errorString: "Required value",
},
"repeated-scheduler-name": {
expectedToFail: true,
config: repeatedSchedulerName,
errorString: "Duplicate value",
},
"different-queue-sort": {
expectedToFail: true,
config: differentQueueSort,
errorString: "has to match for all profiles",
},
"one-empty-queue-sort": {
expectedToFail: true,
config: oneEmptyQueueSort,
errorString: "has to match for all profiles",
},
"extender-negative-weight": {
expectedToFail: true,
config: extenderNegativeWeight,
errorString: "must have a positive weight applied to it",
},
"extender-duplicate-managed-resources": {
expectedToFail: true,
config: extenderDuplicateManagedResource,
errorString: "duplicate extender managed resource name",
},
"extender-duplicate-bind": {
expectedToFail: true,
config: extenderDuplicateBind,
errorString: "only one extender can implement bind",
},
"invalid-node-percentage": {
expectedToFail: true,
config: invalidNodePercentage,
errorString: "not in valid range [0, 100]",
},
"invalid-plugin-args": {
expectedToFail: true,
config: invalidPluginArgs,
errorString: "has to match plugin args",
},
"duplicated-plugin-config": {
expectedToFail: true,
config: duplicatedPluginConfig,
errorString: "Duplicate value: \"config\"",
},
"mismatch-queue-sort": {
expectedToFail: true,
config: mismatchQueueSort,
errorString: "has to match for all profiles",
},
"good-invalid-plugins": {
"valid-plugins": {
expectedToFail: false,
config: goodInvalidPlugins,
config: validPlugins,
},
}
@@ -293,9 +312,8 @@ func TestValidateKubeSchedulerConfigurationV1beta2(t *testing.T) {
if errs != nil && !scenario.expectedToFail {
t.Errorf("Unexpected failure: %+v", errs)
}
fmt.Println(errs)
if errs != nil && scenario.errorString != "" && errs.Error() != scenario.errorString {
if errs != nil && scenario.errorString != "" && !strings.Contains(errs.Error(), scenario.errorString) {
t.Errorf("Unexpected error string\n want:\t%s\n got:\t%s", scenario.errorString, errs.Error())
}
})
@@ -470,10 +488,11 @@ func TestValidateKubeSchedulerConfigurationV1beta3(t *testing.T) {
extenderDuplicateBind.Extenders = append(extenderDuplicateBind.Extenders, config.Extender{
PrioritizeVerb: "prioritize",
BindVerb: "bar",
Weight: 1,
})
goodInvalidPlugins := validConfig.DeepCopy()
goodInvalidPlugins.Profiles[0].Plugins.Score.Enabled = append(goodInvalidPlugins.Profiles[0].Plugins.Score.Enabled, config.Plugin{Name: "PodTopologySpread", Weight: 2})
validPlugins := validConfig.DeepCopy()
validPlugins.Profiles[0].Plugins.Score.Enabled = append(validPlugins.Profiles[0].Plugins.Score.Enabled, config.Plugin{Name: "PodTopologySpread", Weight: 2})
scenarios := map[string]struct {
expectedToFail bool
@@ -487,74 +506,91 @@ func TestValidateKubeSchedulerConfigurationV1beta3(t *testing.T) {
"bad-parallelism-invalid-value": {
expectedToFail: true,
config: invalidParallelismValue,
errorString: "should be an integer value greater than zero",
},
"bad-resource-name-not-set": {
expectedToFail: true,
config: resourceNameNotSet,
errorString: "resourceName is required",
},
"bad-resource-namespace-not-set": {
expectedToFail: true,
config: resourceNamespaceNotSet,
errorString: "resourceNamespace is required",
},
"non-empty-metrics-bind-addr": {
expectedToFail: true,
config: metricsBindAddrInvalid,
errorString: "must be empty or with an explicit 0 port",
},
"non-empty-healthz-bind-addr": {
expectedToFail: true,
config: healthzBindAddrInvalid,
errorString: "must be empty or with an explicit 0 port",
},
"bad-percentage-of-nodes-to-score": {
expectedToFail: true,
config: percentageOfNodesToScore101,
errorString: "not in valid range [0-100]",
},
"scheduler-name-not-set": {
expectedToFail: true,
config: schedulerNameNotSet,
errorString: "Required value",
},
"repeated-scheduler-name": {
expectedToFail: true,
config: repeatedSchedulerName,
errorString: "Duplicate value",
},
"different-queue-sort": {
expectedToFail: true,
config: differentQueueSort,
errorString: "has to match for all profiles",
},
"one-empty-queue-sort": {
expectedToFail: true,
config: oneEmptyQueueSort,
errorString: "has to match for all profiles",
},
"extender-negative-weight": {
expectedToFail: true,
config: extenderNegativeWeight,
errorString: "must have a positive weight applied to it",
},
"extender-duplicate-managed-resources": {
expectedToFail: true,
config: extenderDuplicateManagedResource,
errorString: "duplicate extender managed resource name",
},
"extender-duplicate-bind": {
expectedToFail: true,
config: extenderDuplicateBind,
errorString: "only one extender can implement bind",
},
"invalid-node-percentage": {
expectedToFail: true,
config: invalidNodePercentage,
errorString: "not in valid range [0, 100]",
},
"invalid-plugin-args": {
expectedToFail: true,
config: invalidPluginArgs,
errorString: "has to match plugin args",
},
"duplicated-plugin-config": {
expectedToFail: true,
config: duplicatedPluginConfig,
errorString: "Duplicate value: \"config\"",
},
"mismatch-queue-sort": {
expectedToFail: true,
config: mismatchQueueSort,
errorString: "has to match for all profiles",
},
"good-invalid-plugins": {
"valid-plugins": {
expectedToFail: false,
config: goodInvalidPlugins,
config: validPlugins,
},
}
@@ -567,9 +603,307 @@ func TestValidateKubeSchedulerConfigurationV1beta3(t *testing.T) {
if errs != nil && !scenario.expectedToFail {
t.Errorf("Unexpected failure: %+v", errs)
}
fmt.Println(errs)
if errs != nil && scenario.errorString != "" && errs.Error() != scenario.errorString {
if errs != nil && scenario.errorString != "" && !strings.Contains(errs.Error(), scenario.errorString) {
t.Errorf("Unexpected error string\n want:\t%s\n got:\t%s", scenario.errorString, errs.Error())
}
})
}
}
func TestValidateKubeSchedulerConfigurationV1(t *testing.T) {
podInitialBackoffSeconds := int64(1)
podMaxBackoffSeconds := int64(1)
validConfig := &config.KubeSchedulerConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: configv1.SchemeGroupVersion.String(),
},
Parallelism: 8,
ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
AcceptContentTypes: "application/json",
ContentType: "application/json",
QPS: 10,
Burst: 10,
},
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
ResourceLock: "configmap",
LeaderElect: true,
LeaseDuration: metav1.Duration{Duration: 30 * time.Second},
RenewDeadline: metav1.Duration{Duration: 15 * time.Second},
RetryPeriod: metav1.Duration{Duration: 5 * time.Second},
ResourceNamespace: "name",
ResourceName: "name",
},
PodInitialBackoffSeconds: podInitialBackoffSeconds,
PodMaxBackoffSeconds: podMaxBackoffSeconds,
PercentageOfNodesToScore: 35,
Profiles: []config.KubeSchedulerProfile{
{
SchedulerName: "me",
Plugins: &config.Plugins{
QueueSort: config.PluginSet{
Enabled: []config.Plugin{{Name: "CustomSort"}},
},
Score: config.PluginSet{
Disabled: []config.Plugin{{Name: "*"}},
},
},
PluginConfig: []config.PluginConfig{
{
Name: "DefaultPreemption",
Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 10, MinCandidateNodesAbsolute: 100},
},
},
},
{
SchedulerName: "other",
Plugins: &config.Plugins{
QueueSort: config.PluginSet{
Enabled: []config.Plugin{{Name: "CustomSort"}},
},
Bind: config.PluginSet{
Enabled: []config.Plugin{{Name: "CustomBind"}},
},
},
},
},
Extenders: []config.Extender{
{
PrioritizeVerb: "prioritize",
Weight: 1,
},
},
}
invalidParallelismValue := validConfig.DeepCopy()
invalidParallelismValue.Parallelism = 0
resourceNameNotSet := validConfig.DeepCopy()
resourceNameNotSet.LeaderElection.ResourceName = ""
resourceNamespaceNotSet := validConfig.DeepCopy()
resourceNamespaceNotSet.LeaderElection.ResourceNamespace = ""
enableContentProfilingSetWithoutEnableProfiling := validConfig.DeepCopy()
enableContentProfilingSetWithoutEnableProfiling.EnableProfiling = false
enableContentProfilingSetWithoutEnableProfiling.EnableContentionProfiling = true
metricsBindAddrInvalid := validConfig.DeepCopy()
metricsBindAddrInvalid.MetricsBindAddress = "0.0.0.0:9090"
healthzBindAddrInvalid := validConfig.DeepCopy()
healthzBindAddrInvalid.HealthzBindAddress = "0.0.0.0:9090"
percentageOfNodesToScore101 := validConfig.DeepCopy()
percentageOfNodesToScore101.PercentageOfNodesToScore = int32(101)
schedulerNameNotSet := validConfig.DeepCopy()
schedulerNameNotSet.Profiles[1].SchedulerName = ""
repeatedSchedulerName := validConfig.DeepCopy()
repeatedSchedulerName.Profiles[0].SchedulerName = "other"
differentQueueSort := validConfig.DeepCopy()
differentQueueSort.Profiles[1].Plugins.QueueSort.Enabled[0].Name = "AnotherSort"
oneEmptyQueueSort := validConfig.DeepCopy()
oneEmptyQueueSort.Profiles[0].Plugins = nil
extenderNegativeWeight := validConfig.DeepCopy()
extenderNegativeWeight.Extenders[0].Weight = -1
invalidNodePercentage := validConfig.DeepCopy()
invalidNodePercentage.Profiles[0].PluginConfig = []config.PluginConfig{
{
Name: "DefaultPreemption",
Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 200, MinCandidateNodesAbsolute: 100},
},
}
invalidPluginArgs := validConfig.DeepCopy()
invalidPluginArgs.Profiles[0].PluginConfig = []config.PluginConfig{
{
Name: "DefaultPreemption",
Args: &config.InterPodAffinityArgs{},
},
}
duplicatedPluginConfig := validConfig.DeepCopy()
duplicatedPluginConfig.Profiles[0].PluginConfig = []config.PluginConfig{
{
Name: "config",
},
{
Name: "config",
},
}
mismatchQueueSort := validConfig.DeepCopy()
mismatchQueueSort.Profiles = []config.KubeSchedulerProfile{
{
SchedulerName: "me",
Plugins: &config.Plugins{
QueueSort: config.PluginSet{
Enabled: []config.Plugin{{Name: "PrioritySort"}},
},
},
PluginConfig: []config.PluginConfig{
{
Name: "PrioritySort",
},
},
},
{
SchedulerName: "other",
Plugins: &config.Plugins{
QueueSort: config.PluginSet{
Enabled: []config.Plugin{{Name: "CustomSort"}},
},
},
PluginConfig: []config.PluginConfig{
{
Name: "CustomSort",
},
},
},
}
extenderDuplicateManagedResource := validConfig.DeepCopy()
extenderDuplicateManagedResource.Extenders[0].ManagedResources = []config.ExtenderManagedResource{
{Name: "foo", IgnoredByScheduler: false},
{Name: "foo", IgnoredByScheduler: false},
}
extenderDuplicateBind := validConfig.DeepCopy()
extenderDuplicateBind.Extenders[0].BindVerb = "foo"
extenderDuplicateBind.Extenders = append(extenderDuplicateBind.Extenders, config.Extender{
PrioritizeVerb: "prioritize",
BindVerb: "bar",
Weight: 1,
})
validPlugins := validConfig.DeepCopy()
validPlugins.Profiles[0].Plugins.Score.Enabled = append(validPlugins.Profiles[0].Plugins.Score.Enabled, config.Plugin{Name: "PodTopologySpread", Weight: 2})
invalidPlugins := validConfig.DeepCopy()
invalidPlugins.Profiles[0].Plugins.Score.Enabled = append(invalidPlugins.Profiles[0].Plugins.Score.Enabled, config.Plugin{Name: "SelectorSpread"})
scenarios := map[string]struct {
expectedToFail bool
config *config.KubeSchedulerConfiguration
errorString string
}{
"good": {
expectedToFail: false,
config: validConfig,
},
"bad-parallelism-invalid-value": {
expectedToFail: true,
config: invalidParallelismValue,
errorString: "should be an integer value greater than zero",
},
"bad-resource-name-not-set": {
expectedToFail: true,
config: resourceNameNotSet,
errorString: "resourceName is required",
},
"bad-resource-namespace-not-set": {
expectedToFail: true,
config: resourceNamespaceNotSet,
errorString: "resourceNamespace is required",
},
"non-empty-metrics-bind-addr": {
expectedToFail: true,
config: metricsBindAddrInvalid,
errorString: "must be empty or with an explicit 0 port",
},
"non-empty-healthz-bind-addr": {
expectedToFail: true,
config: healthzBindAddrInvalid,
errorString: "must be empty or with an explicit 0 port",
},
"bad-percentage-of-nodes-to-score": {
expectedToFail: true,
config: percentageOfNodesToScore101,
errorString: "not in valid range [0-100]",
},
"scheduler-name-not-set": {
expectedToFail: true,
config: schedulerNameNotSet,
errorString: "Required value",
},
"repeated-scheduler-name": {
expectedToFail: true,
config: repeatedSchedulerName,
errorString: "Duplicate value",
},
"different-queue-sort": {
expectedToFail: true,
config: differentQueueSort,
errorString: "has to match for all profiles",
},
"one-empty-queue-sort": {
expectedToFail: true,
config: oneEmptyQueueSort,
errorString: "has to match for all profiles",
},
"extender-negative-weight": {
expectedToFail: true,
config: extenderNegativeWeight,
errorString: "must have a positive weight applied to it",
},
"extender-duplicate-managed-resources": {
expectedToFail: true,
config: extenderDuplicateManagedResource,
errorString: "duplicate extender managed resource name",
},
"extender-duplicate-bind": {
expectedToFail: true,
config: extenderDuplicateBind,
errorString: "only one extender can implement bind",
},
"invalid-node-percentage": {
expectedToFail: true,
config: invalidNodePercentage,
errorString: "not in valid range [0, 100]",
},
"invalid-plugin-args": {
expectedToFail: true,
config: invalidPluginArgs,
errorString: "has to match plugin args",
},
"duplicated-plugin-config": {
expectedToFail: true,
config: duplicatedPluginConfig,
errorString: "Duplicate value: \"config\"",
},
"mismatch-queue-sort": {
expectedToFail: true,
config: mismatchQueueSort,
errorString: "has to match for all profiles",
},
"valid-plugins": {
expectedToFail: false,
config: validPlugins,
},
"invalid-plugins": {
expectedToFail: true,
config: invalidPlugins,
errorString: "\"SelectorSpread\": was invalid",
},
}
for name, scenario := range scenarios {
t.Run(name, func(t *testing.T) {
errs := ValidateKubeSchedulerConfiguration(scenario.config)
if errs == nil && scenario.expectedToFail {
t.Error("Unexpected success")
}
if errs != nil && !scenario.expectedToFail {
t.Errorf("Unexpected failure: %+v", errs)
}
if errs != nil && scenario.errorString != "" && !strings.Contains(errs.Error(), scenario.errorString) {
t.Errorf("Unexpected error string\n want:\t%s\n got:\t%s", scenario.errorString, errs.Error())
}
})