Skip to content

Commit 16df00a

Browse files
authored
Merge pull request #512 from PiotrProkop/nrt-add-validation
Introduce pluginargs validation for NodeResourceTopologyMatch plugin
2 parents 9d8e6b1 + 7775051 commit 16df00a

File tree

4 files changed

+168
-0
lines changed

4 files changed

+168
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package validation
18+
19+
import (
20+
"k8s.io/apimachinery/pkg/util/sets"
21+
"k8s.io/apimachinery/pkg/util/validation/field"
22+
23+
"sigs.k8s.io/scheduler-plugins/apis/config"
24+
)
25+
26+
var validScoringStrategy = sets.NewString(
27+
string(config.MostAllocated),
28+
string(config.BalancedAllocation),
29+
string(config.LeastAllocated),
30+
string(config.LeastNUMANodes),
31+
)
32+
33+
func ValidateNodeResourceTopologyMatchArgs(path *field.Path, args *config.NodeResourceTopologyMatchArgs) error {
34+
var allErrs field.ErrorList
35+
scoringStrategyTypePath := path.Child("scoringStrategy.type")
36+
if err := validateScoringStrategyType(args.ScoringStrategy.Type, scoringStrategyTypePath); err != nil {
37+
allErrs = append(allErrs, err)
38+
}
39+
40+
return allErrs.ToAggregate()
41+
}
42+
43+
func validateScoringStrategyType(scoringStrategy config.ScoringStrategyType, path *field.Path) *field.Error {
44+
if !validScoringStrategy.Has(string(scoringStrategy)) {
45+
return field.Invalid(path, scoringStrategy, "invalid ScoringStrategyType")
46+
}
47+
return nil
48+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package validation
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
"testing"
23+
24+
"sigs.k8s.io/scheduler-plugins/apis/config"
25+
)
26+
27+
func TestValidateNodeResourceTopologyMatchArgs(t *testing.T) {
28+
testCases := []struct {
29+
args *config.NodeResourceTopologyMatchArgs
30+
expectedErr error
31+
description string
32+
}{
33+
{
34+
description: "correct config",
35+
args: &config.NodeResourceTopologyMatchArgs{
36+
ScoringStrategy: config.ScoringStrategy{
37+
Type: config.MostAllocated,
38+
},
39+
},
40+
},
41+
{
42+
description: "incorrect config, wrong ScoringStrategy type",
43+
args: &config.NodeResourceTopologyMatchArgs{
44+
ScoringStrategy: config.ScoringStrategy{
45+
Type: "not existent",
46+
},
47+
},
48+
expectedErr: fmt.Errorf("scoringStrategy.type: Invalid value:"),
49+
},
50+
}
51+
52+
for _, testCase := range testCases {
53+
t.Run(testCase.description, func(t *testing.T) {
54+
err := ValidateNodeResourceTopologyMatchArgs(nil, testCase.args)
55+
if testCase.expectedErr != nil {
56+
if err == nil {
57+
t.Errorf("expected err to equal %v not nil", testCase.expectedErr)
58+
}
59+
60+
if !strings.Contains(err.Error(), testCase.expectedErr.Error()) {
61+
t.Errorf("expected err to contain %s in error message: %s", testCase.expectedErr.Error(), err.Error())
62+
}
63+
}
64+
if testCase.expectedErr == nil && err != nil {
65+
t.Errorf("unexpected error: %v", err)
66+
}
67+
})
68+
}
69+
}

pkg/noderesourcetopology/plugin.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"k8s.io/kubernetes/pkg/scheduler/framework"
2727

2828
apiconfig "sigs.k8s.io/scheduler-plugins/apis/config"
29+
"sigs.k8s.io/scheduler-plugins/apis/config/validation"
2930
nrtcache "sigs.k8s.io/scheduler-plugins/pkg/noderesourcetopology/cache"
3031

3132
topologyapi "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/apis/topology"
@@ -120,6 +121,10 @@ func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error)
120121
return nil, fmt.Errorf("want args to be of type NodeResourceTopologyMatchArgs, got %T", args)
121122
}
122123

