mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 18:00:08 +00:00
volumebinding: scheduler queueing hints - PersistentVolumeClaim
This commit is contained in:
parent
4c7960a67c
commit
aceb4468b6
@ -99,9 +99,11 @@ func (pl *VolumeBinding) EventsToRegister() []framework.ClusterEventWithHint {
|
|||||||
// (e.g., allowedTopologies, volumeBindingMode), and hence may become
|
// (e.g., allowedTopologies, volumeBindingMode), and hence may become
|
||||||
// schedulable upon StorageClass Add or Update events.
|
// schedulable upon StorageClass Add or Update events.
|
||||||
{Event: framework.ClusterEvent{Resource: framework.StorageClass, ActionType: framework.Add | framework.Update}},
|
{Event: framework.ClusterEvent{Resource: framework.StorageClass, ActionType: framework.Add | framework.Update}},
|
||||||
|
|
||||||
// We bind PVCs with PVs, so any changes may make the pods schedulable.
|
// We bind PVCs with PVs, so any changes may make the pods schedulable.
|
||||||
{Event: framework.ClusterEvent{Resource: framework.PersistentVolumeClaim, ActionType: framework.Add | framework.Update}},
|
{Event: framework.ClusterEvent{Resource: framework.PersistentVolumeClaim, ActionType: framework.Add | framework.Update}, QueueingHintFn: pl.isSchedulableAfterPersistentVolumeClaimChange},
|
||||||
{Event: framework.ClusterEvent{Resource: framework.PersistentVolume, ActionType: framework.Add | framework.Update}},
|
{Event: framework.ClusterEvent{Resource: framework.PersistentVolume, ActionType: framework.Add | framework.Update}},
|
||||||
|
|
||||||
// Pods may fail to find available PVs because the node labels do not
|
// Pods may fail to find available PVs because the node labels do not
|
||||||
// match the storage class's allowed topologies or PV's node affinity.
|
// match the storage class's allowed topologies or PV's node affinity.
|
||||||
// A new or updated node may make pods schedulable.
|
// A new or updated node may make pods schedulable.
|
||||||
@ -115,9 +117,11 @@ func (pl *VolumeBinding) EventsToRegister() []framework.ClusterEventWithHint {
|
|||||||
// We can remove UpdateNodeTaint when we remove the preCheck feature.
|
// We can remove UpdateNodeTaint when we remove the preCheck feature.
|
||||||
// See: https://github.com/kubernetes/kubernetes/issues/110175
|
// See: https://github.com/kubernetes/kubernetes/issues/110175
|
||||||
{Event: framework.ClusterEvent{Resource: framework.Node, ActionType: framework.Add | framework.UpdateNodeLabel | framework.UpdateNodeTaint}},
|
{Event: framework.ClusterEvent{Resource: framework.Node, ActionType: framework.Add | framework.UpdateNodeLabel | framework.UpdateNodeTaint}},
|
||||||
|
|
||||||
// We rely on CSI node to translate in-tree PV to CSI.
|
// We rely on CSI node to translate in-tree PV to CSI.
|
||||||
// TODO: kube-schduler will unregister the CSINode events once all the volume plugins has completed their CSI migration.
|
// TODO: kube-schduler will unregister the CSINode events once all the volume plugins has completed their CSI migration.
|
||||||
{Event: framework.ClusterEvent{Resource: framework.CSINode, ActionType: framework.Add | framework.Update}, QueueingHintFn: pl.isSchedulableAfterCSINodeChange},
|
{Event: framework.ClusterEvent{Resource: framework.CSINode, ActionType: framework.Add | framework.Update}, QueueingHintFn: pl.isSchedulableAfterCSINodeChange},
|
||||||
|
|
||||||
// When CSIStorageCapacity is enabled, pods may become schedulable
|
// When CSIStorageCapacity is enabled, pods may become schedulable
|
||||||
// on CSI driver & storage capacity changes.
|
// on CSI driver & storage capacity changes.
|
||||||
{Event: framework.ClusterEvent{Resource: framework.CSIDriver, ActionType: framework.Add | framework.Update}},
|
{Event: framework.ClusterEvent{Resource: framework.CSIDriver, ActionType: framework.Add | framework.Update}},
|
||||||
@ -151,6 +155,46 @@ func (pl *VolumeBinding) isSchedulableAfterCSINodeChange(logger klog.Logger, pod
|
|||||||
return framework.QueueSkip, nil
|
return framework.QueueSkip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pl *VolumeBinding) isSchedulableAfterPersistentVolumeClaimChange(logger klog.Logger, pod *v1.Pod, oldObj, newObj interface{}) (framework.QueueingHint, error) {
|
||||||
|
_, newPVC, err := util.As[*v1.PersistentVolumeClaim](oldObj, newObj)
|
||||||
|
if err != nil {
|
||||||
|
return framework.Queue, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = klog.LoggerWithValues(
|
||||||
|
logger,
|
||||||
|
"Pod", klog.KObj(pod),
|
||||||
|
"PersistentVolumeClaim", klog.KObj(newPVC),
|
||||||
|
)
|
||||||
|
|
||||||
|
if pod.Namespace != newPVC.Namespace {
|
||||||
|
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")
|
||||||
|
return framework.QueueSkip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, vol := range pod.Spec.Volumes {
|
||||||
|
var pvcName string
|
||||||
|
switch {
|
||||||
|
case vol.PersistentVolumeClaim != nil:
|
||||||
|
pvcName = vol.PersistentVolumeClaim.ClaimName
|
||||||
|
case vol.Ephemeral != nil:
|
||||||
|
pvcName = ephemeral.VolumeClaimName(pod, &vol)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if pvcName == newPVC.Name {
|
||||||
|
// Return Queue because, in this case,
|
||||||
|
// all PVC creations and almost all PVC updates could make the Pod schedulable.
|
||||||
|
logger.V(5).Info("PersistentVolumeClaim the pod requires was created or updated, potentially making the target Pod schedulable")
|
||||||
|
return framework.Queue, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.V(5).Info("PersistentVolumeClaim was created or updated, but it doesn't make this pod schedulable")
|
||||||
|
return framework.QueueSkip, nil
|
||||||
|
}
|
||||||
|
|
||||||
// podHasPVCs returns 2 values:
|
// podHasPVCs returns 2 values:
|
||||||
// - the first one to denote if the given "pod" has any PVC defined.
|
// - the first one to denote if the given "pod" has any PVC defined.
|
||||||
// - the second one to return any error if the requested PVC is illegal.
|
// - the second one to return any error if the requested PVC is illegal.
|
||||||
|
@ -996,3 +996,100 @@ func TestIsSchedulableAfterCSINodeChange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsSchedulableAfterPersistentVolumeClaimChange(t *testing.T) {
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
pod *v1.Pod
|
||||||
|
oldPVC interface{}
|
||||||
|
newPVC interface{}
|
||||||
|
wantErr bool
|
||||||
|
expect framework.QueueingHint
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "pod has no pvc or ephemeral volumes",
|
||||||
|
pod: makePod("pod-a").withEmptyDirVolume().Pod,
|
||||||
|
oldPVC: makePVC("pvc-b", "sc-a").PersistentVolumeClaim,
|
||||||
|
newPVC: makePVC("pvc-b", "sc-a").PersistentVolumeClaim,
|
||||||
|
wantErr: false,
|
||||||
|
expect: framework.QueueSkip,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pvc with the same name as the one used by the pod in a different namespace is modified",
|
||||||
|
pod: makePod("pod-a").
|
||||||
|
withNamespace("ns-a").
|
||||||
|
withPVCVolume("pvc-a", "").
|
||||||
|
withPVCVolume("pvc-b", "").
|
||||||
|
Pod,
|
||||||
|
oldPVC: nil,
|
||||||
|
newPVC: makePVC("pvc-b", "").PersistentVolumeClaim,
|
||||||
|
wantErr: false,
|
||||||
|
expect: framework.QueueSkip,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pod has no pvc that is being modified",
|
||||||
|
pod: makePod("pod-a").
|
||||||
|
withPVCVolume("pvc-a", "").
|
||||||
|
withPVCVolume("pvc-c", "").
|
||||||
|
Pod,
|
||||||
|
oldPVC: makePVC("pvc-b", "").PersistentVolumeClaim,
|
||||||
|
newPVC: makePVC("pvc-b", "").PersistentVolumeClaim,
|
||||||
|
wantErr: false,
|
||||||
|
expect: framework.QueueSkip,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pod has no generic ephemeral volume that is being modified",
|
||||||
|
pod: makePod("pod-a").
|
||||||
|
withGenericEphemeralVolume("ephemeral-a").
|
||||||
|
withGenericEphemeralVolume("ephemeral-c").
|
||||||
|
Pod,
|
||||||
|
oldPVC: makePVC("pod-a-ephemeral-b", "").PersistentVolumeClaim,
|
||||||
|
newPVC: makePVC("pod-a-ephemeral-b", "").PersistentVolumeClaim,
|
||||||
|
wantErr: false,
|
||||||
|
expect: framework.QueueSkip,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pod has the pvc that is being modified",
|
||||||
|
pod: makePod("pod-a").
|
||||||
|
withPVCVolume("pvc-a", "").
|
||||||
|
withPVCVolume("pvc-b", "").
|
||||||
|
Pod,
|
||||||
|
oldPVC: makePVC("pvc-b", "").PersistentVolumeClaim,
|
||||||
|
newPVC: makePVC("pvc-b", "").PersistentVolumeClaim,
|
||||||
|
wantErr: false,
|
||||||
|
expect: framework.Queue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pod has the generic ephemeral volume that is being modified",
|
||||||
|
pod: makePod("pod-a").
|
||||||
|
withGenericEphemeralVolume("ephemeral-a").
|
||||||
|
withGenericEphemeralVolume("ephemeral-b").
|
||||||
|
Pod,
|
||||||
|
oldPVC: makePVC("pod-a-ephemeral-b", "").PersistentVolumeClaim,
|
||||||
|
newPVC: makePVC("pod-a-ephemeral-b", "").PersistentVolumeClaim,
|
||||||
|
wantErr: false,
|
||||||
|
expect: framework.Queue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "type conversion error",
|
||||||
|
oldPVC: new(struct{}),
|
||||||
|
newPVC: new(struct{}),
|
||||||
|
wantErr: true,
|
||||||
|
expect: framework.Queue,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range table {
|
||||||
|
t.Run(item.name, func(t *testing.T) {
|
||||||
|
pl := &VolumeBinding{}
|
||||||
|
logger, _ := ktesting.NewTestContext(t)
|
||||||
|
qhint, err := pl.isSchedulableAfterPersistentVolumeClaimChange(logger, item.pod, item.oldPVC, item.newPVC)
|
||||||
|
if (err != nil) != item.wantErr {
|
||||||
|
t.Errorf("isSchedulableAfterPersistentVolumeClaimChange failed - got: %q", err)
|
||||||
|
}
|
||||||
|
if qhint != item.expect {
|
||||||
|
t.Errorf("QHint does not match: %v, want: %v", qhint, item.expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user