Refactor scheduler event handlers for pods to handle binding event in one place

This commit is contained in:
Maciej Skoczeń
2025-10-29 09:56:24 +00:00
parent 286d13b96c
commit 1502996787
4 changed files with 427 additions and 126 deletions

View File

@@ -126,12 +126,93 @@ func (sched *Scheduler) deleteNodeFromCache(obj interface{}) {
}
}
func (sched *Scheduler) addPodToSchedulingQueue(obj interface{}) {
func (sched *Scheduler) addPod(obj interface{}) {
logger := sched.logger
pod, ok := obj.(*v1.Pod)
if !ok {
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert to *v1.Pod", "obj", obj)
return
}
if assignedPod(pod) {
sched.addAssignedPodToCache(pod)
} else if responsibleForPod(pod, sched.Profiles) {
sched.addPodToSchedulingQueue(pod)
}
}
func (sched *Scheduler) updatePod(oldObj, newObj interface{}) {
logger := sched.logger
oldPod, ok := oldObj.(*v1.Pod)
if !ok {
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert oldObj to *v1.Pod", "oldObj", oldObj)
return
}
newPod, ok := newObj.(*v1.Pod)
if !ok {
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert newObj to *v1.Pod", "newObj", newObj)
return
}
if assignedPod(oldPod) {
sched.updateAssignedPodInCache(oldPod, newPod)
} else if assignedPod(newPod) {
// This update means binding operation. We can treat it as adding the pod to a cache
// (addition to the cache will handle this binding appropriately).
sched.addAssignedPodToCache(newPod)
if responsibleForPod(oldPod, sched.Profiles) {
// Pod shouldn't be in the scheduling queue, but in unlikely event that the pod has been bound
// by another component, it should be removed from scheduling queue for correctness.
// Passing "true" means that removal from the scheduling queue is caused by a binding event,
// not by removal of the pod from the cluster.
sched.deletePodFromSchedulingQueue(oldPod, true)
}
} else if responsibleForPod(oldPod, sched.Profiles) {
sched.updatePodInSchedulingQueue(oldPod, newPod)
}
}
func (sched *Scheduler) deletePod(obj interface{}) {
logger := sched.logger
var pod *v1.Pod
switch t := obj.(type) {
case *v1.Pod:
pod = t
if assignedPod(pod) {
sched.deleteAssignedPodFromCache(pod)
} else if responsibleForPod(pod, sched.Profiles) {
// Passing "false" means that removal from the scheduling queue is caused by
// removal of the pod from the cluster, not by a binding event.
sched.deletePodFromSchedulingQueue(pod, false)
}
return
case cache.DeletedFinalStateUnknown:
var ok bool
pod, ok = t.Obj.(*v1.Pod)
if !ok {
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert to *v1.Pod", "obj", t.Obj)
return
}
// The carried object may be stale, so we don't use it to check if
// it's assigned or not. Attempting to cleanup anyways.
sched.deleteAssignedPodFromCache(pod)
if responsibleForPod(pod, sched.Profiles) {
// Passing "false" means that removal from the scheduling queue is caused by
// removal of the pod from the cluster, not by a binding event.
sched.deletePodFromSchedulingQueue(pod, false)
}
return
default:
utilruntime.HandleErrorWithLogger(logger, nil, "Unable to handle object", "objType", fmt.Sprintf("%T", obj), "obj", obj)
return
}
}
func (sched *Scheduler) addPodToSchedulingQueue(pod *v1.Pod) {
start := time.Now()
defer metrics.EventHandlingLatency.WithLabelValues(framework.EventUnscheduledPodAdd.Label()).Observe(metrics.SinceInSeconds(start))
logger := sched.logger
pod := obj.(*v1.Pod)
logger.V(3).Info("Add event for unscheduled pod", "pod", klog.KObj(pod))
sched.SchedulingQueue.Add(logger, pod)
}
@@ -150,10 +231,9 @@ func (sched *Scheduler) syncPodWithDispatcher(pod *v1.Pod) *v1.Pod {
return enrichedPod
}
func (sched *Scheduler) updatePodInSchedulingQueue(oldObj, newObj interface{}) {
func (sched *Scheduler) updatePodInSchedulingQueue(oldPod, newPod *v1.Pod) {
start := time.Now()
logger := sched.logger
oldPod, newPod := oldObj.(*v1.Pod), newObj.(*v1.Pod)
// Bypass update event that carries identical objects; otherwise, a duplicated
// Pod may go through scheduling and cause unexpected behavior (see #96071).
if oldPod.ResourceVersion == newPod.ResourceVersion {
@@ -195,29 +275,21 @@ func hasNominatedNodeNameChanged(oldPod, newPod *v1.Pod) bool {
return len(oldPod.Status.NominatedNodeName) > 0 && oldPod.Status.NominatedNodeName != newPod.Status.NominatedNodeName
}
func (sched *Scheduler) deletePodFromSchedulingQueue(obj interface{}) {
func (sched *Scheduler) deletePodFromSchedulingQueue(pod *v1.Pod, inBinding bool) {
start := time.Now()
defer metrics.EventHandlingLatency.WithLabelValues(framework.EventUnscheduledPodDelete.Label()).Observe(metrics.SinceInSeconds(start))
logger := sched.logger
var pod *v1.Pod
switch t := obj.(type) {
case *v1.Pod:
pod = obj.(*v1.Pod)
case cache.DeletedFinalStateUnknown:
var ok bool
pod, ok = t.Obj.(*v1.Pod)
if !ok {
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert to *v1.Pod", "obj", t.Obj)
return
}
default:
utilruntime.HandleErrorWithLogger(logger, nil, "Unable to handle object", "objType", fmt.Sprintf("%T", obj), "obj", obj)
return
}
logger.V(3).Info("Delete event for unscheduled pod", "pod", klog.KObj(pod))
sched.SchedulingQueue.Delete(pod)
if inBinding {
// In the case of a binding, the rest can be skipped because it is not really a pod removal operation, but a binding.
// Any necessary notifications will be sent by the binding process, unless it was an unlikely external binding.
// In that case, we need to notify about the release of resources that were held by different assume/nomination
// once the https://github.com/kubernetes/kubernetes/issues/134859 is fixed.
return
}
fwk, err := sched.frameworkForPod(pod)
if err != nil {
// This shouldn't happen, because we only accept for scheduling the pods
@@ -246,16 +318,11 @@ func getLEPriorityPreCheck(priority int32) queue.PreEnqueueCheck {
}
}
func (sched *Scheduler) addPodToCache(obj interface{}) {
func (sched *Scheduler) addAssignedPodToCache(pod *v1.Pod) {
start := time.Now()
defer metrics.EventHandlingLatency.WithLabelValues(framework.EventAssignedPodAdd.Label()).Observe(metrics.SinceInSeconds(start))
logger := sched.logger
pod, ok := obj.(*v1.Pod)
if !ok {
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert to *v1.Pod", "obj", obj)
return
}
logger.V(3).Info("Add event for scheduled pod", "pod", klog.KObj(pod))
if err := sched.Cache.AddPod(logger, pod); err != nil {
@@ -278,21 +345,11 @@ func (sched *Scheduler) addPodToCache(obj interface{}) {
}
}
func (sched *Scheduler) updatePodInCache(oldObj, newObj interface{}) {
func (sched *Scheduler) updateAssignedPodInCache(oldPod, newPod *v1.Pod) {
start := time.Now()
defer metrics.EventHandlingLatency.WithLabelValues(framework.EventAssignedPodUpdate.Label()).Observe(metrics.SinceInSeconds(start))
logger := sched.logger
oldPod, ok := oldObj.(*v1.Pod)
if !ok {
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert oldObj to *v1.Pod", "oldObj", oldObj)
return
}
newPod, ok := newObj.(*v1.Pod)
if !ok {
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert newObj to *v1.Pod", "newObj", newObj)
return
}
if sched.APIDispatcher != nil {
// If the API dispatcher is available, sync the new pod with the details.
@@ -331,26 +388,11 @@ func (sched *Scheduler) updatePodInCache(oldObj, newObj interface{}) {
}
}
func (sched *Scheduler) deletePodFromCache(obj interface{}) {
func (sched *Scheduler) deleteAssignedPodFromCache(pod *v1.Pod) {
start := time.Now()
defer metrics.EventHandlingLatency.WithLabelValues(framework.EventAssignedPodDelete.Label()).Observe(metrics.SinceInSeconds(start))
logger := sched.logger
var pod *v1.Pod
switch t := obj.(type) {
case *v1.Pod:
pod = t
case cache.DeletedFinalStateUnknown:
var ok bool
pod, ok = t.Obj.(*v1.Pod)
if !ok {
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert to *v1.Pod", "obj", t.Obj)
return
}
default:
utilruntime.HandleErrorWithLogger(logger, nil, "Unable to handle object", "objType", fmt.Sprintf("%T", obj), "obj", obj)
return
}
logger.V(3).Info("Delete event for scheduled pod", "pod", klog.KObj(pod))
if err := sched.Cache.RemovePod(logger, pod); err != nil {
@@ -405,64 +447,12 @@ func addAllEventHandlers(
)
logger := sched.logger
// scheduled pod cache
if handlerRegistration, err = informerFactory.Core().V1().Pods().Informer().AddEventHandler(
cache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
switch t := obj.(type) {
case *v1.Pod:
return assignedPod(t)
case cache.DeletedFinalStateUnknown:
if _, ok := t.Obj.(*v1.Pod); ok {
// The carried object may be stale, so we don't use it to check if
// it's assigned or not. Attempting to cleanup anyways.
return true
}
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert to *v1.Pod", "obj", t.Obj)
return false
default:
utilruntime.HandleErrorWithLogger(logger, nil, "Unable to handle object", "objType", fmt.Sprintf("%T", obj), "obj", obj)
return false
}
},
Handler: cache.ResourceEventHandlerFuncs{
AddFunc: sched.addPodToCache,
UpdateFunc: sched.updatePodInCache,
DeleteFunc: sched.deletePodFromCache,
},
},
); err != nil {
return err
}
handlers = append(handlers, handlerRegistration)
// unscheduled pod queue
if handlerRegistration, err = informerFactory.Core().V1().Pods().Informer().AddEventHandler(
cache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
switch t := obj.(type) {
case *v1.Pod:
return !assignedPod(t) && responsibleForPod(t, sched.Profiles)
case cache.DeletedFinalStateUnknown:
if pod, ok := t.Obj.(*v1.Pod); ok {
// The carried object may be stale, so we don't use it to check if
// it's assigned or not.
return responsibleForPod(pod, sched.Profiles)
}
utilruntime.HandleErrorWithLogger(logger, nil, "Cannot convert to *v1.Pod", "obj", t.Obj)
return false
default:
utilruntime.HandleErrorWithLogger(logger, nil, "Unable to handle object", "objType", fmt.Sprintf("%T", obj), "obj", obj)
return false
}
},
Handler: cache.ResourceEventHandlerFuncs{
AddFunc: sched.addPodToSchedulingQueue,
UpdateFunc: sched.updatePodInSchedulingQueue,
DeleteFunc: sched.deletePodFromSchedulingQueue,
},
},
); err != nil {
if handlerRegistration, err = informerFactory.Core().V1().Pods().Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: sched.addPod,
UpdateFunc: sched.updatePod,
DeleteFunc: sched.deletePod,
}); err != nil {
return err
}
handlers = append(handlers, handlerRegistration)

View File

@@ -41,6 +41,7 @@ import (
dyfake "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/cache"
featuregatetesting "k8s.io/component-base/featuregate/testing"
resourceslicetracker "k8s.io/dynamic-resource-allocation/resourceslice/tracker"
"k8s.io/klog/v2"
@@ -52,13 +53,17 @@ import (
internalqueue "k8s.io/kubernetes/pkg/scheduler/backend/queue"
"k8s.io/kubernetes/pkg/scheduler/framework"
apicalls "k8s.io/kubernetes/pkg/scheduler/framework/api_calls"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultbinder"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/queuesort"
frameworkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
"k8s.io/kubernetes/pkg/scheduler/metrics"
"k8s.io/kubernetes/pkg/scheduler/profile"
st "k8s.io/kubernetes/pkg/scheduler/testing"
tf "k8s.io/kubernetes/pkg/scheduler/testing/framework"
"k8s.io/kubernetes/pkg/scheduler/util"
"k8s.io/kubernetes/pkg/scheduler/util/assumecache"
)
@@ -127,7 +132,7 @@ func TestEventHandlers_MoveToActiveOnNominatedNodeUpdate(t *testing.T) {
{
name: "Removal of a pod that had nominated node name should trigger rescheduling of lower priority pods",
updateFunc: func(s *Scheduler) {
s.deletePodFromSchedulingQueue(medNominatedPriorityPod)
s.deletePodFromSchedulingQueue(medNominatedPriorityPod, false)
},
wantInActiveOrBackoff: sets.New(lowPriorityPod.Name, medPriorityPod.Name),
},
@@ -222,24 +227,24 @@ func newDefaultQueueSort() fwk.LessFunc {
return sort.Less
}
func TestUpdatePodInCache(t *testing.T) {
func TestUpdateAssignedPodInCache(t *testing.T) {
ttl := 10 * time.Second
nodeName := "node"
tests := []struct {
name string
oldObj interface{}
newObj interface{}
oldPod *v1.Pod
newPod *v1.Pod
}{
{
name: "pod updated with the same UID",
oldObj: withPodName(podWithPort("oldUID", nodeName, 80), "pod"),
newObj: withPodName(podWithPort("oldUID", nodeName, 8080), "pod"),
oldPod: withPodName(podWithPort("oldUID", nodeName, 80), "pod"),
newPod: withPodName(podWithPort("oldUID", nodeName, 8080), "pod"),
},
{
name: "pod updated with different UIDs",
oldObj: withPodName(podWithPort("oldUID", nodeName, 80), "pod"),
newObj: withPodName(podWithPort("newUID", nodeName, 8080), "pod"),
oldPod: withPodName(podWithPort("oldUID", nodeName, 80), "pod"),
newPod: withPodName(podWithPort("newUID", nodeName, 8080), "pod"),
},
}
for _, tt := range tests {
@@ -252,20 +257,20 @@ func TestUpdatePodInCache(t *testing.T) {
SchedulingQueue: internalqueue.NewTestQueue(ctx, nil),
logger: logger,
}
sched.addPodToCache(tt.oldObj)
sched.updatePodInCache(tt.oldObj, tt.newObj)
sched.addAssignedPodToCache(tt.oldPod)
sched.updateAssignedPodInCache(tt.oldPod, tt.newPod)
if tt.oldObj.(*v1.Pod).UID != tt.newObj.(*v1.Pod).UID {
if pod, err := sched.Cache.GetPod(tt.oldObj.(*v1.Pod)); err == nil {
if tt.oldPod.UID != tt.newPod.UID {
if pod, err := sched.Cache.GetPod(tt.oldPod); err == nil {
t.Errorf("Get pod UID %v from cache but it should not happen", pod.UID)
}
}
pod, err := sched.Cache.GetPod(tt.newObj.(*v1.Pod))
pod, err := sched.Cache.GetPod(tt.newPod)
if err != nil {
t.Errorf("Failed to get pod from scheduler: %v", err)
}
if pod.UID != tt.newObj.(*v1.Pod).UID {
t.Errorf("Want pod UID %v, got %v", tt.newObj.(*v1.Pod).UID, pod.UID)
if pod.UID != tt.newPod.UID {
t.Errorf("Want pod UID %v, got %v", tt.newPod.UID, pod.UID)
}
})
}
@@ -674,3 +679,303 @@ func TestAdmissionCheck(t *testing.T) {
})
}
}
func TestAddPod(t *testing.T) {
tests := []struct {
name string
pod *v1.Pod
expectInQueue bool
expectInCache bool
}{
{
name: "add unscheduled pod",
pod: st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").SchedulerName("supported-scheduler").Obj(),
expectInQueue: true,
},
{
name: "add unscheduled pod with other scheduler name",
pod: st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").SchedulerName("other-scheduler").Obj(),
},
{
name: "add scheduled pod",
pod: st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").Node("node1").Obj(),
expectInCache: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger, ctx := ktesting.NewTestContext(t)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
sched := &Scheduler{
Cache: internalcache.New(ctx, 0, nil),
SchedulingQueue: internalqueue.NewTestQueue(ctx, nil),
logger: logger,
Profiles: profile.Map{
"supported-scheduler": nil,
},
}
sched.addPod(tt.pod)
_, ok := sched.SchedulingQueue.GetPod(tt.pod.Name, tt.pod.Namespace)
if tt.expectInQueue && !ok {
t.Errorf("Expected pod to be in scheduling queue")
} else if !tt.expectInQueue && ok {
t.Errorf("Expected pod not to be in scheduling queue")
}
_, err := sched.Cache.GetPod(tt.pod)
if tt.expectInCache && err != nil {
t.Errorf("Expected pod to be in cache: %v", err)
} else if !tt.expectInCache && err == nil {
t.Errorf("Expected pod not to be in cache")
}
})
}
}
func TestUpdatePod(t *testing.T) {
pod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").SchedulerName("supported-scheduler").Obj()
updatedPod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").Labels(map[string]string{"foo": "bar"}).ResourceVersion("2").SchedulerName("supported-scheduler").Obj()
otherPod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").SchedulerName("other-scheduler").Obj()
updatedOtherPod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").Labels(map[string]string{"foo": "bar"}).ResourceVersion("2").SchedulerName("other-scheduler").Obj()
scheduledPod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").Node("node1").SchedulerName("supported-scheduler").Obj()
updatedScheduledPod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").Labels(map[string]string{"foo": "bar"}).ResourceVersion("2").Node("node1").SchedulerName("supported-scheduler").Obj()
otherScheduledPod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").Node("node1").SchedulerName("other-scheduler").Obj()
updatedOtherScheduledPod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").Labels(map[string]string{"foo": "bar"}).ResourceVersion("2").Node("node1").SchedulerName("other-scheduler").Obj()
scheduledPodOtherNode := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").Node("node2").SchedulerName("supported-scheduler").Obj()
tests := []struct {
name string
oldPod *v1.Pod
assumedPod *v1.Pod
newPod *v1.Pod
expectInQueue *v1.Pod
expectInCache *v1.Pod
}{
{
name: "update unscheduled pod",
oldPod: pod,
newPod: updatedPod,
expectInQueue: updatedPod,
},
{
name: "update unscheduled pod with other scheduler name",
oldPod: otherPod,
newPod: updatedOtherPod,
},
{
name: "update assumed pod",
oldPod: pod,
assumedPod: scheduledPod,
newPod: updatedPod,
expectInCache: scheduledPod,
},
{
name: "update scheduled pod",
oldPod: scheduledPod,
newPod: updatedScheduledPod,
expectInCache: updatedScheduledPod,
},
{
name: "update scheduled pod with other scheduler name",
oldPod: otherScheduledPod,
newPod: updatedOtherScheduledPod,
expectInCache: updatedOtherScheduledPod,
},
{
name: "bind unscheduled pod",
oldPod: pod,
newPod: scheduledPod,
expectInCache: scheduledPod,
},
{
name: "bind unscheduled pod with other scheduler name",
oldPod: pod,
newPod: scheduledPod,
expectInCache: scheduledPod,
},
{
name: "bind assumed pod",
oldPod: pod,
assumedPod: scheduledPod,
newPod: scheduledPod,
expectInCache: scheduledPod,
},
{
name: "bind assumed pod to a different node",
oldPod: pod,
assumedPod: scheduledPod,
newPod: scheduledPodOtherNode,
expectInCache: scheduledPodOtherNode,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger, ctx := ktesting.NewTestContext(t)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
registerPluginFuncs := []tf.RegisterPluginFunc{
tf.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
tf.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
}
waitingPods := frameworkruntime.NewWaitingPodsMap()
schedFramework, err := tf.NewFramework(
ctx,
registerPluginFuncs,
"supported-scheduler",
frameworkruntime.WithWaitingPods(waitingPods),
)
if err != nil {
t.Fatalf("Failed to create framework: %v", err)
}
sched := &Scheduler{
Cache: internalcache.New(ctx, 0, nil),
SchedulingQueue: internalqueue.NewTestQueue(ctx, nil),
logger: logger,
Profiles: profile.Map{
"supported-scheduler": schedFramework,
},
}
if tt.assumedPod != nil {
err := sched.Cache.AssumePod(logger, tt.assumedPod)
if err != nil {
t.Fatalf("Failed to assume pod: %v", err)
}
} else {
sched.addPod(tt.oldPod)
}
sched.updatePod(tt.oldPod, tt.newPod)
qPod, ok := sched.SchedulingQueue.GetPod(tt.newPod.Name, tt.newPod.Namespace)
if tt.expectInQueue != nil {
if !ok {
t.Errorf("Expected pod to be in scheduling queue")
} else if diff := cmp.Diff(tt.expectInQueue, qPod.Pod); diff != "" {
t.Errorf("Unexpected pod after update (-want,+got):\n%s", diff)
}
} else if ok {
t.Errorf("Expected pod not to be in scheduling queue")
}
pod, err := sched.Cache.GetPod(tt.newPod)
if tt.expectInCache != nil {
if err != nil {
t.Errorf("Expected pod to be in cache: %v", err)
} else if diff := cmp.Diff(tt.expectInCache, pod); diff != "" {
t.Errorf("Unexpected pod after update (-want,+got):\n%s", diff)
}
} else if err == nil {
t.Errorf("Expected pod not to be in cache")
}
})
}
}
func TestDeletePod(t *testing.T) {
pod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").SchedulerName("supported-scheduler").Obj()
otherPod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").SchedulerName("other-scheduler").Obj()
scheduledPod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").Node("node1").SchedulerName("supported-scheduler").Obj()
otherScheduledPod := st.MakePod().Name("pod1").Namespace("ns1").UID("pod1").Node("node1").SchedulerName("other-scheduler").Obj()
tests := []struct {
name string
initialPod *v1.Pod
assumed bool
waitingOnPermit bool
podToDelete any
}{
{
name: "delete unscheduled pod",
initialPod: pod,
podToDelete: pod,
},
{
name: "delete unscheduled pod with other scheduler name",
initialPod: otherPod,
podToDelete: otherPod,
},
{
name: "delete unscheduled pod with unknown state",
initialPod: pod,
podToDelete: cache.DeletedFinalStateUnknown{Obj: pod},
},
{
name: "delete scheduled pod",
initialPod: scheduledPod,
podToDelete: scheduledPod,
},
{
name: "delete scheduled pod with other scheduler name",
initialPod: otherScheduledPod,
podToDelete: otherScheduledPod,
},
{
name: "delete scheduled pod with unknown state",
initialPod: scheduledPod,
podToDelete: cache.DeletedFinalStateUnknown{Obj: scheduledPod},
},
{
name: "delete scheduled pod with unknown older state",
initialPod: scheduledPod,
podToDelete: cache.DeletedFinalStateUnknown{Obj: pod},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger, ctx := ktesting.NewTestContext(t)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
registerPluginFuncs := []tf.RegisterPluginFunc{
tf.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
tf.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
}
waitingPods := frameworkruntime.NewWaitingPodsMap()
schedFramework, err := tf.NewFramework(
ctx,
registerPluginFuncs,
"supported-scheduler",
frameworkruntime.WithWaitingPods(waitingPods),
)
if err != nil {
t.Fatalf("Failed to create framework: %v", err)
}
sched := &Scheduler{
Cache: internalcache.New(ctx, 0, nil),
SchedulingQueue: internalqueue.NewTestQueue(ctx, nil),
logger: logger,
Profiles: profile.Map{
"supported-scheduler": schedFramework,
},
}
if tt.assumed {
err := sched.Cache.AssumePod(logger, tt.initialPod)
if err != nil {
t.Fatalf("Failed to assume pod: %v", err)
}
} else {
sched.addPod(tt.initialPod)
}
sched.deletePod(tt.podToDelete)
_, err = sched.Cache.GetPod(tt.initialPod)
if err == nil {
t.Errorf("Unexpected pod in cache after removal")
}
_, ok := sched.SchedulingQueue.GetPod(tt.initialPod.Name, tt.initialPod.Namespace)
if ok {
t.Errorf("Unexpected pod in scheduling queue after removal")
}
})
}
}

View File

@@ -2097,7 +2097,7 @@ func TestSchedulerBinding(t *testing.T) {
}
}
func TestUpdatePod(t *testing.T) {
func TestUpdatePodStatus(t *testing.T) {
tests := []struct {
name string
currentPodConditions []v1.PodCondition

View File

@@ -562,6 +562,12 @@ func (p *PodWrapper) SchedulingGates(gates []string) *PodWrapper {
return p
}
// ResourceVersion sets the inner pod's ResurceVersion.
func (p *PodWrapper) ResourceVersion(version string) *PodWrapper {
p.ObjectMeta.ResourceVersion = version
return p
}
// PodAffinityKind represents different kinds of PodAffinity.
type PodAffinityKind int