Skip to content

Commit 9247a21

Browse files
authored
Merge pull request kubernetes#124959 from bells17/qhint-volume-binding-pvc
volumebinding: scheduler queueing hints - PersistentVolumeClaim
2 parents 8aff9d3 + aceb446 commit 9247a21

File tree

2 files changed

+142
-1
lines changed

2 files changed

+142
-1
lines changed

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,11 @@ func (pl *VolumeBinding) EventsToRegister() []framework.ClusterEventWithHint {
9999
// (e.g., allowedTopologies, volumeBindingMode), and hence may become
100100
// schedulable upon StorageClass Add or Update events.
101101
{Event: framework.ClusterEvent{Resource: framework.StorageClass, ActionType: framework.Add | framework.Update}},
102+
102103
// We bind PVCs with PVs, so any changes may make the pods schedulable.
103-
{Event: framework.ClusterEvent{Resource: framework.PersistentVolumeClaim, ActionType: framework.Add | framework.Update}},
104+
{Event: framework.ClusterEvent{Resource: framework.PersistentVolumeClaim, ActionType: framework.Add | framework.Update}, QueueingHintFn: pl.isSchedulableAfterPersistentVolumeClaimChange},
104105
{Event: framework.ClusterEvent{Resource: framework.PersistentVolume, ActionType: framework.Add | framework.Update}},
106+
105107
// Pods may fail to find available PVs because the node labels do not
106108
// match the storage class's allowed topologies or PV's node affinity.
107109
// A new or updated node may make pods schedulable.
@@ -115,9 +117,11 @@ func (pl *VolumeBinding) EventsToRegister() []framework.ClusterEventWithHint {
115117
// We can remove UpdateNodeTaint when we remove the preCheck feature.
116118
// See: https://github.com/kubernetes/kubernetes/issues/110175
117119
{Event: framework.ClusterEvent{Resource: framework.Node, ActionType: framework.Add | framework.UpdateNodeLabel | framework.UpdateNodeTaint}},
120+
118121
// We rely on CSI node to translate in-tree PV to CSI.
119122
// TODO: kube-schduler will unregister the CSINode events once all the volume plugins has completed their CSI migration.
120123
{Event: framework.ClusterEvent{Resource: framework.CSINode, ActionType: framework.Add | framework.Update}, QueueingHintFn: pl.isSchedulableAfterCSINodeChange},
124+
121125
// When CSIStorageCapacity is enabled, pods may become schedulable
122126
// on CSI driver & storage capacity changes.
123127
{Event: framework.ClusterEvent{Resource: framework.CSIDriver, ActionType: framework.Add | framework.Update}},
@@ -151,6 +155,46 @@ func (pl *VolumeBinding) isSchedulableAfterCSINodeChange(logger klog.Logger, pod
151155
return framework.QueueSkip, nil
152156
}
153157

158+
func (pl *VolumeBinding) isSchedulableAfterPersistentVolumeClaimChange(logger klog.Logger, pod *v1.Pod, oldObj, newObj interface{}) (framework.QueueingHint, error) {
159+
_, newPVC, err := util.As[*v1.PersistentVolumeClaim](oldObj, newObj)
160+
if err != nil {
161+
return framework.Queue, err
162+
}
163+
164+
logger = klog.LoggerWithValues(
165+
logger,
166+
"Pod", klog.KObj(pod),
167+
"PersistentVolumeClaim", klog.KObj(newPVC),
168+
)
169+
170+
if pod.Namespace != newPVC.Namespace {
171+
logger.V(5).Info("PersistentVolumeClaim was created or updated, but it doesn't make this pod schedulable because the PVC belongs to a different namespace")
172+
return framework.QueueSkip, nil
173+
}
174+
175+
for _, vol := range pod.Spec.Volumes {
176+
var pvcName string
177+
switch {
178+
case vol.PersistentVolumeClaim != nil:
179+
pvcName = vol.PersistentVolumeClaim.ClaimName
180+
case vol.Ephemeral != nil:
181+
pvcName = ephemeral.VolumeClaimName(pod, &vol)
182+
default:
183+
continue
184+
}
185+
186+
if pvcName == newPVC.Name {
187+
// Return Queue because, in this case,
188+
// all PVC creations and almost all PVC updates could make the Pod schedulable.
189+
logger.V(5).Info("PersistentVolumeClaim the pod requires was created or updated, potentially making the target Pod schedulable")
190+
return framework.Queue, nil
191+
}
192+
}
193+
194+
logger.V(5).Info("PersistentVolumeClaim was created or updated, but it doesn't make this pod schedulable")
195+
return framework.QueueSkip, nil
196+
}
197+
154198
// podHasPVCs returns 2 values:
155199
// - the first one to denote if the given "pod" has any PVC defined.
156200
// - the second one to return any error if the requested PVC is illegal.

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

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,3 +996,100 @@ func TestIsSchedulableAfterCSINodeChange(t *testing.T) {
996996
})
997997
}
998998
}
999+
1000+
func TestIsSchedulableAfterPersistentVolumeClaimChange(t *testing.T) {
1001+
table := []struct {
1002+
name string
1003+
pod *v1.Pod
1004+
oldPVC interface{}
1005+
newPVC interface{}
1006+
wantErr bool
1007+
expect framework.QueueingHint
1008+
}{
1009+
{
1010+
name: "pod has no pvc or ephemeral volumes",
1011+
pod: makePod("pod-a").withEmptyDirVolume().Pod,
1012+
oldPVC: makePVC("pvc-b", "sc-a").PersistentVolumeClaim,
1013+
newPVC: makePVC("pvc-b", "sc-a").PersistentVolumeClaim,
1014+
wantErr: false,
1015+
expect: framework.QueueSkip,
1016+
},
1017+
{
1018+
name: "pvc with the same name as the one used by the pod in a different namespace is modified",
1019+
pod: makePod("pod-a").
1020+
withNamespace("ns-a").
1021+
withPVCVolume("pvc-a", "").
1022+
withPVCVolume("pvc-b", "").
1023+
Pod,
1024+
oldPVC: nil,
1025+
newPVC: makePVC("pvc-b", "").PersistentVolumeClaim,
1026+
wantErr: false,
1027+
expect: framework.QueueSkip,
1028+
},
1029+
{
1030+
name: "pod has no pvc that is being modified",
1031+
pod: makePod("pod-a").
1032+
withPVCVolume("pvc-a", "").
1033+
withPVCVolume("pvc-c", "").
1034+
Pod,
1035+
oldPVC: makePVC("pvc-b", "").PersistentVolumeClaim,
1036+
newPVC: makePVC("pvc-b", "").PersistentVolumeClaim,
1037+
wantErr: false,
1038+
expect: framework.QueueSkip,
1039+
},
1040+
{
1041+
name: "pod has no generic ephemeral volume that is being modified",
1042+
pod: makePod("pod-a").
1043+
withGenericEphemeralVolume("ephemeral-a").
1044+
withGenericEphemeralVolume("ephemeral-c").
1045+
Pod,
1046+
oldPVC: makePVC("pod-a-ephemeral-b", "").PersistentVolumeClaim,
1047+
newPVC: makePVC("pod-a-ephemeral-b", "").PersistentVolumeClaim,
1048+
wantErr: false,
1049+
expect: framework.QueueSkip,
1050+
},
1051+
{
1052+
name: "pod has the pvc that is being modified",
1053+
pod: makePod("pod-a").
1054+
withPVCVolume("pvc-a", "").
1055+
withPVCVolume("pvc-b", "").
1056+
Pod,
1057+
oldPVC: makePVC("pvc-b", "").PersistentVolumeClaim,
1058+
newPVC: makePVC("pvc-b", "").PersistentVolumeClaim,
1059+
wantErr: false,
1060+
expect: framework.Queue,
1061+
},
1062+
{
1063+
name: "pod has the generic ephemeral volume that is being modified",
1064+
pod: makePod("pod-a").
1065+
withGenericEphemeralVolume("ephemeral-a").
1066+
withGenericEphemeralVolume("ephemeral-b").
1067+
Pod,
1068+
oldPVC: makePVC("pod-a-ephemeral-b", "").PersistentVolumeClaim,
1069+
newPVC: makePVC("pod-a-ephemeral-b", "").PersistentVolumeClaim,
1070+
wantErr: false,
1071+
expect: framework.Queue,
1072+
},
1073+
{
1074+
name: "type conversion error",
1075+
oldPVC: new(struct{}),
1076+
newPVC: new(struct{}),
1077+
wantErr: true,
1078+
expect: framework.Queue,
1079+
},
1080+
}
1081+
1082+
for _, item := range table {
1083+
t.Run(item.name, func(t *testing.T) {
1084+
pl := &VolumeBinding{}
1085+
logger, _ := ktesting.NewTestContext(t)
1086+
qhint, err := pl.isSchedulableAfterPersistentVolumeClaimChange(logger, item.pod, item.oldPVC, item.newPVC)
1087+
if (err != nil) != item.wantErr {
1088+
t.Errorf("isSchedulableAfterPersistentVolumeClaimChange failed - got: %q", err)
1089+
}
1090+
if qhint != item.expect {
1091+
t.Errorf("QHint does not match: %v, want: %v", qhint, item.expect)
1092+
}
1093+
})
1094+
}
1095+
}

0 commit comments

Comments
 (0)