Skip to content

Commit ecead7c

Browse files
authored
fix reservation on mutil-scheduler (#431)
Signed-off-by: saintube <saintube@foxmail.com>
1 parent a89cd98 commit ecead7c

File tree

4 files changed

+175
-19
lines changed

4 files changed

+175
-19
lines changed

pkg/scheduler/eventhandlers/reservation_handler.go

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"k8s.io/client-go/tools/cache"
2121
"k8s.io/klog/v2"
2222
"k8s.io/kubernetes/pkg/scheduler"
23+
"k8s.io/kubernetes/pkg/scheduler/profile"
2324

2425
schedulingv1alpha1 "github.com/koordinator-sh/koordinator/apis/scheduling/v1alpha1"
2526
"github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext"
@@ -66,12 +67,11 @@ func AddScheduleEventHandler(sched *scheduler.Scheduler, internalHandler Schedul
6667
FilterFunc: func(obj interface{}) bool {
6768
switch t := obj.(type) {
6869
case *schedulingv1alpha1.Reservation:
69-
// scheduler is always responsible for schedulingv1alpha1.reservation object
70-
return !reservation.IsReservationScheduled(t) && !reservation.IsReservationFailed(t)
70+
return !reservation.IsReservationScheduled(t) && !reservation.IsReservationFailed(t) && isResponsibleForReservation(sched.Profiles, t)
7171
case cache.DeletedFinalStateUnknown:
72-
if _, ok := t.Obj.(*schedulingv1alpha1.Reservation); ok {
72+
if r, ok := t.Obj.(*schedulingv1alpha1.Reservation); ok {
7373
// DeletedFinalStateUnknown object can be stale, so just try to cleanup without check.
74-
return true
74+
return isResponsibleForReservation(sched.Profiles, r)
7575
}
7676
klog.Errorf("unable to convert object %T to *schedulingv1alpha1.Reservation in %T", t.Obj, sched)
7777
return false
@@ -304,28 +304,29 @@ func handleExpiredReservation(sched *scheduler.Scheduler, internalHandler Schedu
304304
return
305305
}
306306
reservePod := reservation.NewReservePod(r)
307-
if len(reservation.GetReservationNodeName(r)) > 0 {
308-
err := internalHandler.GetCache().RemovePod(reservePod)
307+
308+
// in case the pod has expired before scheduling cache initialized, or the pod just finished scheduling cycle and
309+
// deleted, both we need to check if pod is cached
310+
_, err = internalHandler.GetCache().GetPod(reservePod)
311+
if err == nil {
312+
err = internalHandler.GetCache().RemovePod(reservePod)
309313
if err != nil {
310-
klog.Errorf("failed to remove reserve pod in scheduler cache, reservation %v, err: %v", klog.KObj(r), err)
314+
klog.Errorf("failed to remove expired reserve pod in scheduler cache, reservation %v, err: %s",
315+
klog.KObj(r), err)
311316
}
312317
internalHandler.MoveAllToActiveOrBackoffQueue(assignedPodDelete)
313-
} else { // otherwise, try dequeue the reserve pod from the scheduling queue
314-
// in case the pod just finished scheduling cycle and deleted, also check if pod is cached
315-
_, err := internalHandler.GetCache().GetPod(reservePod)
316-
if err == nil {
317-
klog.V(5).InfoS("reserve pod is just scheduled and deleted, remove it in cache", "reservation", klog.KObj(r))
318-
err = internalHandler.GetCache().RemovePod(reservePod)
319-
if err != nil {
320-
klog.Errorf("failed to remove reserve pod in scheduler cache, reservation %v, err: %v", klog.KObj(r), err)
321-
}
322-
internalHandler.MoveAllToActiveOrBackoffQueue(assignedPodDelete)
323-
}
318+
}
324319

320+
if len(reservation.GetReservationNodeName(r)) <= 0 {
321+
// pod is unscheduled, try dequeue the reserve pod from the scheduling queue
325322
err = internalHandler.GetQueue().Delete(reservePod)
326323
if err != nil {
327-
klog.Errorf("failed to delete reserve pod in scheduling queue, reservation %v, err: %v", klog.KObj(r), err)
324+
klog.Errorf("failed to delete expired reserve pod in scheduling queue, reservation %v, err: %v", klog.KObj(r), err)
328325
}
329326
}
330327
klog.V(4).InfoS("handle expired reservation", "reservation", klog.KObj(r), "phase", r.Status.Phase)
331328
}
329+
330+
func isResponsibleForReservation(profiles profile.Map, r *schedulingv1alpha1.Reservation) bool {
331+
return profiles.HandlesSchedulerName(reservation.GetReservationSchedulerName(r))
332+
}

pkg/scheduler/eventhandlers/reservation_handler_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@ import (
2020
"testing"
2121
"time"
2222

23+
"github.com/stretchr/testify/assert"
2324
corev1 "k8s.io/api/core/v1"
2425
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2526
"k8s.io/kubernetes/pkg/scheduler"
27+
"k8s.io/kubernetes/pkg/scheduler/framework"
28+
"k8s.io/kubernetes/pkg/scheduler/profile"
2629

2730
schedulingv1alpha1 "github.com/koordinator-sh/koordinator/apis/scheduling/v1alpha1"
2831
koordfake "github.com/koordinator-sh/koordinator/pkg/client/clientset/versioned/fake"
@@ -440,6 +443,50 @@ func Test_updateReservationInSchedulingQueue(t *testing.T) {
440443
},
441444
},
442445
},
446+
{
447+
name: "update new reservation successfully",
448+
args: args{
449+
internalHandler: &fakeSchedulerInternalHandler{},
450+
oldObj: &schedulingv1alpha1.Reservation{
451+
ObjectMeta: metav1.ObjectMeta{
452+
Name: "r-0",
453+
UID: "456",
454+
ResourceVersion: "0",
455+
},
456+
Spec: schedulingv1alpha1.ReservationSpec{
457+
Template: &corev1.PodTemplateSpec{},
458+
Owners: []schedulingv1alpha1.ReservationOwner{
459+
{
460+
Object: &corev1.ObjectReference{
461+
Kind: "Pod",
462+
Name: "pod-0",
463+
},
464+
},
465+
},
466+
Expires: &metav1.Time{Time: now.Add(30 * time.Minute)},
467+
},
468+
},
469+
newObj: &schedulingv1alpha1.Reservation{
470+
ObjectMeta: metav1.ObjectMeta{
471+
Name: "r-0",
472+
UID: "456",
473+
ResourceVersion: "1",
474+
},
475+
Spec: schedulingv1alpha1.ReservationSpec{
476+
Template: &corev1.PodTemplateSpec{},
477+
Owners: []schedulingv1alpha1.ReservationOwner{
478+
{
479+
Object: &corev1.ObjectReference{
480+
Kind: "Pod",
481+
Name: "pod-0",
482+
},
483+
},
484+
},
485+
Expires: &metav1.Time{Time: now.Add(30 * time.Minute)},
486+
},
487+
},
488+
},
489+
},
443490
}
444491
for _, tt := range tests {
445492
t.Run(tt.name, func(t *testing.T) {
@@ -597,3 +644,57 @@ func Test_handleExpiredReservation(t *testing.T) {
597644
})
598645
}
599646
}
647+
648+
var _ framework.Framework = &fakeFramework{}
649+
650+
type fakeFramework struct {
651+
framework.Framework
652+
}
653+
654+
func Test_isResponsibleForReservation(t *testing.T) {
655+
type args struct {
656+
profiles profile.Map
657+
r *schedulingv1alpha1.Reservation
658+
}
659+
tests := []struct {
660+
name string
661+
args args
662+
want bool
663+
}{
664+
{
665+
name: "not responsible when profile is empty",
666+
args: args{
667+
profiles: profile.Map{},
668+
r: &schedulingv1alpha1.Reservation{},
669+
},
670+
want: false,
671+
},
672+
{
673+
name: "responsible when scheduler name matched the profile",
674+
args: args{
675+
profiles: profile.Map{
676+
"test-scheduler": &fakeFramework{},
677+
},
678+
r: &schedulingv1alpha1.Reservation{
679+
ObjectMeta: metav1.ObjectMeta{
680+
Name: "test-reserve-sample",
681+
},
682+
Spec: schedulingv1alpha1.ReservationSpec{
683+
Template: &corev1.PodTemplateSpec{
684+
Spec: corev1.PodSpec{
685+
SchedulerName: "test-scheduler",
686+
},
687+
},
688+
},
689+
},
690+
},
691+
want: true,
692+
},
693+
}
694+
for _, tt := range tests {
695+
t.Run(tt.name, func(t *testing.T) {
696+
got := isResponsibleForReservation(tt.args.profiles, tt.args.r)
697+
assert.Equal(t, tt.want, got)
698+
})
699+
}
700+
}

pkg/scheduler/plugins/reservation/rpod.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,10 @@ func GetReservePodNodeName(pod *corev1.Pod) string {
8787
func GetReservationNameFromReservePod(pod *corev1.Pod) string {
8888
return pod.Annotations[AnnotationReservationName]
8989
}
90+
91+
func GetReservationSchedulerName(r *schedulingv1alpha1.Reservation) string {
92+
if r == nil || r.Spec.Template == nil || len(r.Spec.Template.Spec.SchedulerName) <= 0 {
93+
return corev1.DefaultSchedulerName
94+
}
95+
return r.Spec.Template.Spec.SchedulerName
96+
}

pkg/scheduler/plugins/reservation/utils_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,50 @@ func Test_matchReservationOwners(t *testing.T) {
313313
})
314314
}
315315
}
316+
317+
func TestGetReservationSchedulerName(t *testing.T) {
318+
tests := []struct {
319+
name string
320+
arg *schedulingv1alpha1.Reservation
321+
want string
322+
}{
323+
{
324+
name: "empty reservation",
325+
arg: nil,
326+
want: corev1.DefaultSchedulerName,
327+
},
328+
{
329+
name: "empty template",
330+
arg: &schedulingv1alpha1.Reservation{},
331+
want: corev1.DefaultSchedulerName,
332+
},
333+
{
334+
name: "empty scheduler name",
335+
arg: &schedulingv1alpha1.Reservation{
336+
Spec: schedulingv1alpha1.ReservationSpec{
337+
Template: &corev1.PodTemplateSpec{},
338+
},
339+
},
340+
want: corev1.DefaultSchedulerName,
341+
},
342+
{
343+
name: "get scheduler name successfully",
344+
arg: &schedulingv1alpha1.Reservation{
345+
Spec: schedulingv1alpha1.ReservationSpec{
346+
Template: &corev1.PodTemplateSpec{
347+
Spec: corev1.PodSpec{
348+
SchedulerName: "test-scheduler",
349+
},
350+
},
351+
},
352+
},
353+
want: "test-scheduler",
354+
},
355+
}
356+
for _, tt := range tests {
357+
t.Run(tt.name, func(t *testing.T) {
358+
got := GetReservationSchedulerName(tt.arg)
359+
assert.Equal(t, tt.want, got)
360+
})
361+
}
362+
}

0 commit comments

Comments
 (0)