From dd1b4fa407e43b43f0d9ab4e5c98913eb749432b Mon Sep 17 00:00:00 2001 From: Yuan Chen Date: Tue, 18 Oct 2022 15:52:31 -0700 Subject: [PATCH] Replace error string with ErrorList in scheduler valiation test Remove expected status Remove unnecessary code Revert changes to BindVerb --- .../apis/config/validation/validation_test.go | 698 ++++++++++++------ 1 file changed, 458 insertions(+), 240 deletions(-) diff --git a/pkg/scheduler/apis/config/validation/validation_test.go b/pkg/scheduler/apis/config/validation/validation_test.go index c2fb27a002d..679bb87484f 100644 --- a/pkg/scheduler/apis/config/validation/validation_test.go +++ b/pkg/scheduler/apis/config/validation/validation_test.go @@ -17,11 +17,12 @@ limitations under the License. package validation import ( - "strings" "testing" "time" + "github.com/google/go-cmp/cmp" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" componentbaseconfig "k8s.io/component-base/config" "k8s.io/kubernetes/pkg/scheduler/apis/config" configv1 "k8s.io/kubernetes/pkg/scheduler/apis/config/v1" @@ -198,8 +199,8 @@ func TestValidateKubeSchedulerConfigurationV1beta2(t *testing.T) { extenderDuplicateManagedResource := validConfig.DeepCopy() extenderDuplicateManagedResource.Extenders[0].ManagedResources = []config.ExtenderManagedResource{ - {Name: "foo", IgnoredByScheduler: false}, - {Name: "foo", IgnoredByScheduler: false}, + {Name: "example.com/foo", IgnoredByScheduler: false}, + {Name: "example.com/foo", IgnoredByScheduler: false}, } extenderDuplicateBind := validConfig.DeepCopy() @@ -214,132 +215,203 @@ func TestValidateKubeSchedulerConfigurationV1beta2(t *testing.T) { 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 - config *config.KubeSchedulerConfiguration - errorString string + config *config.KubeSchedulerConfiguration + wantErrs field.ErrorList }{ "good": { - expectedToFail: false, - config: validConfig, + config: validConfig, }, "bad-parallelism-invalid-value": { - expectedToFail: true, - config: invalidParallelismValue, - errorString: "should be an integer value greater than zero", + config: invalidParallelismValue, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "parallelism", + }, + }, }, "bad-resource-name-not-set": { - expectedToFail: true, - config: resourceNameNotSet, - errorString: "resourceName is required", + config: resourceNameNotSet, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "leaderElection.resourceName", + }, + }, }, "bad-resource-namespace-not-set": { - expectedToFail: true, - config: resourceNamespaceNotSet, - errorString: "resourceNamespace is required", + config: resourceNamespaceNotSet, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "leaderElection.resourceNamespace", + }, + }, }, "non-empty-metrics-bind-addr": { - expectedToFail: true, - config: metricsBindAddrInvalid, - errorString: "must be empty or with an explicit 0 port", + config: metricsBindAddrInvalid, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "metricsBindAddress", + }, + }, }, "non-empty-healthz-bind-addr": { - expectedToFail: true, - config: healthzBindAddrInvalid, - errorString: "must be empty or with an explicit 0 port", + config: healthzBindAddrInvalid, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "healthzBindAddress", + }, + }, }, "greater-than-100-percentage-of-nodes-to-score": { - expectedToFail: true, - config: percentageOfNodesToScore101, - errorString: "not in valid range [0-100]", + config: percentageOfNodesToScore101, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "percentageOfNodesToScore", + }, + }, }, "negative-percentage-of-nodes-to-score": { - expectedToFail: true, - config: percentageOfNodesToScoreNegative, - errorString: "not in valid range [0-100]", + config: percentageOfNodesToScoreNegative, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "percentageOfNodesToScore", + }, + }, }, "scheduler-name-not-set": { - expectedToFail: true, - config: schedulerNameNotSet, - errorString: "Required value", + config: schedulerNameNotSet, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeRequired, + Field: "profiles[1].schedulerName", + }, + }, }, "repeated-scheduler-name": { - expectedToFail: true, - config: repeatedSchedulerName, - errorString: "Duplicate value", + config: repeatedSchedulerName, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeDuplicate, + Field: "profiles[1].schedulerName", + }, + }, }, "greater-than-100-profile-percentage-of-nodes-to-score": { - expectedToFail: true, - config: profilePercentageOfNodesToScore101, - errorString: "not in valid range [0-100]", + config: profilePercentageOfNodesToScore101, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].percentageOfNodesToScore", + }, + }, }, "negative-100-profile-percentage-of-nodes-to-score": { - expectedToFail: true, - config: profilePercentageOfNodesToScoreNegative, - errorString: "not in valid range [0-100]", + config: profilePercentageOfNodesToScoreNegative, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].percentageOfNodesToScore", + }, + }, }, "different-queue-sort": { - expectedToFail: true, - config: differentQueueSort, - errorString: "has to match for all profiles", + config: differentQueueSort, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].plugins.queueSort", + }, + }, }, "one-empty-queue-sort": { - expectedToFail: true, - config: oneEmptyQueueSort, - errorString: "has to match for all profiles", + config: oneEmptyQueueSort, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].plugins.queueSort", + }, + }, }, "extender-negative-weight": { - expectedToFail: true, - config: extenderNegativeWeight, - errorString: "must have a positive weight applied to it", + config: extenderNegativeWeight, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "extenders[0].weight", + }, + }, }, "extender-duplicate-managed-resources": { - expectedToFail: true, - config: extenderDuplicateManagedResource, - errorString: "duplicate extender managed resource name", + config: extenderDuplicateManagedResource, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "extenders[0].managedResources[1].name", + }, + }, }, "extender-duplicate-bind": { - expectedToFail: true, - config: extenderDuplicateBind, - errorString: "only one extender can implement bind", + config: extenderDuplicateBind, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "extenders", + }, + }, }, "invalid-node-percentage": { - expectedToFail: true, - config: invalidNodePercentage, - errorString: "not in valid range [0, 100]", + config: invalidNodePercentage, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[0].pluginConfig[0].args.minCandidateNodesPercentage", + }, + }, }, "invalid-plugin-args": { - expectedToFail: true, - config: invalidPluginArgs, - errorString: "has to match plugin args", + config: invalidPluginArgs, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[0].pluginConfig[0].args", + }, + }, }, "duplicated-plugin-config": { - expectedToFail: true, - config: duplicatedPluginConfig, - errorString: "Duplicate value: \"config\"", + config: duplicatedPluginConfig, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeDuplicate, + Field: `profiles[0].pluginConfig[1]`, + }, + }, }, "mismatch-queue-sort": { - expectedToFail: true, - config: mismatchQueueSort, - errorString: "has to match for all profiles", + config: mismatchQueueSort, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].plugins.queueSort", + }, + }, }, "valid-plugins": { - expectedToFail: false, - config: validPlugins, + config: validPlugins, }, } 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()) + diff := cmp.Diff(scenario.wantErrs.ToAggregate(), errs, ignoreBadValueDetail) + if diff != "" { + t.Errorf("KubeSchedulerConfiguration returned err (-want,+got):\n%s", diff) } }) } @@ -513,8 +585,8 @@ func TestValidateKubeSchedulerConfigurationV1beta3(t *testing.T) { extenderDuplicateManagedResource := validConfig.DeepCopy() extenderDuplicateManagedResource.Extenders[0].ManagedResources = []config.ExtenderManagedResource{ - {Name: "foo", IgnoredByScheduler: false}, - {Name: "foo", IgnoredByScheduler: false}, + {Name: "example.com/foo", IgnoredByScheduler: false}, + {Name: "example.com/foo", IgnoredByScheduler: false}, } extenderDuplicateBind := validConfig.DeepCopy() @@ -529,132 +601,203 @@ func TestValidateKubeSchedulerConfigurationV1beta3(t *testing.T) { 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 - config *config.KubeSchedulerConfiguration - errorString string + config *config.KubeSchedulerConfiguration + wantErrs field.ErrorList }{ "good": { - expectedToFail: false, - config: validConfig, + config: validConfig, }, "bad-parallelism-invalid-value": { - expectedToFail: true, - config: invalidParallelismValue, - errorString: "should be an integer value greater than zero", + config: invalidParallelismValue, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "parallelism", + }, + }, }, "bad-resource-name-not-set": { - expectedToFail: true, - config: resourceNameNotSet, - errorString: "resourceName is required", + config: resourceNameNotSet, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "leaderElection.resourceName", + }, + }, }, "bad-resource-namespace-not-set": { - expectedToFail: true, - config: resourceNamespaceNotSet, - errorString: "resourceNamespace is required", + config: resourceNamespaceNotSet, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "leaderElection.resourceNamespace", + }, + }, }, "non-empty-metrics-bind-addr": { - expectedToFail: true, - config: metricsBindAddrInvalid, - errorString: "must be empty or with an explicit 0 port", + config: metricsBindAddrInvalid, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "metricsBindAddress", + }, + }, }, "non-empty-healthz-bind-addr": { - expectedToFail: true, - config: healthzBindAddrInvalid, - errorString: "must be empty or with an explicit 0 port", + config: healthzBindAddrInvalid, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "healthzBindAddress", + }, + }, }, "bad-percentage-of-nodes-to-score": { - expectedToFail: true, - config: percentageOfNodesToScore101, - errorString: "not in valid range [0-100]", + config: percentageOfNodesToScore101, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "percentageOfNodesToScore", + }, + }, }, "negative-percentage-of-nodes-to-score": { - expectedToFail: true, - config: percentageOfNodesToScoreNegative, - errorString: "not in valid range [0-100]", + config: percentageOfNodesToScoreNegative, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].percentageOfNodesToScore", + }, + }, }, "scheduler-name-not-set": { - expectedToFail: true, - config: schedulerNameNotSet, - errorString: "Required value", + config: schedulerNameNotSet, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeRequired, + Field: "profiles[1].schedulerName", + }, + }, }, "repeated-scheduler-name": { - expectedToFail: true, - config: repeatedSchedulerName, - errorString: "Duplicate value", + config: repeatedSchedulerName, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeDuplicate, + Field: "profiles[1].schedulerName", + }, + }, }, "greater-than-100-profile-percentage-of-nodes-to-score": { - expectedToFail: true, - config: profilePercentageOfNodesToScore101, - errorString: "not in valid range [0-100]", + config: profilePercentageOfNodesToScore101, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].percentageOfNodesToScore", + }, + }, }, "negative-100-profile-percentage-of-nodes-to-score": { - expectedToFail: true, - config: profilePercentageOfNodesToScoreNegative, - errorString: "not in valid range [0-100]", + config: profilePercentageOfNodesToScoreNegative, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].percentageOfNodesToScore", + }, + }, }, "different-queue-sort": { - expectedToFail: true, - config: differentQueueSort, - errorString: "has to match for all profiles", + config: differentQueueSort, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].plugins.queueSort", + }, + }, }, "one-empty-queue-sort": { - expectedToFail: true, - config: oneEmptyQueueSort, - errorString: "has to match for all profiles", + config: oneEmptyQueueSort, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].plugins.queueSort", + }, + }, }, "extender-negative-weight": { - expectedToFail: true, - config: extenderNegativeWeight, - errorString: "must have a positive weight applied to it", + config: extenderNegativeWeight, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "extenders[0].weight", + }, + }, }, "extender-duplicate-managed-resources": { - expectedToFail: true, - config: extenderDuplicateManagedResource, - errorString: "duplicate extender managed resource name", + config: extenderDuplicateManagedResource, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "extenders[0].managedResources[1].name", + }, + }, }, "extender-duplicate-bind": { - expectedToFail: true, - config: extenderDuplicateBind, - errorString: "only one extender can implement bind", + config: extenderDuplicateBind, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "extenders", + }, + }, }, "invalid-node-percentage": { - expectedToFail: true, - config: invalidNodePercentage, - errorString: "not in valid range [0, 100]", + config: invalidNodePercentage, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[0].pluginConfig[0].args.minCandidateNodesPercentage", + }, + }, }, "invalid-plugin-args": { - expectedToFail: true, - config: invalidPluginArgs, - errorString: "has to match plugin args", + config: invalidPluginArgs, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[0].pluginConfig[0].args", + }, + }, }, "duplicated-plugin-config": { - expectedToFail: true, - config: duplicatedPluginConfig, - errorString: "Duplicate value: \"config\"", + config: duplicatedPluginConfig, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeDuplicate, + Field: "profiles[0].pluginConfig[1]", + }, + }, }, "mismatch-queue-sort": { - expectedToFail: true, - config: mismatchQueueSort, - errorString: "has to match for all profiles", + config: mismatchQueueSort, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].plugins.queueSort", + }, + }, }, "valid-plugins": { - expectedToFail: false, - config: validPlugins, + config: validPlugins, }, } 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()) + diff := cmp.Diff(scenario.wantErrs.ToAggregate(), errs, ignoreBadValueDetail) + if diff != "" { + t.Errorf("KubeSchedulerConfiguration returned err (-want,+got):\n%s", diff) } }) } @@ -829,8 +972,8 @@ func TestValidateKubeSchedulerConfigurationV1(t *testing.T) { extenderDuplicateManagedResource := validConfig.DeepCopy() extenderDuplicateManagedResource.Extenders[0].ManagedResources = []config.ExtenderManagedResource{ - {Name: "foo", IgnoredByScheduler: false}, - {Name: "foo", IgnoredByScheduler: false}, + {Name: "example.com/foo", IgnoredByScheduler: false}, + {Name: "example.com/foo", IgnoredByScheduler: false}, } extenderDuplicateBind := validConfig.DeepCopy() @@ -848,137 +991,212 @@ func TestValidateKubeSchedulerConfigurationV1(t *testing.T) { 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 + config *config.KubeSchedulerConfiguration + wantErrs field.ErrorList }{ "good": { - expectedToFail: false, - config: validConfig, + config: validConfig, }, "bad-parallelism-invalid-value": { - expectedToFail: true, - config: invalidParallelismValue, - errorString: "should be an integer value greater than zero", + config: invalidParallelismValue, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "parallelism", + }, + }, }, "bad-resource-name-not-set": { - expectedToFail: true, - config: resourceNameNotSet, - errorString: "resourceName is required", + config: resourceNameNotSet, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "leaderElection.resourceName", + }, + }, }, "bad-resource-namespace-not-set": { - expectedToFail: true, - config: resourceNamespaceNotSet, - errorString: "resourceNamespace is required", + config: resourceNamespaceNotSet, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "leaderElection.resourceNamespace", + }, + }, }, "non-empty-metrics-bind-addr": { - expectedToFail: true, - config: metricsBindAddrInvalid, - errorString: "must be empty or with an explicit 0 port", + config: metricsBindAddrInvalid, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "metricsBindAddress", + }, + }, }, "non-empty-healthz-bind-addr": { - expectedToFail: true, - config: healthzBindAddrInvalid, - errorString: "must be empty or with an explicit 0 port", + config: healthzBindAddrInvalid, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "healthzBindAddress", + }, + }, }, "bad-percentage-of-nodes-to-score": { - expectedToFail: true, - config: percentageOfNodesToScore101, - errorString: "not in valid range [0-100]", + config: percentageOfNodesToScore101, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "percentageOfNodesToScore", + }, + }, }, "negative-percentage-of-nodes-to-score": { - expectedToFail: true, - config: percentageOfNodesToScoreNegative, - errorString: "not in valid range [0-100]", + config: percentageOfNodesToScoreNegative, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "percentageOfNodesToScore", + }, + }, }, "scheduler-name-not-set": { - expectedToFail: true, - config: schedulerNameNotSet, - errorString: "Required value", + config: schedulerNameNotSet, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeRequired, + Field: "profiles[1].schedulerName", + }, + }, }, "repeated-scheduler-name": { - expectedToFail: true, - config: repeatedSchedulerName, - errorString: "Duplicate value", + config: repeatedSchedulerName, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeDuplicate, + Field: "profiles[1].schedulerName", + }, + }, }, "greater-than-100-profile-percentage-of-nodes-to-score": { - expectedToFail: true, - config: profilePercentageOfNodesToScore101, - errorString: "not in valid range [0-100]", + config: profilePercentageOfNodesToScore101, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].percentageOfNodesToScore", + }, + }, }, "negative-profile-percentage-of-nodes-to-score": { - expectedToFail: true, - config: profilePercentageOfNodesToScoreNegative, - errorString: "not in valid range [0-100]", + config: profilePercentageOfNodesToScoreNegative, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].percentageOfNodesToScore", + }, + }, }, "different-queue-sort": { - expectedToFail: true, - config: differentQueueSort, - errorString: "has to match for all profiles", + config: differentQueueSort, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].plugins.queueSort", + }, + }, }, "one-empty-queue-sort": { - expectedToFail: true, - config: oneEmptyQueueSort, - errorString: "has to match for all profiles", + config: oneEmptyQueueSort, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].plugins.queueSort", + }, + }, }, "extender-negative-weight": { - expectedToFail: true, - config: extenderNegativeWeight, - errorString: "must have a positive weight applied to it", + config: extenderNegativeWeight, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "extenders[0].weight", + }, + }, }, "extender-duplicate-managed-resources": { - expectedToFail: true, - config: extenderDuplicateManagedResource, - errorString: "duplicate extender managed resource name", + config: extenderDuplicateManagedResource, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "extenders[0].managedResources[1].name", + }, + }, }, "extender-duplicate-bind": { - expectedToFail: true, - config: extenderDuplicateBind, - errorString: "only one extender can implement bind", + config: extenderDuplicateBind, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "extenders", + }, + }, }, "invalid-node-percentage": { - expectedToFail: true, - config: invalidNodePercentage, - errorString: "not in valid range [0, 100]", + config: invalidNodePercentage, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[0].pluginConfig[0].args.minCandidateNodesPercentage", + }, + }, }, "invalid-plugin-args": { - expectedToFail: true, - config: invalidPluginArgs, - errorString: "has to match plugin args", + config: invalidPluginArgs, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[0].pluginConfig[0].args", + }, + }, }, "duplicated-plugin-config": { - expectedToFail: true, - config: duplicatedPluginConfig, - errorString: "Duplicate value: \"config\"", + config: duplicatedPluginConfig, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeDuplicate, + Field: "profiles[0].pluginConfig[1]", + }, + }, }, "mismatch-queue-sort": { - expectedToFail: true, - config: mismatchQueueSort, - errorString: "has to match for all profiles", - }, - "valid-plugins": { - expectedToFail: false, - config: validPlugins, + config: mismatchQueueSort, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[1].plugins.queueSort", + }, + }, }, "invalid-plugins": { - expectedToFail: true, - config: invalidPlugins, - errorString: "\"SelectorSpread\": was invalid", + config: invalidPlugins, + wantErrs: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "profiles[0].plugins.score.enabled[0]", + }, + }, + }, + "valid-plugins": { + config: validPlugins, }, } 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()) + diff := cmp.Diff(scenario.wantErrs.ToAggregate(), errs, ignoreBadValueDetail) + if diff != "" { + t.Errorf("KubeSchedulerConfiguration returned err (-want,+got):\n%s", diff) } }) }