Skip to content

Commit 83408ac

Browse files
author
zouyee
committed
Move volumebinding predicate to its filter plugin
Signed-off-by: Zou Nengren <[email protected]>
1 parent 82c7c86 commit 83408ac

File tree

6 files changed

+187
-78
lines changed

6 files changed

+187
-78
lines changed

pkg/scheduler/algorithm/predicates/BUILD

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ go_library(
2222
"//pkg/scheduler/algorithm:go_default_library",
2323
"//pkg/scheduler/nodeinfo:go_default_library",
2424
"//pkg/scheduler/util:go_default_library",
25-
"//pkg/scheduler/volumebinder:go_default_library",
2625
"//pkg/volume/util:go_default_library",
2726
"//staging/src/k8s.io/api/core/v1:go_default_library",
2827
"//staging/src/k8s.io/api/storage/v1:go_default_library",

pkg/scheduler/algorithm/predicates/predicates.go

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import (
4141
"k8s.io/kubernetes/pkg/scheduler/algorithm"
4242
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
4343
schedutil "k8s.io/kubernetes/pkg/scheduler/util"
44-
"k8s.io/kubernetes/pkg/scheduler/volumebinder"
4544
volumeutil "k8s.io/kubernetes/pkg/volume/util"
4645
)
4746

@@ -1051,74 +1050,6 @@ func podToleratesNodeTaints(pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo, f
10511050
return false, []PredicateFailureReason{ErrTaintsTolerationsNotMatch}, nil
10521051
}
10531052

1054-
// VolumeBindingChecker contains information to check a volume binding.
1055-
type VolumeBindingChecker struct {
1056-
binder *volumebinder.VolumeBinder
1057-
}
1058-
1059-
// NewVolumeBindingPredicate evaluates if a pod can fit due to the volumes it requests,
1060-
// for both bound and unbound PVCs.
1061-
//
1062-
// For PVCs that are bound, then it checks that the corresponding PV's node affinity is
1063-
// satisfied by the given node.
1064-
//
1065-
// For PVCs that are unbound, it tries to find available PVs that can satisfy the PVC requirements
1066-
// and that the PV node affinity is satisfied by the given node.
1067-
//
1068-
// The predicate returns true if all bound PVCs have compatible PVs with the node, and if all unbound
1069-
// PVCs can be matched with an available and node-compatible PV.
1070-
func NewVolumeBindingPredicate(binder *volumebinder.VolumeBinder) FitPredicate {
1071-
c := &VolumeBindingChecker{
1072-
binder: binder,
1073-
}
1074-
return c.predicate
1075-
}
1076-
1077-
func podHasPVCs(pod *v1.Pod) bool {
1078-
for _, vol := range pod.Spec.Volumes {
1079-
if vol.PersistentVolumeClaim != nil {
1080-
return true
1081-
}
1082-
}
1083-
return false
1084-
}
1085-
1086-
func (c *VolumeBindingChecker) predicate(pod *v1.Pod, meta Metadata, nodeInfo *schedulernodeinfo.NodeInfo) (bool, []PredicateFailureReason, error) {
1087-
// If pod does not request any PVC, we don't need to do anything.
1088-
if !podHasPVCs(pod) {
1089-
return true, nil, nil
1090-
}
1091-
1092-
node := nodeInfo.Node()
1093-
if node == nil {
1094-
return false, nil, fmt.Errorf("node not found")
1095-
}
1096-
1097-
unboundSatisfied, boundSatisfied, err := c.binder.Binder.FindPodVolumes(pod, node)
1098-
if err != nil {
1099-
return false, nil, err
1100-
}
1101-
1102-
failReasons := []PredicateFailureReason{}
1103-
if !boundSatisfied {
1104-
klog.V(5).Infof("Bound PVs not satisfied for pod %v/%v, node %q", pod.Namespace, pod.Name, node.Name)
1105-
failReasons = append(failReasons, ErrVolumeNodeConflict)
1106-
}
1107-
1108-
if !unboundSatisfied {
1109-
klog.V(5).Infof("Couldn't find matching PVs for pod %v/%v, node %q", pod.Namespace, pod.Name, node.Name)
1110-
failReasons = append(failReasons, ErrVolumeBindConflict)
1111-
}
1112-
1113-
if len(failReasons) > 0 {
1114-
return false, failReasons, nil
1115-
}
1116-
1117-
// All volumes bound or matching PVs found for all unbound PVCs
1118-
klog.V(5).Infof("All PVCs found matches for pod %v/%v, node %q", pod.Namespace, pod.Name, node.Name)
1119-
return true, nil, nil
1120-
}
1121-
11221053
// EvenPodsSpreadPredicate is the legacy function using old path of metadata.
11231054
// DEPRECATED
11241055
func EvenPodsSpreadPredicate(pod *v1.Pod, meta Metadata, nodeInfo *schedulernodeinfo.NodeInfo) (bool, []PredicateFailureReason, error) {

pkg/scheduler/algorithmprovider/defaults/register_predicates.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func init() {
133133
scheduler.RegisterFitPredicateFactory(
134134
predicates.CheckVolumeBindingPred,
135135
func(args scheduler.AlgorithmFactoryArgs) predicates.FitPredicate {
136-
return predicates.NewVolumeBindingPredicate(args.VolumeBinder)
136+
return nil
137137
},
138138
)
139139
}

pkg/scheduler/framework/plugins/volumebinding/BUILD

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
load("@io_bazel_rules_go//go:def.bzl", "go_library")
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
22

33
go_library(
44
name = "go_default_library",
@@ -7,7 +7,6 @@ go_library(
77
visibility = ["//visibility:public"],
88
deps = [
99
"//pkg/scheduler/algorithm/predicates:go_default_library",
10-
"//pkg/scheduler/framework/plugins/migration:go_default_library",
1110
"//pkg/scheduler/framework/v1alpha1:go_default_library",
1211
"//pkg/scheduler/nodeinfo:go_default_library",
1312
"//pkg/scheduler/volumebinder:go_default_library",
@@ -28,3 +27,17 @@ filegroup(
2827
tags = ["automanaged"],
2928
visibility = ["//visibility:public"],
3029
)
30+
31+
go_test(
32+
name = "go_default_test",
33+
srcs = ["volume_binding_test.go"],
34+
embed = [":go_default_library"],
35+
deps = [
36+
"//pkg/controller/volume/scheduling:go_default_library",
37+
"//pkg/scheduler/algorithm/predicates:go_default_library",
38+
"//pkg/scheduler/framework/v1alpha1:go_default_library",
39+
"//pkg/scheduler/nodeinfo:go_default_library",
40+
"//pkg/scheduler/volumebinder:go_default_library",
41+
"//staging/src/k8s.io/api/core/v1:go_default_library",
42+
],
43+
)

pkg/scheduler/framework/plugins/volumebinding/volume_binding.go

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ import (
2121

2222
v1 "k8s.io/api/core/v1"
2323
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
24-
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/migration"
2524
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
2625
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
2726
"k8s.io/kubernetes/pkg/scheduler/volumebinder"
2827
)
2928

3029
// VolumeBinding is a plugin that binds pod volumes in scheduling.
3130
type VolumeBinding struct {
32-
predicate predicates.FitPredicate
31+
binder *volumebinder.VolumeBinder
3332
}
3433

3534
var _ framework.FilterPlugin = &VolumeBinding{}
@@ -42,15 +41,59 @@ func (pl *VolumeBinding) Name() string {
4241
return Name
4342
}
4443

44+
func podHasPVCs(pod *v1.Pod) bool {
45+
for _, vol := range pod.Spec.Volumes {
46+
if vol.PersistentVolumeClaim != nil {
47+
return true
48+
}
49+
}
50+
return false
51+
}
52+
4553
// Filter invoked at the filter extension point.
54+
// It evaluates if a pod can fit due to the volumes it requests,
55+
// for both bound and unbound PVCs.
56+
//
57+
// For PVCs that are bound, then it checks that the corresponding PV's node affinity is
58+
// satisfied by the given node.
59+
//
60+
// For PVCs that are unbound, it tries to find available PVs that can satisfy the PVC requirements
61+
// and that the PV node affinity is satisfied by the given node.
62+
//
63+
// The predicate returns true if all bound PVCs have compatible PVs with the node, and if all unbound
64+
// PVCs can be matched with an available and node-compatible PV.
4665
func (pl *VolumeBinding) Filter(ctx context.Context, cs *framework.CycleState, pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *framework.Status {
47-
_, reasons, err := pl.predicate(pod, nil, nodeInfo)
48-
return migration.PredicateResultToFrameworkStatus(reasons, err)
66+
node := nodeInfo.Node()
67+
if node == nil {
68+
return framework.NewStatus(framework.Error, "node not found")
69+
}
70+
// If pod does not request any PVC, we don't need to do anything.
71+
if !podHasPVCs(pod) {
72+
return nil
73+
}
74+
75+
unboundSatisfied, boundSatisfied, err := pl.binder.Binder.FindPodVolumes(pod, node)
76+
77+
if err != nil {
78+
return framework.NewStatus(framework.Error, err.Error())
79+
}
80+
81+
if !boundSatisfied || !unboundSatisfied {
82+
status := framework.NewStatus(framework.UnschedulableAndUnresolvable)
83+
if !boundSatisfied {
84+
status.AppendReason(predicates.ErrVolumeNodeConflict.GetReason())
85+
}
86+
if !unboundSatisfied {
87+
status.AppendReason(predicates.ErrVolumeBindConflict.GetReason())
88+
}
89+
return status
90+
}
91+
return nil
4992
}
5093

5194
// NewFromVolumeBinder initializes a new plugin with volume binder and returns it.
5295
func NewFromVolumeBinder(volumeBinder *volumebinder.VolumeBinder) framework.Plugin {
5396
return &VolumeBinding{
54-
predicate: predicates.NewVolumeBindingPredicate(volumeBinder),
97+
binder: volumeBinder,
5598
}
5699
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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 volumebinding
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"reflect"
23+
"testing"
24+
25+
v1 "k8s.io/api/core/v1"
26+
volumescheduling "k8s.io/kubernetes/pkg/controller/volume/scheduling"
27+
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
28+
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
29+
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
30+
"k8s.io/kubernetes/pkg/scheduler/volumebinder"
31+
)
32+
33+
func TestVolumeBinding(t *testing.T) {
34+
findErr := fmt.Errorf("find err")
35+
volState := v1.PodSpec{
36+
Volumes: []v1.Volume{
37+
{
38+
VolumeSource: v1.VolumeSource{
39+
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{},
40+
},
41+
},
42+
},
43+
}
44+
table := []struct {
45+
name string
46+
pod *v1.Pod
47+
node *v1.Node
48+
volumeBinderConfig *volumescheduling.FakeVolumeBinderConfig
49+
wantStatus *framework.Status
50+
}{
51+
{
52+
name: "nothing",
53+
pod: &v1.Pod{},
54+
node: &v1.Node{},
55+
wantStatus: nil,
56+
},
57+
{
58+
name: "all bound",
59+
pod: &v1.Pod{Spec: volState},
60+
node: &v1.Node{},
61+
volumeBinderConfig: &volumescheduling.FakeVolumeBinderConfig{
62+
AllBound: true,
63+
FindUnboundSatsified: true,
64+
FindBoundSatsified: true,
65+
},
66+
wantStatus: nil,
67+
},
68+
{
69+
name: "unbound/no matches",
70+
pod: &v1.Pod{Spec: volState},
71+
node: &v1.Node{},
72+
volumeBinderConfig: &volumescheduling.FakeVolumeBinderConfig{
73+
FindUnboundSatsified: false,
74+
FindBoundSatsified: true,
75+
},
76+
wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, predicates.ErrVolumeBindConflict.GetReason()),
77+
},
78+
{
79+
name: "bound and unbound unsatisfied",
80+
pod: &v1.Pod{Spec: volState},
81+
node: &v1.Node{},
82+
volumeBinderConfig: &volumescheduling.FakeVolumeBinderConfig{
83+
FindUnboundSatsified: false,
84+
FindBoundSatsified: false,
85+
},
86+
wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, predicates.ErrVolumeNodeConflict.GetReason(),
87+
predicates.ErrVolumeBindConflict.GetReason()),
88+
},
89+
{
90+
name: "unbound/found matches/bind succeeds",
91+
pod: &v1.Pod{Spec: volState},
92+
node: &v1.Node{},
93+
volumeBinderConfig: &volumescheduling.FakeVolumeBinderConfig{
94+
FindUnboundSatsified: true,
95+
FindBoundSatsified: true,
96+
},
97+
wantStatus: nil,
98+
},
99+
{
100+
name: "predicate error",
101+
pod: &v1.Pod{Spec: volState},
102+
node: &v1.Node{},
103+
volumeBinderConfig: &volumescheduling.FakeVolumeBinderConfig{
104+
FindErr: findErr,
105+
},
106+
wantStatus: framework.NewStatus(framework.Error, findErr.Error()),
107+
},
108+
}
109+
110+
for _, item := range table {
111+
t.Run(item.name, func(t *testing.T) {
112+
nodeInfo := schedulernodeinfo.NewNodeInfo()
113+
nodeInfo.SetNode(item.node)
114+
fakeVolumeBinder := volumebinder.NewFakeVolumeBinder(item.volumeBinderConfig)
115+
p := NewFromVolumeBinder(fakeVolumeBinder)
116+
gotStatus := p.(framework.FilterPlugin).Filter(context.Background(), nil, item.pod, nodeInfo)
117+
if !reflect.DeepEqual(gotStatus, item.wantStatus) {
118+
t.Errorf("status does not match: %v, want: %v", gotStatus, item.wantStatus)
119+
}
120+
121+
})
122+
}
123+
}

0 commit comments

Comments
 (0)