Merge pull request #114898 from AxeZhan/volumerestrictions

feature(volume_restrictions): return Skip in PreFilter
This commit is contained in:
Kubernetes Prow Robot 2023-04-11 15:35:04 -07:00 committed by GitHub
commit e77ca49022
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 220 additions and 61 deletions

View File

@ -154,11 +154,27 @@ func haveOverlap(a1, a2 []string) bool {
return false return false
} }
// return true if there are conflict checking targets.
func needsRestrictionsCheck(v v1.Volume) bool {
return v.GCEPersistentDisk != nil || v.AWSElasticBlockStore != nil || v.RBD != nil || v.ISCSI != nil
}
// PreFilter computes and stores cycleState containing details for enforcing ReadWriteOncePod. // PreFilter computes and stores cycleState containing details for enforcing ReadWriteOncePod.
func (pl *VolumeRestrictions) PreFilter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod) (*framework.PreFilterResult, *framework.Status) { func (pl *VolumeRestrictions) PreFilter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
needsCheck := false
for i := range pod.Spec.Volumes {
if needsRestrictionsCheck(pod.Spec.Volumes[i]) {
needsCheck = true
break
}
}
if !pl.enableReadWriteOncePod { if !pl.enableReadWriteOncePod {
if needsCheck {
return nil, nil return nil, nil
} }
return nil, framework.NewStatus(framework.Skip)
}
pvcs, err := pl.readWriteOncePodPVCsForPod(ctx, pod) pvcs, err := pl.readWriteOncePodPVCsForPod(ctx, pod)
if err != nil { if err != nil {
@ -172,6 +188,10 @@ func (pl *VolumeRestrictions) PreFilter(ctx context.Context, cycleState *framewo
if err != nil { if err != nil {
return nil, framework.AsStatus(err) return nil, framework.AsStatus(err)
} }
if !needsCheck && s.conflictingPVCRefCount == 0 {
return nil, framework.NewStatus(framework.Skip)
}
cycleState.Write(preFilterStateKey, s) cycleState.Write(preFilterStateKey, s)
return nil, nil return nil, nil
} }
@ -257,14 +277,12 @@ func (pl *VolumeRestrictions) readWriteOncePodPVCsForPod(ctx context.Context, po
// existing volumes. // existing volumes.
func satisfyVolumeConflicts(pod *v1.Pod, nodeInfo *framework.NodeInfo) bool { func satisfyVolumeConflicts(pod *v1.Pod, nodeInfo *framework.NodeInfo) bool {
for i := range pod.Spec.Volumes { for i := range pod.Spec.Volumes {
v := &pod.Spec.Volumes[i] v := pod.Spec.Volumes[i]
// fast path if there is no conflict checking targets. if !needsRestrictionsCheck(v) {
if v.GCEPersistentDisk == nil && v.AWSElasticBlockStore == nil && v.RBD == nil && v.ISCSI == nil {
continue continue
} }
for _, ev := range nodeInfo.Pods { for _, ev := range nodeInfo.Pods {
if isVolumeConflict(v, ev.Pod) { if isVolumeConflict(&v, ev.Pod) {
return false return false
} }
} }

View File

@ -18,7 +18,6 @@ package volumerestrictions
import ( import (
"context" "context"
"reflect"
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
@ -51,18 +50,53 @@ func TestGCEDiskConflicts(t *testing.T) {
}, },
}, },
} }
volWithNoRestriction := v1.Volume{
Name: "volume with no restriction",
VolumeSource: v1.VolumeSource{},
}
errStatus := framework.NewStatus(framework.Unschedulable, ErrReasonDiskConflict) errStatus := framework.NewStatus(framework.Unschedulable, ErrReasonDiskConflict)
tests := []struct { tests := []struct {
pod *v1.Pod pod *v1.Pod
nodeInfo *framework.NodeInfo nodeInfo *framework.NodeInfo
isOk bool
name string name string
preFilterWantStatus *framework.Status
wantStatus *framework.Status wantStatus *framework.Status
}{ }{
{&v1.Pod{}, framework.NewNodeInfo(), true, "nothing", nil}, {
{&v1.Pod{}, framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), true, "one state", nil}, pod: &v1.Pod{},
{st.MakePod().Volume(volState).Obj(), framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), false, "same state", errStatus}, nodeInfo: framework.NewNodeInfo(),
{st.MakePod().Volume(volState2).Obj(), framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), true, "different state", nil}, name: "nothing",
preFilterWantStatus: framework.NewStatus(framework.Skip),
wantStatus: nil,
},
{
pod: &v1.Pod{},
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "one state",
preFilterWantStatus: framework.NewStatus(framework.Skip),
wantStatus: nil,
},
{
pod: st.MakePod().Volume(volState).Obj(),
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "same state",
preFilterWantStatus: nil,
wantStatus: errStatus,
},
{
pod: st.MakePod().Volume(volState2).Obj(),
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "different state",
preFilterWantStatus: nil,
wantStatus: nil,
},
{
pod: st.MakePod().Volume(volWithNoRestriction).Obj(),
nodeInfo: framework.NewNodeInfo(),
name: "pod with a volume that doesn't have restrictions",
preFilterWantStatus: framework.NewStatus(framework.Skip),
wantStatus: nil,
},
} }
for _, test := range tests { for _, test := range tests {
@ -71,10 +105,16 @@ func TestGCEDiskConflicts(t *testing.T) {
defer cancel() defer cancel()
p := newPlugin(ctx, t) p := newPlugin(ctx, t)
cycleState := framework.NewCycleState() cycleState := framework.NewCycleState()
p.(framework.PreFilterPlugin).PreFilter(ctx, cycleState, test.pod) _, preFilterGotStatus := p.(framework.PreFilterPlugin).PreFilter(ctx, cycleState, test.pod)
if diff := cmp.Diff(test.preFilterWantStatus, preFilterGotStatus); diff != "" {
t.Errorf("Unexpected PreFilter status (-want, +got): %s", diff)
}
// If PreFilter fails, then Filter will not run.
if test.preFilterWantStatus.IsSuccess() {
gotStatus := p.(framework.FilterPlugin).Filter(ctx, cycleState, test.pod, test.nodeInfo) gotStatus := p.(framework.FilterPlugin).Filter(ctx, cycleState, test.pod, test.nodeInfo)
if !reflect.DeepEqual(gotStatus, test.wantStatus) { if diff := cmp.Diff(test.wantStatus, gotStatus); diff != "" {
t.Errorf("status does not match: %v, want: %v", gotStatus, test.wantStatus) t.Errorf("Unexpected Filter status (-want, +got): %s", diff)
}
} }
}) })
} }
@ -99,14 +139,38 @@ func TestAWSDiskConflicts(t *testing.T) {
tests := []struct { tests := []struct {
pod *v1.Pod pod *v1.Pod
nodeInfo *framework.NodeInfo nodeInfo *framework.NodeInfo
isOk bool
name string name string
wantStatus *framework.Status wantStatus *framework.Status
preFilterWantStatus *framework.Status
}{ }{
{&v1.Pod{}, framework.NewNodeInfo(), true, "nothing", nil}, {
{&v1.Pod{}, framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), true, "one state", nil}, pod: &v1.Pod{},
{st.MakePod().Volume(volState).Obj(), framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), false, "same state", errStatus}, nodeInfo: framework.NewNodeInfo(),
{st.MakePod().Volume(volState2).Obj(), framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), true, "different state", nil}, name: "nothing",
wantStatus: nil,
preFilterWantStatus: framework.NewStatus(framework.Skip),
},
{
pod: &v1.Pod{},
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "one state",
wantStatus: nil,
preFilterWantStatus: framework.NewStatus(framework.Skip),
},
{
pod: st.MakePod().Volume(volState).Obj(),
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "same state",
wantStatus: errStatus,
preFilterWantStatus: nil,
},
{
pod: st.MakePod().Volume(volState2).Obj(),
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "different state",
wantStatus: nil,
preFilterWantStatus: nil,
},
} }
for _, test := range tests { for _, test := range tests {
@ -115,10 +179,16 @@ func TestAWSDiskConflicts(t *testing.T) {
defer cancel() defer cancel()
p := newPlugin(ctx, t) p := newPlugin(ctx, t)
cycleState := framework.NewCycleState() cycleState := framework.NewCycleState()
p.(framework.PreFilterPlugin).PreFilter(ctx, cycleState, test.pod) _, preFilterGotStatus := p.(framework.PreFilterPlugin).PreFilter(ctx, cycleState, test.pod)
if diff := cmp.Diff(test.preFilterWantStatus, preFilterGotStatus); diff != "" {
t.Errorf("Unexpected PreFilter status (-want, +got): %s", diff)
}
// If PreFilter fails, then Filter will not run.
if test.preFilterWantStatus.IsSuccess() {
gotStatus := p.(framework.FilterPlugin).Filter(ctx, cycleState, test.pod, test.nodeInfo) gotStatus := p.(framework.FilterPlugin).Filter(ctx, cycleState, test.pod, test.nodeInfo)
if !reflect.DeepEqual(gotStatus, test.wantStatus) { if diff := cmp.Diff(test.wantStatus, gotStatus); diff != "" {
t.Errorf("status does not match: %v, want: %v", gotStatus, test.wantStatus) t.Errorf("Unexpected Filter status (-want, +got): %s", diff)
}
} }
}) })
} }
@ -149,14 +219,38 @@ func TestRBDDiskConflicts(t *testing.T) {
tests := []struct { tests := []struct {
pod *v1.Pod pod *v1.Pod
nodeInfo *framework.NodeInfo nodeInfo *framework.NodeInfo
isOk bool
name string name string
wantStatus *framework.Status wantStatus *framework.Status
preFilterWantStatus *framework.Status
}{ }{
{&v1.Pod{}, framework.NewNodeInfo(), true, "nothing", nil}, {
{&v1.Pod{}, framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), true, "one state", nil}, pod: &v1.Pod{},
{st.MakePod().Volume(volState).Obj(), framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), false, "same state", errStatus}, nodeInfo: framework.NewNodeInfo(),
{st.MakePod().Volume(volState2).Obj(), framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), true, "different state", nil}, name: "nothing",
wantStatus: nil,
preFilterWantStatus: framework.NewStatus(framework.Skip),
},
{
pod: &v1.Pod{},
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "one state",
wantStatus: nil,
preFilterWantStatus: framework.NewStatus(framework.Skip),
},
{
pod: st.MakePod().Volume(volState).Obj(),
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "same state",
wantStatus: errStatus,
preFilterWantStatus: nil,
},
{
pod: st.MakePod().Volume(volState2).Obj(),
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "different state",
wantStatus: nil,
preFilterWantStatus: nil,
},
} }
for _, test := range tests { for _, test := range tests {
@ -165,10 +259,16 @@ func TestRBDDiskConflicts(t *testing.T) {
defer cancel() defer cancel()
p := newPlugin(ctx, t) p := newPlugin(ctx, t)
cycleState := framework.NewCycleState() cycleState := framework.NewCycleState()
p.(framework.PreFilterPlugin).PreFilter(ctx, cycleState, test.pod) _, preFilterGotStatus := p.(framework.PreFilterPlugin).PreFilter(ctx, cycleState, test.pod)
if diff := cmp.Diff(test.preFilterWantStatus, preFilterGotStatus); diff != "" {
t.Errorf("Unexpected PreFilter status (-want, +got): %s", diff)
}
// If PreFilter fails, then Filter will not run.
if test.preFilterWantStatus.IsSuccess() {
gotStatus := p.(framework.FilterPlugin).Filter(ctx, cycleState, test.pod, test.nodeInfo) gotStatus := p.(framework.FilterPlugin).Filter(ctx, cycleState, test.pod, test.nodeInfo)
if !reflect.DeepEqual(gotStatus, test.wantStatus) { if diff := cmp.Diff(test.wantStatus, gotStatus); diff != "" {
t.Errorf("status does not match: %v, want: %v", gotStatus, test.wantStatus) t.Errorf("Unexpected Filter status (-want, +got): %s", diff)
}
} }
}) })
} }
@ -199,14 +299,38 @@ func TestISCSIDiskConflicts(t *testing.T) {
tests := []struct { tests := []struct {
pod *v1.Pod pod *v1.Pod
nodeInfo *framework.NodeInfo nodeInfo *framework.NodeInfo
isOk bool
name string name string
wantStatus *framework.Status wantStatus *framework.Status
preFilterWantStatus *framework.Status
}{ }{
{&v1.Pod{}, framework.NewNodeInfo(), true, "nothing", nil}, {
{&v1.Pod{}, framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), true, "one state", nil}, pod: &v1.Pod{},
{st.MakePod().Volume(volState).Obj(), framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), false, "same state", errStatus}, nodeInfo: framework.NewNodeInfo(),
{st.MakePod().Volume(volState2).Obj(), framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()), true, "different state", nil}, name: "nothing",
wantStatus: nil,
preFilterWantStatus: framework.NewStatus(framework.Skip),
},
{
pod: &v1.Pod{},
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "one state",
wantStatus: nil,
preFilterWantStatus: framework.NewStatus(framework.Skip),
},
{
pod: st.MakePod().Volume(volState).Obj(),
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "same state",
wantStatus: errStatus,
preFilterWantStatus: nil,
},
{
pod: st.MakePod().Volume(volState2).Obj(),
nodeInfo: framework.NewNodeInfo(st.MakePod().Volume(volState).Obj()),
name: "different state",
wantStatus: nil,
preFilterWantStatus: nil,
},
} }
for _, test := range tests { for _, test := range tests {
@ -215,10 +339,16 @@ func TestISCSIDiskConflicts(t *testing.T) {
defer cancel() defer cancel()
p := newPlugin(ctx, t) p := newPlugin(ctx, t)
cycleState := framework.NewCycleState() cycleState := framework.NewCycleState()
p.(framework.PreFilterPlugin).PreFilter(ctx, cycleState, test.pod) _, preFilterGotStatus := p.(framework.PreFilterPlugin).PreFilter(ctx, cycleState, test.pod)
if diff := cmp.Diff(test.preFilterWantStatus, preFilterGotStatus); diff != "" {
t.Errorf("Unexpected PreFilter status (-want, +got): %s", diff)
}
// If PreFilter fails, then Filter will not run.
if test.preFilterWantStatus.IsSuccess() {
gotStatus := p.(framework.FilterPlugin).Filter(ctx, cycleState, test.pod, test.nodeInfo) gotStatus := p.(framework.FilterPlugin).Filter(ctx, cycleState, test.pod, test.nodeInfo)
if !reflect.DeepEqual(gotStatus, test.wantStatus) { if diff := cmp.Diff(test.wantStatus, gotStatus); diff != "" {
t.Errorf("status does not match: %v, want: %v", gotStatus, test.wantStatus) t.Errorf("Unexpected Filter status (-want, +got): %s", diff)
}
} }
}) })
} }
@ -289,7 +419,18 @@ func TestAccessModeConflicts(t *testing.T) {
existingNodes: []*v1.Node{}, existingNodes: []*v1.Node{},
existingPVCs: []*v1.PersistentVolumeClaim{}, existingPVCs: []*v1.PersistentVolumeClaim{},
enableReadWriteOncePod: true, enableReadWriteOncePod: true,
preFilterWantStatus: nil, preFilterWantStatus: framework.NewStatus(framework.Skip),
wantStatus: nil,
},
{
name: "nothing, ReadWriteOncePod disabled",
pod: &v1.Pod{},
nodeInfo: framework.NewNodeInfo(),
existingPods: []*v1.Pod{},
existingNodes: []*v1.Node{},
existingPVCs: []*v1.PersistentVolumeClaim{},
enableReadWriteOncePod: false,
preFilterWantStatus: framework.NewStatus(framework.Skip),
wantStatus: nil, wantStatus: nil,
}, },
{ {
@ -311,7 +452,7 @@ func TestAccessModeConflicts(t *testing.T) {
existingNodes: []*v1.Node{node}, existingNodes: []*v1.Node{node},
existingPVCs: []*v1.PersistentVolumeClaim{readWriteOncePodPVC1, readWriteManyPVC}, existingPVCs: []*v1.PersistentVolumeClaim{readWriteOncePodPVC1, readWriteManyPVC},
enableReadWriteOncePod: true, enableReadWriteOncePod: true,
preFilterWantStatus: nil, preFilterWantStatus: framework.NewStatus(framework.Skip),
wantStatus: nil, wantStatus: nil,
}, },
{ {