Merge pull request #115768 from AxeZhan/volumebinding

feature(volumebinding): Implement PreScore for VolumeBinding plugin to skip score
This commit is contained in:
Kubernetes Prow Robot 2023-12-13 21:24:43 +01:00 committed by GitHub
commit c02f5bc0f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 18 deletions

View File

@ -260,6 +260,7 @@ leaderElection:
{Name: "NodePorts"},
}
plugins.PreScore.Enabled = []config.Plugin{
{Name: "VolumeBinding"},
{Name: "NodeResourcesFit"},
{Name: "InterPodAffinity"},
{Name: "TaintToleration"},

View File

@ -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},

View File

@ -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 {

View File

@ -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)