diff --git a/cmd/kube-scheduler/app/server_test.go b/cmd/kube-scheduler/app/server_test.go index 6f3e15d0630..116dae85743 100644 --- a/cmd/kube-scheduler/app/server_test.go +++ b/cmd/kube-scheduler/app/server_test.go @@ -260,6 +260,7 @@ leaderElection: {Name: "NodePorts"}, } plugins.PreScore.Enabled = []config.Plugin{ + {Name: "VolumeBinding"}, {Name: "NodeResourcesFit"}, {Name: "InterPodAffinity"}, {Name: "TaintToleration"}, diff --git a/pkg/scheduler/apis/config/testing/defaults/defaults.go b/pkg/scheduler/apis/config/testing/defaults/defaults.go index d3d29011f68..34d4a51e814 100644 --- a/pkg/scheduler/apis/config/testing/defaults/defaults.go +++ b/pkg/scheduler/apis/config/testing/defaults/defaults.go @@ -107,6 +107,7 @@ var ExpandedPluginsV1 = &config.Plugins{ {Name: names.TaintToleration}, {Name: names.NodeAffinity}, {Name: names.NodeResourcesFit}, + {Name: names.VolumeBinding}, {Name: names.PodTopologySpread}, {Name: names.InterPodAffinity}, {Name: names.NodeResourcesBalancedAllocation}, diff --git a/pkg/scheduler/framework/plugins/volumebinding/volume_binding.go b/pkg/scheduler/framework/plugins/volumebinding/volume_binding.go index a8f3a596d17..050003351ae 100644 --- a/pkg/scheduler/framework/plugins/volumebinding/volume_binding.go +++ b/pkg/scheduler/framework/plugins/volumebinding/volume_binding.go @@ -53,6 +53,9 @@ type stateData struct { // it's initialized in the PreFilter phase podVolumesByNode map[string]*PodVolumes podVolumeClaims *PodVolumeClaims + // hasStaticBindings declares whether the pod contains one or more StaticBinding. + // If not, vloumeBinding will skip score extension point. + hasStaticBindings bool sync.Mutex } @@ -74,6 +77,7 @@ var _ framework.PreFilterPlugin = &VolumeBinding{} var _ framework.FilterPlugin = &VolumeBinding{} var _ framework.ReservePlugin = &VolumeBinding{} var _ framework.PreBindPlugin = &VolumeBinding{} +var _ framework.PreScorePlugin = &VolumeBinding{} var _ framework.ScorePlugin = &VolumeBinding{} var _ framework.EnqueueExtensions = &VolumeBinding{} @@ -258,10 +262,26 @@ func (pl *VolumeBinding) Filter(ctx context.Context, cs *framework.CycleState, p // multiple goroutines call `Filter` on different nodes simultaneously and the `CycleState` may be duplicated, so we must use a local lock here state.Lock() state.podVolumesByNode[node.Name] = podVolumes + state.hasStaticBindings = state.hasStaticBindings || (podVolumes != nil && len(podVolumes.StaticBindings) > 0) state.Unlock() return nil } +// PreScore invoked at the preScore extension point. It checks whether volumeBinding can skip Score +func (pl *VolumeBinding) PreScore(ctx context.Context, cs *framework.CycleState, pod *v1.Pod, nodes []*v1.Node) *framework.Status { + if pl.scorer == nil { + return framework.NewStatus(framework.Skip) + } + state, err := getStateData(cs) + if err != nil { + return framework.AsStatus(err) + } + if state.hasStaticBindings { + return nil + } + return framework.NewStatus(framework.Skip) +} + // Score invoked at the score extension point. func (pl *VolumeBinding) Score(ctx context.Context, cs *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) { if pl.scorer == nil { diff --git a/pkg/scheduler/framework/plugins/volumebinding/volume_binding_test.go b/pkg/scheduler/framework/plugins/volumebinding/volume_binding_test.go index 96dc2096d28..ad2d67473ef 100644 --- a/pkg/scheduler/framework/plugins/volumebinding/volume_binding_test.go +++ b/pkg/scheduler/framework/plugins/volumebinding/volume_binding_test.go @@ -85,6 +85,7 @@ func TestVolumeBinding(t *testing.T) { wantStateAfterPreFilter *stateData wantFilterStatus []*framework.Status wantScores []int64 + wantPreScoreStatus *framework.Status }{ { name: "pod has not pvcs", @@ -96,9 +97,7 @@ func TestVolumeBinding(t *testing.T) { wantFilterStatus: []*framework.Status{ nil, }, - wantScores: []int64{ - 0, - }, + wantPreScoreStatus: framework.NewStatus(framework.Skip), }, { name: "all bound", @@ -125,9 +124,7 @@ func TestVolumeBinding(t *testing.T) { wantFilterStatus: []*framework.Status{ nil, }, - wantScores: []int64{ - 0, - }, + wantPreScoreStatus: framework.NewStatus(framework.Skip), }, { name: "all bound with local volumes", @@ -164,9 +161,7 @@ func TestVolumeBinding(t *testing.T) { wantFilterStatus: []*framework.Status{ nil, }, - wantScores: []int64{ - 0, - }, + wantPreScoreStatus: framework.NewStatus(framework.Skip), }, { name: "PVC does not exist", @@ -239,9 +234,7 @@ func TestVolumeBinding(t *testing.T) { wantFilterStatus: []*framework.Status{ framework.NewStatus(framework.UnschedulableAndUnresolvable, string(ErrReasonBindConflict)), }, - wantScores: []int64{ - 0, - }, + wantPreScoreStatus: framework.NewStatus(framework.Skip), }, { name: "bound and unbound unsatisfied", @@ -279,9 +272,7 @@ func TestVolumeBinding(t *testing.T) { wantFilterStatus: []*framework.Status{ framework.NewStatus(framework.UnschedulableAndUnresolvable, string(ErrReasonNodeConflict), string(ErrReasonBindConflict)), }, - wantScores: []int64{ - 0, - }, + wantPreScoreStatus: framework.NewStatus(framework.Skip), }, { name: "pvc not found", @@ -320,9 +311,7 @@ func TestVolumeBinding(t *testing.T) { wantFilterStatus: []*framework.Status{ framework.NewStatus(framework.UnschedulableAndUnresolvable, `node(s) unavailable due to one or more pvc(s) bound to non-existent pv(s)`), }, - wantScores: []int64{ - 0, - }, + wantPreScoreStatus: framework.NewStatus(framework.Skip), }, { name: "pv not found claim lost", @@ -878,6 +867,15 @@ func TestVolumeBinding(t *testing.T) { assert.Equal(t, item.wantFilterStatus[i], gotStatus) } + t.Logf("Verify: call PreScore and check status") + gotPreScoreStatus := p.PreScore(ctx, state, item.pod, item.nodes) + if diff := cmp.Diff(item.wantPreScoreStatus, gotPreScoreStatus); diff != "" { + t.Errorf("state got after prescore does not match (-want,+got):\n%s", diff) + } + if !gotPreScoreStatus.IsSuccess() { + return + } + t.Logf("Verify: Score") for i, node := range item.nodes { score, status := p.Score(ctx, state, item.pod, node.Name)