volumezone: scheduler queueing hints
This commit is contained in:
		| @@ -276,7 +276,7 @@ func (pl *VolumeZone) EventsToRegister() []framework.ClusterEventWithHint { | ||||
| 	return []framework.ClusterEventWithHint{ | ||||
| 		// New storageClass with bind mode `VolumeBindingWaitForFirstConsumer` will make a pod schedulable. | ||||
| 		// 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 note about UpdateNodeTaint event: | ||||
| @@ -342,6 +342,23 @@ func (pl *VolumeZone) isPVCRequestedFromPod(logger klog.Logger, pvc *v1.Persiste | ||||
| 	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. | ||||
| func New(_ context.Context, _ runtime.Object, handle framework.Handle) (framework.Plugin, error) { | ||||
| 	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) { | ||||
| 	tests := []struct { | ||||
| 		Name      string | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 moriya
					moriya