124+
if err := validation.ValidateNodeResourceTopologyMatchArgs(nil, tcfg); err != nil {
125+
return nil, err
126+
}
127+
123128
nrtCache, err := initNodeTopologyInformer(tcfg, handle)
124129
if err != nil {
125130
klog.ErrorS(err, "Cannot create clientset for NodeTopologyResource", "kubeConfig", handle.KubeConfig())

test/integration/noderesourcetopology_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,18 @@ import (
2727
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2828
"k8s.io/apimachinery/pkg/util/uuid"
2929
"k8s.io/apimachinery/pkg/util/wait"
30+
"k8s.io/client-go/dynamic"
31+
"k8s.io/client-go/dynamic/dynamicinformer"
3032
"k8s.io/client-go/kubernetes"
3133
clientset "k8s.io/client-go/kubernetes"
34+
"k8s.io/client-go/tools/events"
3235
"k8s.io/klog/v2"
3336
"k8s.io/kubernetes/pkg/scheduler"
3437
schedapi "k8s.io/kubernetes/pkg/scheduler/apis/config"
3538
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultbinder"
3639
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/queuesort"
3740
fwkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
41+
"k8s.io/kubernetes/pkg/scheduler/profile"
3842
st "k8s.io/kubernetes/pkg/scheduler/testing"
3943
imageutils "k8s.io/kubernetes/test/utils/image"
4044

@@ -76,6 +80,48 @@ type nrtTestEntry struct {
7680
errMsg string
7781
}
7882

83+
func TestTopologyMatchPluginValidation(t *testing.T) {
84+
cfg, err := util.NewDefaultSchedulerComponentConfig()
85+
if err != nil {
86+
t.Fatal(err)
87+
}
88+
cfg.Profiles[0].Plugins.Filter.Enabled = append(cfg.Profiles[0].Plugins.Filter.Enabled, schedapi.Plugin{Name: noderesourcetopology.Name})
89+
cfg.Profiles[0].Plugins.Score.Enabled = append(cfg.Profiles[0].Plugins.Score.Enabled, schedapi.Plugin{Name: noderesourcetopology.Name})
90+
cfg.Profiles[0].PluginConfig = append(cfg.Profiles[0].PluginConfig, schedapi.PluginConfig{
91+
Name: noderesourcetopology.Name,
92+
Args: &scheconfig.NodeResourceTopologyMatchArgs{
93+
ScoringStrategy: scheconfig.ScoringStrategy{Type: "incorrect"},
94+
},
95+
})
96+
97+
cs := kubernetes.NewForConfigOrDie(globalKubeConfig)
98+
informer := scheduler.NewInformerFactory(cs, 0)
99+
dynInformerFactory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(dynamic.NewForConfigOrDie(globalKubeConfig), 0, v1.NamespaceAll, nil)
100+
eventBroadcaster := events.NewBroadcaster(&events.EventSinkImpl{
101+
Interface: cs.EventsV1(),
102+
})
103+
done := make(chan struct{})
104+
105+
_, err = scheduler.New(
106+
cs,
107+
informer,
108+
dynInformerFactory,
109+
profile.NewRecorderFactory(eventBroadcaster),
110+
done,
111+
scheduler.WithProfiles(cfg.Profiles...),
112+
scheduler.WithFrameworkOutOfTreeRegistry(fwkruntime.Registry{noderesourcetopology.Name: noderesourcetopology.New}),
113+
)
114+
115+
if err == nil {
116+
t.Error("expected error not to be nil")
117+
}
118+
119+
errMsg := "scoringStrategy.type: Invalid value:"
120+
if !strings.Contains(err.Error(), errMsg) {
121+
t.Errorf("expected error to contain: %s in error message: %s", errMsg, err.Error())
122+
}
123+
}
124+
79125
func TestTopologyMatchPlugin(t *testing.T) {
80126
testCtx := &testContext{}
81127
testCtx.Ctx, testCtx.CancelFn = context.WithCancel(context.Background())

0 commit comments

Comments
 (0)