volumezone: scheduler queueing hints
This commit is contained in:
		@@ -276,7 +276,7 @@ func (pl *VolumeZone) EventsToRegister() []framework.ClusterEventWithHint {
 | 
				
			|||||||
	return []framework.ClusterEventWithHint{
 | 
						return []framework.ClusterEventWithHint{
 | 
				
			||||||
		// New storageClass with bind mode `VolumeBindingWaitForFirstConsumer` will make a pod schedulable.
 | 
							// New storageClass with bind mode `VolumeBindingWaitForFirstConsumer` will make a pod schedulable.
 | 
				
			||||||
		// Due to immutable field `storageClass.volumeBindingMode`, storageClass update events are ignored.
 | 
							// Due to immutable field `storageClass.volumeBindingMode`, storageClass update events are ignored.
 | 
				
			||||||
		{Event: framework.ClusterEvent{Resource: framework.StorageClass, ActionType: framework.Add}},
 | 
							{Event: framework.ClusterEvent{Resource: framework.StorageClass, ActionType: framework.Add}, QueueingHintFn: pl.isSchedulableAfterStorageClassAdded},
 | 
				
			||||||
		// A new node or updating a node's volume zone labels may make a pod schedulable.
 | 
							// A new node or updating a node's volume zone labels may make a pod schedulable.
 | 
				
			||||||
		//
 | 
							//
 | 
				
			||||||
		// A note about UpdateNodeTaint event:
 | 
							// A note about UpdateNodeTaint event:
 | 
				
			||||||
@@ -342,6 +342,23 @@ func (pl *VolumeZone) isPVCRequestedFromPod(logger klog.Logger, pvc *v1.Persiste
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// isSchedulableAfterStorageClassAdded is invoked whenever a StorageClass is added.
 | 
				
			||||||
 | 
					// It checks whether the addition of StorageClass has made a previously unschedulable pod schedulable.
 | 
				
			||||||
 | 
					// Only a new StorageClass with WaitForFirstConsumer will cause a pod to become schedulable.
 | 
				
			||||||
 | 
					func (pl *VolumeZone) isSchedulableAfterStorageClassAdded(logger klog.Logger, pod *v1.Pod, oldObj, newObj interface{}) (framework.QueueingHint, error) {
 | 
				
			||||||
 | 
						_, addedStorageClass, err := util.As[*storage.StorageClass](nil, newObj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return framework.Queue, fmt.Errorf("unexpected objects in isSchedulableAfterStorageClassAdded: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (addedStorageClass.VolumeBindingMode == nil) || (*addedStorageClass.VolumeBindingMode != storage.VolumeBindingWaitForFirstConsumer) {
 | 
				
			||||||
 | 
							logger.V(5).Info("StorageClass is created, but its VolumeBindingMode is not waitForFirstConsumer, which doesn't make the pod schedulable", "storageClass", klog.KObj(addedStorageClass), "pod", klog.KObj(pod))
 | 
				
			||||||
 | 
							return framework.QueueSkip, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logger.V(5).Info("StorageClass with waitForFirstConsumer mode was created and it might make this pod schedulable", "pod", klog.KObj(pod), "StorageClass", klog.KObj(addedStorageClass))
 | 
				
			||||||
 | 
						return framework.Queue, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New initializes a new plugin and returns it.
 | 
					// New initializes a new plugin and returns it.
 | 
				
			||||||
func New(_ context.Context, _ runtime.Object, handle framework.Handle) (framework.Plugin, error) {
 | 
					func New(_ context.Context, _ runtime.Object, handle framework.Handle) (framework.Plugin, error) {
 | 
				
			||||||
	informerFactory := handle.SharedInformerFactory()
 | 
						informerFactory := handle.SharedInformerFactory()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -627,6 +627,54 @@ func TestIsSchedulableAfterPersistentVolumeClaimAdded(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestIsSchedulableAfterStorageClassAdded(t *testing.T) {
 | 
				
			||||||
 | 
						var modeWait = storagev1.VolumeBindingWaitForFirstConsumer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testcases := map[string]struct {
 | 
				
			||||||
 | 
							pod            *v1.Pod
 | 
				
			||||||
 | 
							oldObj, newObj interface{}
 | 
				
			||||||
 | 
							expectedHint   framework.QueueingHint
 | 
				
			||||||
 | 
							expectedErr    bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							"error-wrong-new-object": {
 | 
				
			||||||
 | 
								pod:          createPodWithVolume("pod_1", "PVC_1"),
 | 
				
			||||||
 | 
								newObj:       "not-a-storageclass",
 | 
				
			||||||
 | 
								expectedHint: framework.Queue,
 | 
				
			||||||
 | 
								expectedErr:  true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"sc-doesn't-have-volume-binding-mode": {
 | 
				
			||||||
 | 
								pod: createPodWithVolume("pod_1", "PVC_1"),
 | 
				
			||||||
 | 
								newObj: &storagev1.StorageClass{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "SC_1"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expectedHint: framework.QueueSkip,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"new-sc-is-wait-for-first-consumer-mode": {
 | 
				
			||||||
 | 
								pod: createPodWithVolume("pod_1", "PVC_1"),
 | 
				
			||||||
 | 
								newObj: &storagev1.StorageClass{
 | 
				
			||||||
 | 
									ObjectMeta:        metav1.ObjectMeta{Name: "SC_1"},
 | 
				
			||||||
 | 
									VolumeBindingMode: &modeWait,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expectedHint: framework.Queue,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for name, tc := range testcases {
 | 
				
			||||||
 | 
							t.Run(name, func(t *testing.T) {
 | 
				
			||||||
 | 
								logger, _ := ktesting.NewTestContext(t)
 | 
				
			||||||
 | 
								p := &VolumeZone{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								got, err := p.isSchedulableAfterStorageClassAdded(logger, tc.pod, tc.oldObj, tc.newObj)
 | 
				
			||||||
 | 
								if err != nil && !tc.expectedErr {
 | 
				
			||||||
 | 
									t.Errorf("unexpected error: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if got != tc.expectedHint {
 | 
				
			||||||
 | 
									t.Errorf("isSchedulableAfterStorageClassAdded() = %v, want %v", got, tc.expectedHint)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func BenchmarkVolumeZone(b *testing.B) {
 | 
					func BenchmarkVolumeZone(b *testing.B) {
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		Name      string
 | 
							Name      string
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user