Skip to content

Commit 00a12c7

Browse files
author
draveness
committed
feat: implement node unschedulable as a filter plugin
1 parent 0f7873a commit 00a12c7

File tree

7 files changed

+217
-11
lines changed

7 files changed

+217
-11
lines changed

pkg/scheduler/api/compatibility/compatibility_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
6161
}`,
6262
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
6363
"FilterPlugin": {
64+
{Name: "NodeUnschedulable"},
6465
{Name: "NodeResources"},
6566
{Name: "NodeName"},
6667
{Name: "NodePorts"},
@@ -102,6 +103,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
102103
),
103104
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
104105
"FilterPlugin": {
106+
{Name: "NodeUnschedulable"},
105107
{Name: "NodeAffinity"},
106108
{Name: "NodeResources"},
107109
{Name: "VolumeRestrictions"},
@@ -147,6 +149,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
147149
),
148150
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
149151
"FilterPlugin": {
152+
{Name: "NodeUnschedulable"},
150153
{Name: "NodeName"},
151154
{Name: "NodePorts"},
152155
{Name: "NodeAffinity"},
@@ -203,6 +206,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
203206
),
204207
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
205208
"FilterPlugin": {
209+
{Name: "NodeUnschedulable"},
206210
{Name: "NodeName"},
207211
{Name: "NodePorts"},
208212
{Name: "NodeAffinity"},
@@ -267,6 +271,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
267271
),
268272
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
269273
"FilterPlugin": {
274+
{Name: "NodeUnschedulable"},
270275
{Name: "NodeName"},
271276
{Name: "NodePorts"},
272277
{Name: "NodeAffinity"},
@@ -338,6 +343,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
338343
),
339344
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
340345
"FilterPlugin": {
346+
{Name: "NodeUnschedulable"},
341347
{Name: "NodeName"},
342348
{Name: "NodePorts"},
343349
{Name: "NodeAffinity"},
@@ -419,6 +425,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
419425
),
420426
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
421427
"FilterPlugin": {
428+
{Name: "NodeUnschedulable"},
422429
{Name: "NodeName"},
423430
{Name: "NodePorts"},
424431
{Name: "NodeAffinity"},
@@ -513,6 +520,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
513520
),
514521
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
515522
"FilterPlugin": {
523+
{Name: "NodeUnschedulable"},
516524
{Name: "NodeName"},
517525
{Name: "NodePorts"},
518526
{Name: "NodeAffinity"},
@@ -608,6 +616,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
608616
),
609617
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
610618
"FilterPlugin": {
619+
{Name: "NodeUnschedulable"},
611620
{Name: "NodeName"},
612621
{Name: "NodePorts"},
613622
{Name: "NodeAffinity"},
@@ -709,6 +718,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
709718
),
710719
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
711720
"FilterPlugin": {
721+
{Name: "NodeUnschedulable"},
712722
{Name: "NodeName"},
713723
{Name: "NodePorts"},
714724
{Name: "NodeAffinity"},
@@ -823,6 +833,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
823833
),
824834
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
825835
"FilterPlugin": {
836+
{Name: "NodeUnschedulable"},
826837
{Name: "NodeName"},
827838
{Name: "NodePorts"},
828839
{Name: "NodeAffinity"},
@@ -938,6 +949,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
938949
),
939950
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
940951
"FilterPlugin": {
952+
{Name: "NodeUnschedulable"},
941953
{Name: "NodeName"},
942954
{Name: "NodePorts"},
943955
{Name: "NodeAffinity"},
@@ -1054,6 +1066,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
10541066
),
10551067
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
10561068
"FilterPlugin": {
1069+
{Name: "NodeUnschedulable"},
10571070
{Name: "NodeName"},
10581071
{Name: "NodePorts"},
10591072
{Name: "NodeAffinity"},
@@ -1174,6 +1187,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
11741187
),
11751188
wantPlugins: map[string][]kubeschedulerconfig.Plugin{
11761189
"FilterPlugin": {
1190+
{Name: "NodeUnschedulable"},
11771191
{Name: "NodeName"},
11781192
{Name: "NodePorts"},
11791193
{Name: "NodeAffinity"},
@@ -1211,9 +1225,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
12111225
registeredPriorities := sets.NewString(scheduler.ListRegisteredPriorityFunctions()...)
12121226
seenPredicates := sets.NewString()
12131227
seenPriorities := sets.NewString()
1214-
mandatoryPredicates := sets.NewString("CheckNodeUnschedulable")
1228+
mandatoryPredicates := sets.NewString()
12151229
generalPredicateFilters := []string{"NodeResources", "NodeName", "NodePorts", "NodeAffinity"}
12161230
filterToPredicateMap := map[string]string{
1231+
"NodeUnschedulable": "CheckNodeUnschedulable",
12171232
"TaintToleration": "PodToleratesNodeTaints",
12181233
"NodeName": "HostName",
12191234
"NodePorts": "PodFitsHostPorts",

pkg/scheduler/framework/plugins/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ go_library(
1616
"//pkg/scheduler/framework/plugins/nodeports:go_default_library",
1717
"//pkg/scheduler/framework/plugins/nodepreferavoidpods:go_default_library",
1818
"//pkg/scheduler/framework/plugins/noderesources:go_default_library",
19+
"//pkg/scheduler/framework/plugins/nodeunschedulable:go_default_library",
1920
"//pkg/scheduler/framework/plugins/nodevolumelimits:go_default_library",
2021
"//pkg/scheduler/framework/plugins/podtopologyspread:go_default_library",
2122
"//pkg/scheduler/framework/plugins/tainttoleration:go_default_library",
@@ -49,6 +50,7 @@ filegroup(
4950
"//pkg/scheduler/framework/plugins/nodeports:all-srcs",
5051
"//pkg/scheduler/framework/plugins/nodepreferavoidpods:all-srcs",
5152
"//pkg/scheduler/framework/plugins/noderesources:all-srcs",
53+
"//pkg/scheduler/framework/plugins/nodeunschedulable:all-srcs",
5254
"//pkg/scheduler/framework/plugins/nodevolumelimits:all-srcs",
5355
"//pkg/scheduler/framework/plugins/podtopologyspread:all-srcs",
5456
"//pkg/scheduler/framework/plugins/tainttoleration:all-srcs",

pkg/scheduler/framework/plugins/default_registry.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports"
3131
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodepreferavoidpods"
3232
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources"
33+
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeunschedulable"
3334
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodevolumelimits"
3435
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/podtopologyspread"
3536
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/tainttoleration"
@@ -60,6 +61,7 @@ func NewDefaultRegistry(args *RegistryArgs) framework.Registry {
6061
nodepreferavoidpods.Name: nodepreferavoidpods.New,
6162
nodeaffinity.Name: nodeaffinity.New,
6263
podtopologyspread.Name: podtopologyspread.New,
64+
nodeunschedulable.Name: nodeunschedulable.New,
6365
volumebinding.Name: func(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
6466
return volumebinding.NewFromVolumeBinder(args.VolumeBinder), nil
6567
},
@@ -131,6 +133,11 @@ func NewDefaultConfigProducerRegistry() *ConfigProducerRegistry {
131133
plugins.Filter = appendToPluginSet(plugins.Filter, nodeaffinity.Name, nil)
132134
return
133135
})
136+
registry.RegisterPredicate(predicates.CheckNodeUnschedulablePred,
137+
func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) {
138+
plugins.Filter = appendToPluginSet(plugins.Filter, nodeunschedulable.Name, nil)
139+
return
140+
})
134141
registry.RegisterPredicate(predicates.CheckVolumeBindingPred,
135142
func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) {
136143
plugins.Filter = appendToPluginSet(plugins.Filter, volumebinding.Name, nil)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["node_unschedulable.go"],
6+
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeunschedulable",
7+
visibility = ["//visibility:public"],
8+
deps = [
9+
"//pkg/scheduler/algorithm/predicates:go_default_library",
10+
"//pkg/scheduler/framework/plugins/migration:go_default_library",
11+
"//pkg/scheduler/framework/v1alpha1:go_default_library",
12+
"//pkg/scheduler/nodeinfo:go_default_library",
13+
"//staging/src/k8s.io/api/core/v1:go_default_library",
14+
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
15+
],
16+
)
17+
18+
go_test(
19+
name = "go_default_test",
20+
srcs = ["node_unschedulable_test.go"],
21+
embed = [":go_default_library"],
22+
deps = [
23+
"//pkg/scheduler/algorithm/predicates:go_default_library",
24+
"//pkg/scheduler/api:go_default_library",
25+
"//pkg/scheduler/framework/v1alpha1:go_default_library",
26+
"//pkg/scheduler/nodeinfo:go_default_library",
27+
"//staging/src/k8s.io/api/core/v1:go_default_library",
28+
],
29+
)
30+
31+
filegroup(
32+
name = "package-srcs",
33+
srcs = glob(["**"]),
34+
tags = ["automanaged"],
35+
visibility = ["//visibility:private"],
36+
)
37+
38+
filegroup(
39+
name = "all-srcs",
40+
srcs = [":package-srcs"],
41+
tags = ["automanaged"],
42+
visibility = ["//visibility:public"],
43+
)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
Copyright 2019 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 nodeunschedulable
18+
19+
import (
20+
"context"
21+
22+
v1 "k8s.io/api/core/v1"
23+
"k8s.io/apimachinery/pkg/runtime"
24+
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
25+
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/migration"
26+
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
27+
"k8s.io/kubernetes/pkg/scheduler/nodeinfo"
28+
)
29+
30+
// NodeUnschedulable is a plugin that priorities nodes according to the node annotation
31+
// "scheduler.alpha.kubernetes.io/preferAvoidPods".
32+
type NodeUnschedulable struct {
33+
}
34+
35+
var _ framework.FilterPlugin = &NodeUnschedulable{}
36+
37+
// Name is the name of the plugin used in the plugin registry and configurations.
38+
const Name = "NodeUnschedulable"
39+
40+
// Name returns name of the plugin. It is used in logs, etc.
41+
func (pl *NodeUnschedulable) Name() string {
42+
return Name
43+
}
44+
45+
// Filter invoked at the filter extension point.
46+
func (pl *NodeUnschedulable) Filter(ctx context.Context, _ *framework.CycleState, pod *v1.Pod, nodeInfo *nodeinfo.NodeInfo) *framework.Status {
47+
_, reasons, err := predicates.CheckNodeUnschedulablePredicate(pod, nil, nodeInfo)
48+
return migration.PredicateResultToFrameworkStatus(reasons, err)
49+
}
50+
51+
// New initializes a new plugin and returns it.
52+
func New(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
53+
return &NodeUnschedulable{}, nil
54+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
Copyright 2019 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 nodeunschedulable
18+
19+
import (
20+
"context"
21+
"reflect"
22+
"testing"
23+
24+
v1 "k8s.io/api/core/v1"
25+
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
26+
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
27+
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
28+
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
29+
)
30+
31+
func TestNodeUnschedulable(t *testing.T) {
32+
testCases := []struct {
33+
name string
34+
pod *v1.Pod
35+
node *v1.Node
36+
wantStatus *framework.Status
37+
}{
38+
{
39+
name: "Does not schedule pod to unschedulable node (node.Spec.Unschedulable==true)",
40+
pod: &v1.Pod{},
41+
node: &v1.Node{
42+
Spec: v1.NodeSpec{
43+
Unschedulable: true,
44+
},
45+
},
46+
wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, predicates.ErrNodeUnschedulable.GetReason()),
47+
},
48+
{
49+
name: "Schedule pod to normal node",
50+
pod: &v1.Pod{},
51+
node: &v1.Node{
52+
Spec: v1.NodeSpec{
53+
Unschedulable: false,
54+
},
55+
},
56+
},
57+
{
58+
name: "Schedule pod with toleration to unschedulable node (node.Spec.Unschedulable==true)",
59+
pod: &v1.Pod{
60+
Spec: v1.PodSpec{
61+
Tolerations: []v1.Toleration{
62+
{
63+
Key: schedulerapi.TaintNodeUnschedulable,
64+
Effect: v1.TaintEffectNoSchedule,
65+
},
66+
},
67+
},
68+
},
69+
node: &v1.Node{
70+
Spec: v1.NodeSpec{
71+
Unschedulable: true,
72+
},
73+
},
74+
},
75+
}
76+
77+
for _, test := range testCases {
78+
nodeInfo := schedulernodeinfo.NewNodeInfo()
79+
nodeInfo.SetNode(test.node)
80+
81+
p, _ := New(nil, nil)
82+
gotStatus := p.(framework.FilterPlugin).Filter(context.Background(), nil, test.pod, nodeInfo)
83+
if !reflect.DeepEqual(gotStatus, test.wantStatus) {
84+
t.Errorf("status does not match: %v, want: %v", gotStatus, test.wantStatus)
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)