2017-10-24 14:14:29 -04:00
/ *
Copyright 2017 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2018-09-21 19:18:48 -04:00
package queue
2017-10-24 14:14:29 -04:00
import (
2023-07-17 18:53:07 -04:00
"container/list"
2021-03-04 07:30:24 -05:00
"context"
2018-09-14 19:59:54 -04:00
"fmt"
2021-09-18 22:09:06 -04:00
"math"
2019-09-29 22:05:50 -04:00
"strings"
2017-11-28 04:40:35 -05:00
"sync"
2017-10-24 14:14:29 -04:00
"testing"
2019-01-15 23:08:19 -05:00
"time"
2017-10-24 14:14:29 -04:00
2021-03-11 15:31:33 -05:00
"github.com/google/go-cmp/cmp"
2021-06-04 14:04:44 -04:00
"github.com/google/go-cmp/cmp/cmpopts"
2021-03-09 06:42:20 -05:00
v1 "k8s.io/api/core/v1"
2017-10-24 14:14:29 -04:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2021-09-18 22:09:06 -04:00
"k8s.io/apimachinery/pkg/runtime"
2018-11-27 19:46:24 -05:00
"k8s.io/apimachinery/pkg/types"
2021-01-29 01:29:10 -05:00
"k8s.io/apimachinery/pkg/util/sets"
2023-07-17 18:53:07 -04:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2021-06-04 14:04:44 -04:00
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
2023-07-17 18:53:07 -04:00
featuregatetesting "k8s.io/component-base/featuregate/testing"
2019-09-29 22:05:50 -04:00
"k8s.io/component-base/metrics/testutil"
2023-03-22 07:48:04 -04:00
"k8s.io/klog/v2"
"k8s.io/klog/v2/ktesting"
2018-11-27 19:46:24 -05:00
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
2023-07-17 18:53:07 -04:00
"k8s.io/kubernetes/pkg/features"
2020-10-09 10:41:44 -04:00
"k8s.io/kubernetes/pkg/scheduler/framework"
2020-02-13 11:08:46 -05:00
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/queuesort"
2019-03-20 02:53:33 -04:00
"k8s.io/kubernetes/pkg/scheduler/metrics"
2022-04-18 21:06:58 -04:00
st "k8s.io/kubernetes/pkg/scheduler/testing"
2018-01-03 21:12:18 -05:00
"k8s.io/kubernetes/pkg/scheduler/util"
2021-09-17 05:48:22 -04:00
testingclock "k8s.io/utils/clock/testing"
2017-10-24 14:14:29 -04:00
)
2019-10-07 13:29:53 -04:00
const queueMetricMetadata = `
2021-11-09 09:08:18 -05:00
# HELP scheduler_queue_incoming_pods_total [ STABLE ] Number of pods added to scheduling queues by event and queue type .
2019-10-07 13:29:53 -04:00
# TYPE scheduler_queue_incoming_pods_total counter
`
2021-03-16 18:08:19 -04:00
var (
TestEvent = framework . ClusterEvent { Resource : "test" }
NodeAllEvent = framework . ClusterEvent { Resource : framework . Node , ActionType : framework . All }
EmptyEvent = framework . ClusterEvent { }
2022-04-01 15:21:10 -04:00
lowPriority , midPriority , highPriority = int32 ( 0 ) , int32 ( 100 ) , int32 ( 1000 )
mediumPriority = ( lowPriority + highPriority ) / 2
2022-10-12 10:11:04 -04:00
highPriorityPodInfo = mustNewPodInfo (
2022-04-18 21:06:58 -04:00
st . MakePod ( ) . Name ( "hpp" ) . Namespace ( "ns1" ) . UID ( "hppns1" ) . Priority ( highPriority ) . Obj ( ) ,
)
2022-10-12 10:11:04 -04:00
highPriNominatedPodInfo = mustNewPodInfo (
2022-04-18 21:06:58 -04:00
st . MakePod ( ) . Name ( "hpp" ) . Namespace ( "ns1" ) . UID ( "hppns1" ) . Priority ( highPriority ) . NominatedNodeName ( "node1" ) . Obj ( ) ,
)
2022-10-12 10:11:04 -04:00
medPriorityPodInfo = mustNewPodInfo (
2022-04-18 21:06:58 -04:00
st . MakePod ( ) . Name ( "mpp" ) . Namespace ( "ns2" ) . UID ( "mppns2" ) . Annotation ( "annot2" , "val2" ) . Priority ( mediumPriority ) . NominatedNodeName ( "node1" ) . Obj ( ) ,
)
2022-10-12 10:11:04 -04:00
unschedulablePodInfo = mustNewPodInfo (
2022-04-18 21:06:58 -04:00
st . MakePod ( ) . Name ( "up" ) . Namespace ( "ns1" ) . UID ( "upns1" ) . Annotation ( "annot2" , "val2" ) . Priority ( lowPriority ) . NominatedNodeName ( "node1" ) . Condition ( v1 . PodScheduled , v1 . ConditionFalse , v1 . PodReasonUnschedulable ) . Obj ( ) ,
)
2022-10-12 10:11:04 -04:00
nonExistentPodInfo = mustNewPodInfo (
2022-04-18 21:06:58 -04:00
st . MakePod ( ) . Name ( "ne" ) . Namespace ( "ns1" ) . UID ( "nens1" ) . Obj ( ) ,
)
2022-10-12 10:11:04 -04:00
scheduledPodInfo = mustNewPodInfo (
2022-04-18 21:06:58 -04:00
st . MakePod ( ) . Name ( "sp" ) . Namespace ( "ns1" ) . UID ( "spns1" ) . Node ( "foo" ) . Obj ( ) ,
)
2023-03-08 16:18:36 -05:00
nominatorCmpOpts = [ ] cmp . Option {
cmp . AllowUnexported ( nominator { } ) ,
cmpopts . IgnoreFields ( nominator { } , "podLister" , "lock" ) ,
}
2023-06-08 00:54:30 -04:00
2023-10-19 07:02:11 -04:00
queueHintReturnQueue = func ( logger klog . Logger , pod * v1 . Pod , oldObj , newObj interface { } ) ( framework . QueueingHint , error ) {
return framework . Queue , nil
2023-06-08 00:54:30 -04:00
}
2023-10-19 07:02:11 -04:00
queueHintReturnSkip = func ( logger klog . Logger , pod * v1 . Pod , oldObj , newObj interface { } ) ( framework . QueueingHint , error ) {
2023-07-13 09:45:26 -04:00
return framework . QueueSkip , nil
2023-06-08 00:54:30 -04:00
}
2022-04-01 15:21:10 -04:00
)
2017-10-24 14:14:29 -04:00
2019-01-17 15:24:18 -05:00
func getUnschedulablePod ( p * PriorityQueue , pod * v1 . Pod ) * v1 . Pod {
2022-03-24 05:38:49 -04:00
pInfo := p . unschedulablePods . get ( pod )
2019-02-21 20:46:18 -05:00
if pInfo != nil {
2019-05-06 21:03:00 -04:00
return pInfo . Pod
2019-02-21 20:46:18 -05:00
}
return nil
2019-01-17 15:24:18 -05:00
}
2023-06-08 00:54:30 -04:00
// makeEmptyQueueingHintMapPerProfile initializes an empty QueueingHintMapPerProfile for "" profile name.
func makeEmptyQueueingHintMapPerProfile ( ) QueueingHintMapPerProfile {
m := make ( QueueingHintMapPerProfile )
m [ "" ] = make ( QueueingHintMap )
return m
}
2017-10-24 14:14:29 -04:00
func TestPriorityQueue_Add ( t * testing . T ) {
2021-06-05 14:30:17 -04:00
objs := [ ] runtime . Object { medPriorityPodInfo . Pod , unschedulablePodInfo . Pod , highPriorityPodInfo . Pod }
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , objs )
2023-03-22 07:48:04 -04:00
if err := q . Add ( logger , medPriorityPodInfo . Pod ) ; err != nil {
2018-12-14 19:31:08 -05:00
t . Errorf ( "add failed: %v" , err )
}
2023-03-22 07:48:04 -04:00
if err := q . Add ( logger , unschedulablePodInfo . Pod ) ; err != nil {
2018-12-14 19:31:08 -05:00
t . Errorf ( "add failed: %v" , err )
}
2023-03-22 07:48:04 -04:00
if err := q . Add ( logger , highPriorityPodInfo . Pod ) ; err != nil {
2018-12-14 19:31:08 -05:00
t . Errorf ( "add failed: %v" , err )
}
2021-06-05 14:30:17 -04:00
expectedNominatedPods := & nominator {
2018-12-18 02:41:53 -05:00
nominatedPodToNode : map [ types . UID ] string {
2021-02-22 09:00:23 -05:00
medPriorityPodInfo . Pod . UID : "node1" ,
unschedulablePodInfo . Pod . UID : "node1" ,
2018-12-18 02:41:53 -05:00
} ,
2021-02-22 09:00:23 -05:00
nominatedPods : map [ string ] [ ] * framework . PodInfo {
"node1" : { medPriorityPodInfo , unschedulablePodInfo } ,
2018-12-18 02:41:53 -05:00
} ,
2018-02-08 21:19:31 -05:00
}
2023-03-08 16:18:36 -05:00
if diff := cmp . Diff ( q . nominator , expectedNominatedPods , nominatorCmpOpts ... ) ; diff != "" {
2021-06-04 14:04:44 -04:00
t . Errorf ( "Unexpected diff after adding pods (-want, +got):\n%s" , diff )
2018-02-08 21:19:31 -05:00
}
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != highPriorityPodInfo . Pod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , highPriorityPodInfo . Pod . Name , p . Pod . Name )
2017-10-24 14:14:29 -04:00
}
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != medPriorityPodInfo . Pod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , medPriorityPodInfo . Pod . Name , p . Pod . Name )
2017-10-24 14:14:29 -04:00
}
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != unschedulablePodInfo . Pod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , unschedulablePodInfo . Pod . Name , p . Pod . Name )
2017-10-24 14:14:29 -04:00
}
2023-03-08 16:18:36 -05:00
if len ( q . nominator . nominatedPods [ "node1" ] ) != 2 {
t . Errorf ( "Expected medPriorityPodInfo and unschedulablePodInfo to be still present in nomindatePods: %v" , q . nominator . nominatedPods [ "node1" ] )
2018-02-08 21:19:31 -05:00
}
}
2020-02-13 11:08:46 -05:00
func newDefaultQueueSort ( ) framework . LessFunc {
sort := & queuesort . PrioritySort { }
return sort . Less
2020-01-07 22:22:51 -05:00
}
2019-05-06 21:03:00 -04:00
func TestPriorityQueue_AddWithReversePriorityLessFunc ( t * testing . T ) {
2021-06-05 14:30:17 -04:00
objs := [ ] runtime . Object { medPriorityPodInfo . Pod , highPriorityPodInfo . Pod }
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , objs )
2023-03-22 07:48:04 -04:00
if err := q . Add ( logger , medPriorityPodInfo . Pod ) ; err != nil {
2019-05-06 21:03:00 -04:00
t . Errorf ( "add failed: %v" , err )
}
2023-03-22 07:48:04 -04:00
if err := q . Add ( logger , highPriorityPodInfo . Pod ) ; err != nil {
2019-05-06 21:03:00 -04:00
t . Errorf ( "add failed: %v" , err )
}
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != highPriorityPodInfo . Pod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , highPriorityPodInfo . Pod . Name , p . Pod . Name )
2019-05-06 21:03:00 -04:00
}
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != medPriorityPodInfo . Pod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , medPriorityPodInfo . Pod . Name , p . Pod . Name )
2020-01-15 15:26:22 -05:00
}
2019-05-06 21:03:00 -04:00
}
2023-09-04 09:48:45 -04:00
func listToValues ( l * list . List ) [ ] interface { } {
var values [ ] interface { }
2023-07-17 18:53:07 -04:00
for e := l . Front ( ) ; e != nil ; e = e . Next ( ) {
2023-09-04 09:48:45 -04:00
values = append ( values , e . Value )
2023-07-17 18:53:07 -04:00
}
2023-09-04 09:48:45 -04:00
return values
2023-07-17 18:53:07 -04:00
}
func Test_InFlightPods ( t * testing . T ) {
2023-09-28 22:59:22 -04:00
logger , _ := ktesting . NewTestContext ( t )
2023-07-17 18:53:07 -04:00
pod := st . MakePod ( ) . Name ( "targetpod" ) . UID ( "pod1" ) . Obj ( )
pod2 := st . MakePod ( ) . Name ( "targetpod2" ) . UID ( "pod2" ) . Obj ( )
pod3 := st . MakePod ( ) . Name ( "targetpod3" ) . UID ( "pod3" ) . Obj ( )
2023-09-05 14:21:40 -04:00
var poppedPod , poppedPod2 * framework . QueuedPodInfo
2023-07-17 18:53:07 -04:00
type action struct {
// ONLY ONE of the following should be set.
eventHappens * framework . ClusterEvent
podPopped * v1 . Pod
podEnqueued * framework . QueuedPodInfo
2023-09-05 14:21:40 -04:00
callback func ( t * testing . T , q * PriorityQueue )
}
2023-07-17 18:53:07 -04:00
tests := [ ] struct {
name string
queueingHintMap QueueingHintMapPerProfile
// initialPods is the initial Pods in the activeQ.
initialPods [ ] * v1 . Pod
actions [ ] action
2023-09-04 09:48:45 -04:00
wantInFlightPods [ ] * v1 . Pod
wantInFlightEvents [ ] interface { }
2023-07-17 18:53:07 -04:00
wantActiveQPodNames [ ] string
wantBackoffQPodNames [ ] string
wantUnschedPodPoolPodNames [ ] string
isSchedulingQueueHintEnabled bool
} {
{
2023-09-04 09:48:45 -04:00
name : "when SchedulingQueueHint is disabled, inFlightPods and inFlightEvents should be empty" ,
2023-07-17 18:53:07 -04:00
initialPods : [ ] * v1 . Pod { pod } ,
actions : [ ] action {
// This Pod shouldn't be added to inFlightPods because SchedulingQueueHint is disabled.
{ podPopped : pod } ,
2023-09-04 09:48:45 -04:00
// This event shouldn't be added to inFlightEvents because SchedulingQueueHint is disabled.
2023-07-17 18:53:07 -04:00
{ eventHappens : & PvAdd } ,
} ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : nil ,
wantInFlightEvents : nil ,
2023-07-17 18:53:07 -04:00
} ,
{
name : "when SchedulingQueueHint is disabled, which queue to enqueue Pod should be decided without SchedulingQueueHint" ,
initialPods : [ ] * v1 . Pod { pod } ,
actions : [ ] action {
{ podPopped : pod } ,
{ eventHappens : & AssignedPodAdd } ,
{ podEnqueued : newQueuedPodInfoForLookup ( pod , "fooPlugin1" ) } ,
} ,
wantBackoffQPodNames : [ ] string { "targetpod" } ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : nil ,
wantInFlightEvents : nil ,
2023-07-17 18:53:07 -04:00
queueingHintMap : QueueingHintMapPerProfile {
"" : {
// This hint fn tells that this event doesn't make a Pod schedulable.
// However, this QueueingHintFn will be ignored actually because SchedulingQueueHint is disabled.
AssignedPodAdd : {
{
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnSkip ,
2023-07-17 18:53:07 -04:00
} ,
} ,
} ,
} ,
} ,
{
2023-09-04 09:48:45 -04:00
name : "Pod is registered in inFlightPods when Pod is popped from activeQ" ,
2023-07-17 18:53:07 -04:00
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod } ,
actions : [ ] action {
2023-09-04 09:48:45 -04:00
// This won't be added to inFlightEvents because no inFlightPods at this point.
2023-07-17 18:53:07 -04:00
{ eventHappens : & PvcAdd } ,
{ podPopped : pod } ,
2023-09-04 09:48:45 -04:00
// This gets added for the pod.
2023-07-17 18:53:07 -04:00
{ eventHappens : & PvAdd } ,
} ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : [ ] * v1 . Pod { pod } ,
wantInFlightEvents : [ ] interface { } { pod , PvAdd } ,
2023-07-17 18:53:07 -04:00
} ,
{
2023-09-04 09:48:45 -04:00
name : "Pod, registered in inFlightPods, is enqueued back to activeQ" ,
2023-07-17 18:53:07 -04:00
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod , pod2 } ,
actions : [ ] action {
2023-09-04 09:48:45 -04:00
// This won't be added to inFlightEvents because no inFlightPods at this point.
2023-07-17 18:53:07 -04:00
{ eventHappens : & PvcAdd } ,
{ podPopped : pod } ,
{ eventHappens : & PvAdd } ,
{ podPopped : pod2 } ,
{ eventHappens : & NodeAdd } ,
{ podEnqueued : newQueuedPodInfoForLookup ( pod ) } ,
} ,
wantBackoffQPodNames : [ ] string { "targetpod" } ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : [ ] * v1 . Pod { pod2 } ,
wantInFlightEvents : [ ] interface { } { pod2 , NodeAdd } ,
2023-07-17 18:53:07 -04:00
} ,
{
2023-09-04 09:48:45 -04:00
name : "All Pods registered in inFlightPods are enqueued back to activeQ" ,
2023-07-17 18:53:07 -04:00
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod , pod2 } ,
actions : [ ] action {
2023-09-04 09:48:45 -04:00
// This won't be added to inFlightEvents because no inFlightPods at this point.
2023-07-17 18:53:07 -04:00
{ eventHappens : & PvcAdd } ,
{ podPopped : pod } ,
{ eventHappens : & PvAdd } ,
{ podPopped : pod2 } ,
{ eventHappens : & NodeAdd } ,
{ podEnqueued : newQueuedPodInfoForLookup ( pod ) } ,
{ eventHappens : & CSINodeUpdate } ,
{ podEnqueued : newQueuedPodInfoForLookup ( pod2 ) } ,
} ,
wantBackoffQPodNames : [ ] string { "targetpod" , "targetpod2" } ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : nil ,
wantInFlightEvents : nil ,
2023-07-17 18:53:07 -04:00
} ,
{
2023-09-04 09:48:45 -04:00
name : "One intermediate Pod registered in inFlightPods is enqueued back to activeQ" ,
2023-07-17 18:53:07 -04:00
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod , pod2 , pod3 } ,
actions : [ ] action {
2023-09-04 09:48:45 -04:00
// This won't be added to inFlightEvents because no inFlightPods at this point.
2023-07-17 18:53:07 -04:00
{ eventHappens : & PvcAdd } ,
{ podPopped : pod } ,
{ eventHappens : & PvAdd } ,
{ podPopped : pod2 } ,
{ eventHappens : & NodeAdd } ,
// This Pod won't be requeued again.
{ podPopped : pod3 } ,
{ eventHappens : & AssignedPodAdd } ,
{ podEnqueued : newQueuedPodInfoForLookup ( pod2 ) } ,
} ,
wantBackoffQPodNames : [ ] string { "targetpod2" } ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : [ ] * v1 . Pod { pod , pod3 } ,
wantInFlightEvents : [ ] interface { } { pod , PvAdd , NodeAdd , pod3 , AssignedPodAdd } ,
2023-07-17 18:53:07 -04:00
} ,
{
name : "events before popping Pod are ignored" ,
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod } ,
actions : [ ] action {
{ eventHappens : & WildCardEvent } ,
{ podPopped : pod } ,
{ eventHappens : & AssignedPodAdd } ,
{ podEnqueued : newQueuedPodInfoForLookup ( pod , "fooPlugin1" ) } ,
} ,
wantUnschedPodPoolPodNames : [ ] string { "targetpod" } ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : nil ,
wantInFlightEvents : nil ,
2023-07-17 18:53:07 -04:00
queueingHintMap : QueueingHintMapPerProfile {
"" : {
// fooPlugin1 has a queueing hint function for AssignedPodAdd,
// but hint fn tells that this event doesn't make a Pod scheudlable.
AssignedPodAdd : {
{
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnSkip ,
2023-07-17 18:53:07 -04:00
} ,
} ,
} ,
} ,
} ,
{
name : "pod is enqueued to backoff if no failed plugin" ,
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod } ,
actions : [ ] action {
{ podPopped : pod } ,
{ eventHappens : & AssignedPodAdd } ,
{ podEnqueued : newQueuedPodInfoForLookup ( pod ) } ,
} ,
wantBackoffQPodNames : [ ] string { "targetpod" } ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : nil ,
wantInFlightEvents : nil ,
2023-07-17 18:53:07 -04:00
queueingHintMap : QueueingHintMapPerProfile {
"" : {
// It will be ignored because no failed plugin.
AssignedPodAdd : {
{
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-07-17 18:53:07 -04:00
} ,
} ,
} ,
} ,
} ,
{
name : "pod is enqueued to unschedulable pod pool if no events that can make the pod schedulable" ,
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod } ,
actions : [ ] action {
{ podPopped : pod } ,
{ eventHappens : & NodeAdd } ,
{ podEnqueued : newQueuedPodInfoForLookup ( pod , "fooPlugin1" ) } ,
} ,
wantUnschedPodPoolPodNames : [ ] string { "targetpod" } ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : nil ,
wantInFlightEvents : nil ,
2023-07-17 18:53:07 -04:00
queueingHintMap : QueueingHintMapPerProfile {
"" : {
// fooPlugin1 has no queueing hint function for NodeAdd.
AssignedPodAdd : {
{
// It will be ignored because the event is not NodeAdd.
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-07-17 18:53:07 -04:00
} ,
} ,
} ,
} ,
} ,
{
2023-10-19 07:02:11 -04:00
name : "pod is enqueued to unschedulable pod pool because the failed plugin has a hint fn but it returns Skip" ,
2023-07-17 18:53:07 -04:00
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod } ,
actions : [ ] action {
{ podPopped : pod } ,
{ eventHappens : & AssignedPodAdd } ,
{ podEnqueued : newQueuedPodInfoForLookup ( pod , "fooPlugin1" ) } ,
} ,
wantUnschedPodPoolPodNames : [ ] string { "targetpod" } ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : nil ,
wantInFlightEvents : nil ,
2023-07-17 18:53:07 -04:00
queueingHintMap : QueueingHintMapPerProfile {
"" : {
// fooPlugin1 has a queueing hint function for AssignedPodAdd,
// but hint fn tells that this event doesn't make a Pod scheudlable.
AssignedPodAdd : {
{
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnSkip ,
2023-07-17 18:53:07 -04:00
} ,
} ,
} ,
} ,
} ,
{
2023-10-19 07:02:11 -04:00
name : "pod is enqueued to activeQ because the Pending plugins has a hint fn and it returns Queue" ,
2023-07-17 18:53:07 -04:00
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod } ,
actions : [ ] action {
{ podPopped : pod } ,
{ eventHappens : & AssignedPodAdd } ,
2023-10-19 07:02:11 -04:00
{ podEnqueued : & framework . QueuedPodInfo {
PodInfo : mustNewPodInfo ( pod ) ,
UnschedulablePlugins : sets . New ( "fooPlugin2" , "fooPlugin3" ) ,
PendingPlugins : sets . New ( "fooPlugin1" ) ,
} } ,
2023-07-17 18:53:07 -04:00
} ,
wantActiveQPodNames : [ ] string { "targetpod" } ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : nil ,
wantInFlightEvents : nil ,
2023-07-17 18:53:07 -04:00
queueingHintMap : QueueingHintMapPerProfile {
"" : {
AssignedPodAdd : {
{
PluginName : "fooPlugin3" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnSkip ,
2023-07-17 18:53:07 -04:00
} ,
{
PluginName : "fooPlugin2" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-07-17 18:53:07 -04:00
} ,
{
// The hint fn tells that this event makes a Pod scheudlable immediately.
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-07-17 18:53:07 -04:00
} ,
} ,
} ,
} ,
} ,
{
2023-10-19 07:02:11 -04:00
name : "pod is enqueued to backoffQ because the failed plugin has a hint fn and it returns Queue" ,
2023-07-17 18:53:07 -04:00
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod } ,
actions : [ ] action {
{ podPopped : pod } ,
{ eventHappens : & AssignedPodAdd } ,
{ podEnqueued : newQueuedPodInfoForLookup ( pod , "fooPlugin1" , "fooPlugin2" ) } ,
} ,
wantBackoffQPodNames : [ ] string { "targetpod" } ,
2023-09-04 09:48:45 -04:00
wantInFlightPods : nil ,
wantInFlightEvents : nil ,
2023-07-17 18:53:07 -04:00
queueingHintMap : QueueingHintMapPerProfile {
"" : {
AssignedPodAdd : {
{
2023-10-19 07:02:11 -04:00
// it will be ignored because the hint fn returns Skip that is weaker than queueHintReturnQueue from fooPlugin1.
2023-07-17 18:53:07 -04:00
PluginName : "fooPlugin2" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnSkip ,
2023-07-17 18:53:07 -04:00
} ,
{
2023-10-19 07:02:11 -04:00
// The hint fn tells that this event makes a Pod schedulable.
2023-07-17 18:53:07 -04:00
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-07-17 18:53:07 -04:00
} ,
} ,
} ,
} ,
} ,
2023-09-05 14:21:40 -04:00
{
2023-10-19 07:02:11 -04:00
name : "pod is enqueued to activeQ because the failed plugin has a hint fn and it returns Queue for a concurrent event that was received while some other pod was in flight" ,
2023-09-05 14:21:40 -04:00
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod , pod2 } ,
actions : [ ] action {
2023-09-28 22:59:22 -04:00
{ callback : func ( t * testing . T , q * PriorityQueue ) { poppedPod = popPod ( t , logger , q , pod ) } } ,
2023-09-05 14:21:40 -04:00
{ eventHappens : & NodeAdd } ,
2023-09-28 22:59:22 -04:00
{ callback : func ( t * testing . T , q * PriorityQueue ) { poppedPod2 = popPod ( t , logger , q , pod2 ) } } ,
2023-09-05 14:21:40 -04:00
{ eventHappens : & AssignedPodAdd } ,
{ callback : func ( t * testing . T , q * PriorityQueue ) {
logger , _ := ktesting . NewTestContext ( t )
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , poppedPod , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
2023-09-05 14:21:40 -04:00
}
} } ,
{ callback : func ( t * testing . T , q * PriorityQueue ) {
logger , _ := ktesting . NewTestContext ( t )
2023-10-19 07:02:11 -04:00
poppedPod2 . UnschedulablePlugins = sets . New ( "fooPlugin2" , "fooPlugin3" )
poppedPod2 . PendingPlugins = sets . New ( "fooPlugin1" )
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , poppedPod2 , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
2023-09-05 14:21:40 -04:00
}
} } ,
} ,
wantActiveQPodNames : [ ] string { pod2 . Name } ,
wantInFlightPods : nil ,
wantInFlightEvents : nil ,
queueingHintMap : QueueingHintMapPerProfile {
"" : {
AssignedPodAdd : {
{
// it will be ignored because the hint fn returns QueueSkip that is weaker than queueHintReturnQueueImmediately from fooPlugin1.
PluginName : "fooPlugin3" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnSkip ,
2023-09-05 14:21:40 -04:00
} ,
{
2023-10-19 07:02:11 -04:00
// it will be ignored because the fooPlugin2 is registered in UnschedulablePlugins and it's interpret as Queue that is weaker than QueueImmediately from fooPlugin1.
2023-09-05 14:21:40 -04:00
PluginName : "fooPlugin2" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-09-05 14:21:40 -04:00
} ,
{
2023-10-19 07:02:11 -04:00
// The hint fn tells that this event makes a Pod scheudlable.
// Given fooPlugin1 is registered as Pendings, we interpret Queue as queueImmediately.
2023-09-05 14:21:40 -04:00
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-09-05 14:21:40 -04:00
} ,
} ,
} ,
} ,
} ,
2023-09-01 01:55:31 -04:00
{
2023-10-19 07:02:11 -04:00
name : "popped pod must have empty UnschedulablePlugins and PendingPlugins" ,
2023-09-01 01:55:31 -04:00
isSchedulingQueueHintEnabled : true ,
initialPods : [ ] * v1 . Pod { pod } ,
actions : [ ] action {
2023-09-28 22:59:22 -04:00
{ callback : func ( t * testing . T , q * PriorityQueue ) { poppedPod = popPod ( t , logger , q , pod ) } } ,
2023-09-01 01:55:31 -04:00
{ callback : func ( t * testing . T , q * PriorityQueue ) {
logger , _ := ktesting . NewTestContext ( t )
2023-10-19 07:02:11 -04:00
// Unschedulable due to PendingPlugins.
poppedPod . PendingPlugins = sets . New ( "fooPlugin1" )
poppedPod . UnschedulablePlugins = sets . New ( "fooPlugin2" )
2023-09-01 01:55:31 -04:00
if err := q . AddUnschedulableIfNotPresent ( logger , poppedPod , q . SchedulingCycle ( ) ) ; err != nil {
t . Errorf ( "Unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
} } ,
{ eventHappens : & PvAdd } , // Active again.
{ callback : func ( t * testing . T , q * PriorityQueue ) {
2023-09-28 22:59:22 -04:00
poppedPod = popPod ( t , logger , q , pod )
2023-09-01 01:55:31 -04:00
if len ( poppedPod . UnschedulablePlugins ) > 0 {
t . Errorf ( "QueuedPodInfo from Pop should have empty UnschedulablePlugins, got instead: %+v" , poppedPod )
}
} } ,
{ callback : func ( t * testing . T , q * PriorityQueue ) {
logger , _ := ktesting . NewTestContext ( t )
// Failed (i.e. no UnschedulablePlugins). Should go to backoff.
if err := q . AddUnschedulableIfNotPresent ( logger , poppedPod , q . SchedulingCycle ( ) ) ; err != nil {
t . Errorf ( "Unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
} } ,
} ,
queueingHintMap : QueueingHintMapPerProfile {
"" : {
PvAdd : {
{
// The hint fn tells that this event makes a Pod scheudlable immediately.
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-09-01 01:55:31 -04:00
} ,
} ,
} ,
} ,
} ,
2023-07-17 18:53:07 -04:00
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . SchedulerQueueingHints , test . isSchedulingQueueHintEnabled ) ( )
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
defer cancel ( )
obj := make ( [ ] runtime . Object , 0 , len ( test . initialPods ) )
for _ , p := range test . initialPods {
obj = append ( obj , p )
}
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , obj , WithQueueingHintMapPerProfile ( test . queueingHintMap ) )
for _ , p := range test . initialPods {
q . Add ( logger , p )
}
for _ , action := range test . actions {
switch {
case action . podPopped != nil :
2023-09-28 22:59:22 -04:00
popPod ( t , logger , q , action . podPopped )
2023-07-17 18:53:07 -04:00
case action . eventHappens != nil :
q . MoveAllToActiveOrBackoffQueue ( logger , * action . eventHappens , nil , nil , nil )
case action . podEnqueued != nil :
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , action . podEnqueued , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2023-09-05 14:21:40 -04:00
case action . callback != nil :
action . callback ( t , q )
2023-07-17 18:53:07 -04:00
}
}
2023-09-04 09:48:45 -04:00
actualInFlightPods := make ( map [ types . UID ] * v1 . Pod )
for uid , element := range q . inFlightPods {
actualInFlightPods [ uid ] = element . Value . ( * v1 . Pod )
}
wantInFlightPods := make ( map [ types . UID ] * v1 . Pod )
for _ , pod := range test . wantInFlightPods {
wantInFlightPods [ pod . UID ] = pod
}
if diff := cmp . Diff ( wantInFlightPods , actualInFlightPods ) ; diff != "" {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Unexpected diff in inFlightPods (-want, +got):\n%s" , diff )
}
2023-09-04 09:48:45 -04:00
var wantInFlightEvents [ ] interface { }
for _ , value := range test . wantInFlightEvents {
if event , ok := value . ( framework . ClusterEvent ) ; ok {
value = & clusterEvent { event : event }
}
wantInFlightEvents = append ( wantInFlightEvents , value )
}
if diff := cmp . Diff ( wantInFlightEvents , listToValues ( q . inFlightEvents ) , cmp . AllowUnexported ( clusterEvent { } ) ) ; diff != "" {
t . Errorf ( "Unexpected diff in inFlightEvents (-want, +got):\n%s" , diff )
2023-07-17 18:53:07 -04:00
}
if test . wantActiveQPodNames != nil {
rawPodInfos := q . activeQ . List ( )
if len ( rawPodInfos ) != len ( test . wantActiveQPodNames ) {
diff := cmp . Diff ( test . wantActiveQPodNames , rawPodInfos , cmpopts . SortSlices ( func ( a , b interface { } ) bool {
return a . ( framework . PodInfo ) . Pod . Name < b . ( framework . PodInfo ) . Pod . Name
} ) )
t . Fatalf ( "Length of activeQ is not expected. Got %v, want %v.\n%s" , len ( rawPodInfos ) , len ( test . wantActiveQPodNames ) , diff )
}
wantPodNames := sets . New ( test . wantActiveQPodNames ... )
for _ , rawPodInfo := range rawPodInfos {
podGotFromActiveQ := rawPodInfo . ( * framework . QueuedPodInfo ) . Pod
if ! wantPodNames . Has ( podGotFromActiveQ . Name ) {
t . Fatalf ( "Pod %v was not expected to be in the activeQ." , podGotFromActiveQ . Name )
}
}
}
if test . wantBackoffQPodNames != nil {
rawPodInfos := q . podBackoffQ . List ( )
if len ( rawPodInfos ) != len ( test . wantBackoffQPodNames ) {
diff := cmp . Diff ( test . wantBackoffQPodNames , rawPodInfos , cmpopts . SortSlices ( func ( a , b interface { } ) bool {
return a . ( framework . PodInfo ) . Pod . Name < b . ( framework . PodInfo ) . Pod . Name
} ) )
t . Fatalf ( "Length of backoffQ is not expected. Got %v, want %v.\n%s" , len ( rawPodInfos ) , len ( test . wantBackoffQPodNames ) , diff )
}
wantPodNames := sets . New ( test . wantBackoffQPodNames ... )
for _ , rawPodInfo := range rawPodInfos {
podGotFromBackoffQ := rawPodInfo . ( * framework . QueuedPodInfo ) . Pod
if ! wantPodNames . Has ( podGotFromBackoffQ . Name ) {
t . Fatalf ( "Pod %v was not expected to be in the backoffQ." , podGotFromBackoffQ . Name )
}
}
}
for _ , podName := range test . wantUnschedPodPoolPodNames {
p := getUnschedulablePod ( q , & st . MakePod ( ) . Name ( podName ) . Pod )
if p == nil {
t . Fatalf ( "Pod %v was not found in the unschedulablePods." , podName )
}
}
} )
}
}
2023-09-28 22:59:22 -04:00
func popPod ( t * testing . T , logger klog . Logger , q * PriorityQueue , pod * v1 . Pod ) * framework . QueuedPodInfo {
p , err := q . Pop ( logger )
2023-09-01 01:55:31 -04:00
if err != nil {
t . Fatalf ( "Pop failed: %v" , err )
}
if p . Pod . UID != pod . UID {
t . Errorf ( "Unexpected popped pod: %v" , p )
}
return p
}
func TestPop ( t * testing . T ) {
pod := st . MakePod ( ) . Name ( "targetpod" ) . UID ( "pod1" ) . Obj ( )
queueingHintMap := QueueingHintMapPerProfile {
"" : {
PvAdd : {
{
2023-10-19 07:02:11 -04:00
// The hint fn tells that this event makes a Pod scheudlable.
2023-09-01 01:55:31 -04:00
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-09-01 01:55:31 -04:00
} ,
} ,
} ,
}
for name , isSchedulingQueueHintEnabled := range map [ string ] bool { "with-hints" : true , "without-hints" : false } {
t . Run ( name , func ( t * testing . T ) {
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . SchedulerQueueingHints , isSchedulingQueueHintEnabled ) ( )
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
defer cancel ( )
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , [ ] runtime . Object { pod } , WithQueueingHintMapPerProfile ( queueingHintMap ) )
q . Add ( logger , pod )
// Simulate failed attempt that makes the pod unschedulable.
2023-09-28 22:59:22 -04:00
poppedPod := popPod ( t , logger , q , pod )
2023-10-19 07:02:11 -04:00
// We put register the plugin to PendingPlugins so that it's interpreted as queueImmediately and skip backoff.
poppedPod . PendingPlugins = sets . New ( "fooPlugin1" )
2023-09-01 01:55:31 -04:00
if err := q . AddUnschedulableIfNotPresent ( logger , poppedPod , q . SchedulingCycle ( ) ) ; err != nil {
t . Errorf ( "Unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
// Activate it again.
q . MoveAllToActiveOrBackoffQueue ( logger , PvAdd , nil , nil , nil )
// Now check result of Pop.
2023-09-28 22:59:22 -04:00
poppedPod = popPod ( t , logger , q , pod )
2023-10-19 07:02:11 -04:00
if len ( poppedPod . PendingPlugins ) > 0 {
t . Errorf ( "QueuedPodInfo from Pop should have empty PendingPlugins, got instead: %+v" , poppedPod )
2023-09-01 01:55:31 -04:00
}
} )
}
}
2018-02-08 21:19:31 -05:00
func TestPriorityQueue_AddUnschedulableIfNotPresent ( t * testing . T ) {
2021-06-05 14:30:17 -04:00
objs := [ ] runtime . Object { highPriNominatedPodInfo . Pod , unschedulablePodInfo . Pod }
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , objs )
2023-07-17 18:53:07 -04:00
// insert unschedulablePodInfo and pop right after that
// because the scheduling queue records unschedulablePod as in-flight Pod.
q . Add ( logger , unschedulablePodInfo . Pod )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != unschedulablePodInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , unschedulablePodInfo . Pod . Name , p . Pod . Name )
}
2023-03-22 07:48:04 -04:00
q . Add ( logger , highPriNominatedPodInfo . Pod )
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , newQueuedPodInfoForLookup ( unschedulablePodInfo . Pod , "plugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2021-06-05 14:30:17 -04:00
expectedNominatedPods := & nominator {
2018-12-18 02:41:53 -05:00
nominatedPodToNode : map [ types . UID ] string {
2021-02-22 09:00:23 -05:00
unschedulablePodInfo . Pod . UID : "node1" ,
highPriNominatedPodInfo . Pod . UID : "node1" ,
2018-12-18 02:41:53 -05:00
} ,
2021-02-22 09:00:23 -05:00
nominatedPods : map [ string ] [ ] * framework . PodInfo {
"node1" : { highPriNominatedPodInfo , unschedulablePodInfo } ,
2018-12-18 02:41:53 -05:00
} ,
2018-02-08 21:19:31 -05:00
}
2023-03-08 16:18:36 -05:00
if diff := cmp . Diff ( q . nominator , expectedNominatedPods , nominatorCmpOpts ... ) ; diff != "" {
2021-06-04 14:04:44 -04:00
t . Errorf ( "Unexpected diff after adding pods (-want, +got):\n%s" , diff )
2018-02-08 21:19:31 -05:00
}
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != highPriNominatedPodInfo . Pod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , highPriNominatedPodInfo . Pod . Name , p . Pod . Name )
2018-02-08 21:19:31 -05:00
}
2023-03-08 16:18:36 -05:00
if len ( q . nominator . nominatedPods ) != 1 {
t . Errorf ( "Expected nomindatePods to have one element: %v" , q . nominator )
2018-02-08 21:19:31 -05:00
}
2023-07-17 18:53:07 -04:00
// unschedulablePodInfo is inserted to unschedulable pod pool because no events happened during scheduling.
2021-02-22 09:00:23 -05:00
if getUnschedulablePod ( q , unschedulablePodInfo . Pod ) != unschedulablePodInfo . Pod {
2022-03-24 05:38:49 -04:00
t . Errorf ( "Pod %v was not found in the unschedulablePods." , unschedulablePodInfo . Pod . Name )
2018-02-08 21:19:31 -05:00
}
2017-10-24 14:14:29 -04:00
}
2019-07-01 20:29:34 -04:00
// TestPriorityQueue_AddUnschedulableIfNotPresent_Backoff tests the scenarios when
// AddUnschedulableIfNotPresent is called asynchronously.
// Pods in and before current scheduling cycle will be put back to activeQueue
// if we were trying to schedule them when we received move request.
2019-02-15 20:43:08 -05:00
func TestPriorityQueue_AddUnschedulableIfNotPresent_Backoff ( t * testing . T ) {
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( testingclock . NewFakeClock ( time . Now ( ) ) ) )
2019-01-25 05:39:53 -05:00
totalNum := 10
expectedPods := make ( [ ] v1 . Pod , 0 , totalNum )
for i := 0 ; i < totalNum ; i ++ {
priority := int32 ( i )
2022-04-18 21:06:58 -04:00
p := st . MakePod ( ) . Name ( fmt . Sprintf ( "pod%d" , i ) ) . Namespace ( fmt . Sprintf ( "ns%d" , i ) ) . UID ( fmt . Sprintf ( "upns%d" , i ) ) . Priority ( priority ) . Obj ( )
expectedPods = append ( expectedPods , * p )
2019-01-25 05:39:53 -05:00
// priority is to make pods ordered in the PriorityQueue
2023-03-22 07:48:04 -04:00
q . Add ( logger , p )
2019-01-25 05:39:53 -05:00
}
// Pop all pods except for the first one
for i := totalNum - 1 ; i > 0 ; i -- {
2023-09-28 22:59:22 -04:00
p , _ := q . Pop ( logger )
2023-06-29 03:28:42 -04:00
if diff := cmp . Diff ( & expectedPods [ i ] , p . Pod ) ; diff != "" {
t . Errorf ( "Unexpected pod (-want, +got):\n%s" , diff )
2019-01-25 05:39:53 -05:00
}
}
// move all pods to active queue when we were trying to schedule them
2023-07-17 18:53:07 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , WildCardEvent , nil , nil , nil )
2019-02-15 20:43:08 -05:00
oldCycle := q . SchedulingCycle ( )
2023-09-28 22:59:22 -04:00
firstPod , _ := q . Pop ( logger )
2023-06-29 03:28:42 -04:00
if diff := cmp . Diff ( & expectedPods [ 0 ] , firstPod . Pod ) ; diff != "" {
t . Errorf ( "Unexpected pod (-want, +got):\n%s" , diff )
2019-02-15 20:43:08 -05:00
}
// mark pods[1] ~ pods[totalNum-1] as unschedulable and add them back
2019-01-25 05:39:53 -05:00
for i := 1 ; i < totalNum ; i ++ {
unschedulablePod := expectedPods [ i ] . DeepCopy ( )
unschedulablePod . Status = v1 . PodStatus {
Conditions : [ ] v1 . PodCondition {
{
Type : v1 . PodScheduled ,
Status : v1 . ConditionFalse ,
Reason : v1 . PodReasonUnschedulable ,
} ,
} ,
}
2019-02-15 20:43:08 -05:00
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , newQueuedPodInfoForLookup ( unschedulablePod , "plugin" ) , oldCycle )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
2019-07-01 20:29:34 -04:00
}
2019-01-25 05:39:53 -05:00
}
2019-02-15 20:43:08 -05:00
// Since there was a move request at the same cycle as "oldCycle", these pods
// should be in the backoff queue.
2019-01-25 05:39:53 -05:00
for i := 1 ; i < totalNum ; i ++ {
2021-02-22 09:00:23 -05:00
if _ , exists , _ := q . podBackoffQ . Get ( newQueuedPodInfoForLookup ( & expectedPods [ i ] ) ) ; ! exists {
2019-02-15 20:43:08 -05:00
t . Errorf ( "Expected %v to be added to podBackoffQ." , expectedPods [ i ] . Name )
2019-01-25 05:39:53 -05:00
}
}
}
2017-10-24 14:14:29 -04:00
func TestPriorityQueue_Pop ( t * testing . T ) {
2021-06-05 14:30:17 -04:00
objs := [ ] runtime . Object { medPriorityPodInfo . Pod }
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , objs )
2017-11-28 04:40:35 -05:00
wg := sync . WaitGroup { }
wg . Add ( 1 )
2017-10-24 14:14:29 -04:00
go func ( ) {
2017-11-28 04:40:35 -05:00
defer wg . Done ( )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != medPriorityPodInfo . Pod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , medPriorityPodInfo . Pod . Name , p . Pod . Name )
2018-02-08 21:19:31 -05:00
}
2023-03-08 16:18:36 -05:00
if len ( q . nominator . nominatedPods [ "node1" ] ) != 1 {
t . Errorf ( "Expected medPriorityPodInfo to be present in nomindatePods: %v" , q . nominator . nominatedPods [ "node1" ] )
2017-10-24 14:14:29 -04:00
}
} ( )
2023-03-22 07:48:04 -04:00
q . Add ( logger , medPriorityPodInfo . Pod )
2017-11-28 04:40:35 -05:00
wg . Wait ( )
2017-10-24 14:14:29 -04:00
}
func TestPriorityQueue_Update ( t * testing . T ) {
2021-06-05 14:30:17 -04:00
objs := [ ] runtime . Object { highPriorityPodInfo . Pod , unschedulablePodInfo . Pod , medPriorityPodInfo . Pod }
2021-09-17 05:48:22 -04:00
c := testingclock . NewFakeClock ( time . Now ( ) )
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , objs , WithClock ( c ) )
2023-07-17 18:53:07 -04:00
// add highPriorityPodInfo to activeQ.
2023-03-22 07:48:04 -04:00
q . Update ( logger , nil , highPriorityPodInfo . Pod )
2021-02-22 09:00:23 -05:00
if _ , exists , _ := q . activeQ . Get ( newQueuedPodInfoForLookup ( highPriorityPodInfo . Pod ) ) ; ! exists {
t . Errorf ( "Expected %v to be added to activeQ." , highPriorityPodInfo . Pod . Name )
2017-10-24 14:14:29 -04:00
}
2023-03-08 16:18:36 -05:00
if len ( q . nominator . nominatedPods ) != 0 {
t . Errorf ( "Expected nomindatePods to be empty: %v" , q . nominator )
2018-02-08 21:19:31 -05:00
}
2021-02-22 09:00:23 -05:00
// Update highPriorityPodInfo and add a nominatedNodeName to it.
2023-03-22 07:48:04 -04:00
q . Update ( logger , highPriorityPodInfo . Pod , highPriNominatedPodInfo . Pod )
2017-12-09 18:09:48 -05:00
if q . activeQ . Len ( ) != 1 {
2017-10-24 14:14:29 -04:00
t . Error ( "Expected only one item in activeQ." )
}
2023-03-08 16:18:36 -05:00
if len ( q . nominator . nominatedPods ) != 1 {
t . Errorf ( "Expected one item in nomindatePods map: %v" , q . nominator )
2018-02-08 21:19:31 -05:00
}
2017-10-24 14:14:29 -04:00
// Updating an unschedulable pod which is not in any of the two queues, should
// add the pod to activeQ.
2023-03-22 07:48:04 -04:00
q . Update ( logger , unschedulablePodInfo . Pod , unschedulablePodInfo . Pod )
2021-02-22 09:00:23 -05:00
if _ , exists , _ := q . activeQ . Get ( newQueuedPodInfoForLookup ( unschedulablePodInfo . Pod ) ) ; ! exists {
t . Errorf ( "Expected %v to be added to activeQ." , unschedulablePodInfo . Pod . Name )
2017-10-24 14:14:29 -04:00
}
2018-02-08 21:19:31 -05:00
// Updating a pod that is already in activeQ, should not change it.
2023-03-22 07:48:04 -04:00
q . Update ( logger , unschedulablePodInfo . Pod , unschedulablePodInfo . Pod )
2022-03-24 05:38:49 -04:00
if len ( q . unschedulablePods . podInfoMap ) != 0 {
t . Error ( "Expected unschedulablePods to be empty." )
2017-10-24 14:14:29 -04:00
}
2021-02-22 09:00:23 -05:00
if _ , exists , _ := q . activeQ . Get ( newQueuedPodInfoForLookup ( unschedulablePodInfo . Pod ) ) ; ! exists {
t . Errorf ( "Expected: %v to be added to activeQ." , unschedulablePodInfo . Pod . Name )
2017-10-24 14:14:29 -04:00
}
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != highPriNominatedPodInfo . Pod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , highPriorityPodInfo . Pod . Name , p . Pod . Name )
2017-10-24 14:14:29 -04:00
}
2021-02-06 04:08:16 -05:00
// Updating a pod that is in backoff queue and it is still backing off
// pod will not be moved to active queue, and it will be updated in backoff queue
podInfo := q . newQueuedPodInfo ( medPriorityPodInfo . Pod )
if err := q . podBackoffQ . Add ( podInfo ) ; err != nil {
t . Errorf ( "adding pod to backoff queue error: %v" , err )
}
2023-03-22 07:48:04 -04:00
q . Update ( logger , podInfo . Pod , podInfo . Pod )
2021-02-06 04:08:16 -05:00
rawPodInfo , err := q . podBackoffQ . Pop ( )
podGotFromBackoffQ := rawPodInfo . ( * framework . QueuedPodInfo ) . Pod
if err != nil || podGotFromBackoffQ != medPriorityPodInfo . Pod {
t . Errorf ( "Expected: %v after Pop, but got: %v" , medPriorityPodInfo . Pod . Name , podGotFromBackoffQ . Name )
}
2023-07-17 18:53:07 -04:00
// To simulate the pod is failed in scheduling in the real world, Pop() the pod from activeQ before testing AddUnschedulableIfNotPresent.
q . activeQ . Add ( podInfo )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != medPriorityPodInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , medPriorityPodInfo . Pod . Name , p . Pod . Name )
}
2023-09-11 12:30:11 -04:00
err = q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( medPriorityPodInfo . Pod , "plugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2022-03-24 05:38:49 -04:00
if len ( q . unschedulablePods . podInfoMap ) != 1 {
t . Error ( "Expected unschedulablePods to be 1." )
2020-02-13 19:31:07 -05:00
}
2021-02-22 09:00:23 -05:00
updatedPod := medPriorityPodInfo . Pod . DeepCopy ( )
2022-03-17 14:35:00 -04:00
updatedPod . Annotations [ "foo" ] = "test"
2023-03-22 07:48:04 -04:00
q . Update ( logger , medPriorityPodInfo . Pod , updatedPod )
2021-02-06 04:08:16 -05:00
rawPodInfo , err = q . podBackoffQ . Pop ( )
podGotFromBackoffQ = rawPodInfo . ( * framework . QueuedPodInfo ) . Pod
if err != nil || podGotFromBackoffQ != updatedPod {
t . Errorf ( "Expected: %v after Pop, but got: %v" , updatedPod . Name , podGotFromBackoffQ . Name )
}
2023-09-11 12:30:11 -04:00
// To simulate the pod is failed in scheduling in the real world, Pop() the pod from activeQ before testing AddUnschedulableIfNotPresent.
err = q . activeQ . Add ( podInfo )
if err != nil {
t . Fatalf ( "unexpected error from activeQ.Add: %v" , err )
}
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != medPriorityPodInfo . Pod {
2023-09-11 12:30:11 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , medPriorityPodInfo . Pod . Name , p . Pod . Name )
}
2021-02-06 04:08:16 -05:00
// updating a pod which is in unschedulable queue, and it is not backing off,
// we will move it to active queue
2023-09-11 12:30:11 -04:00
err = q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( medPriorityPodInfo . Pod , "plugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2022-03-24 05:38:49 -04:00
if len ( q . unschedulablePods . podInfoMap ) != 1 {
t . Error ( "Expected unschedulablePods to be 1." )
2021-02-06 04:08:16 -05:00
}
updatedPod = medPriorityPodInfo . Pod . DeepCopy ( )
2022-03-17 14:35:00 -04:00
updatedPod . Annotations [ "foo" ] = "test1"
2022-03-24 05:38:49 -04:00
// Move clock by podInitialBackoffDuration, so that pods in the unschedulablePods would pass the backing off,
2021-02-06 04:08:16 -05:00
// and the pods will be moved into activeQ.
c . Step ( q . podInitialBackoffDuration )
2023-03-22 07:48:04 -04:00
q . Update ( logger , medPriorityPodInfo . Pod , updatedPod )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != updatedPod {
2020-02-13 19:31:07 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , updatedPod . Name , p . Pod . Name )
}
2017-10-24 14:14:29 -04:00
}
func TestPriorityQueue_Delete ( t * testing . T ) {
2021-06-05 14:30:17 -04:00
objs := [ ] runtime . Object { highPriorityPodInfo . Pod , unschedulablePodInfo . Pod }
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , objs )
2023-03-22 07:48:04 -04:00
q . Update ( logger , highPriorityPodInfo . Pod , highPriNominatedPodInfo . Pod )
q . Add ( logger , unschedulablePodInfo . Pod )
2021-02-22 09:00:23 -05:00
if err := q . Delete ( highPriNominatedPodInfo . Pod ) ; err != nil {
2018-12-14 19:31:08 -05:00
t . Errorf ( "delete failed: %v" , err )
}
2021-02-22 09:00:23 -05:00
if _ , exists , _ := q . activeQ . Get ( newQueuedPodInfoForLookup ( unschedulablePodInfo . Pod ) ) ; ! exists {
t . Errorf ( "Expected %v to be in activeQ." , unschedulablePodInfo . Pod . Name )
2017-10-24 14:14:29 -04:00
}
2021-02-22 09:00:23 -05:00
if _ , exists , _ := q . activeQ . Get ( newQueuedPodInfoForLookup ( highPriNominatedPodInfo . Pod ) ) ; exists {
t . Errorf ( "Didn't expect %v to be in activeQ." , highPriorityPodInfo . Pod . Name )
2017-10-24 14:14:29 -04:00
}
2023-03-08 16:18:36 -05:00
if len ( q . nominator . nominatedPods ) != 1 {
t . Errorf ( "Expected nomindatePods to have only 'unschedulablePodInfo': %v" , q . nominator . nominatedPods )
2018-02-08 21:19:31 -05:00
}
2021-02-22 09:00:23 -05:00
if err := q . Delete ( unschedulablePodInfo . Pod ) ; err != nil {
2018-12-14 19:31:08 -05:00
t . Errorf ( "delete failed: %v" , err )
}
2023-03-08 16:18:36 -05:00
if len ( q . nominator . nominatedPods ) != 0 {
t . Errorf ( "Expected nomindatePods to be empty: %v" , q . nominator )
2018-02-08 21:19:31 -05:00
}
2017-10-24 14:14:29 -04:00
}
2021-12-18 09:12:46 -05:00
func TestPriorityQueue_Activate ( t * testing . T ) {
tests := [ ] struct {
2022-03-24 05:38:49 -04:00
name string
qPodInfoInUnschedulablePods [ ] * framework . QueuedPodInfo
qPodInfoInPodBackoffQ [ ] * framework . QueuedPodInfo
qPodInfoInActiveQ [ ] * framework . QueuedPodInfo
qPodInfoToActivate * framework . QueuedPodInfo
want [ ] * framework . QueuedPodInfo
2021-12-18 09:12:46 -05:00
} {
{
name : "pod already in activeQ" ,
qPodInfoInActiveQ : [ ] * framework . QueuedPodInfo { { PodInfo : highPriNominatedPodInfo } } ,
qPodInfoToActivate : & framework . QueuedPodInfo { PodInfo : highPriNominatedPodInfo } ,
2022-03-24 07:56:10 -04:00
want : [ ] * framework . QueuedPodInfo { { PodInfo : highPriNominatedPodInfo } } , // 1 already active
2021-12-18 09:12:46 -05:00
} ,
{
2022-03-24 05:38:49 -04:00
name : "pod not in unschedulablePods/podBackoffQ" ,
2021-12-18 09:12:46 -05:00
qPodInfoToActivate : & framework . QueuedPodInfo { PodInfo : highPriNominatedPodInfo } ,
want : [ ] * framework . QueuedPodInfo { } ,
} ,
{
2022-03-24 05:38:49 -04:00
name : "pod in unschedulablePods" ,
qPodInfoInUnschedulablePods : [ ] * framework . QueuedPodInfo { { PodInfo : highPriNominatedPodInfo } } ,
qPodInfoToActivate : & framework . QueuedPodInfo { PodInfo : highPriNominatedPodInfo } ,
want : [ ] * framework . QueuedPodInfo { { PodInfo : highPriNominatedPodInfo } } ,
2021-12-18 09:12:46 -05:00
} ,
{
name : "pod in backoffQ" ,
qPodInfoInPodBackoffQ : [ ] * framework . QueuedPodInfo { { PodInfo : highPriNominatedPodInfo } } ,
qPodInfoToActivate : & framework . QueuedPodInfo { PodInfo : highPriNominatedPodInfo } ,
want : [ ] * framework . QueuedPodInfo { { PodInfo : highPriNominatedPodInfo } } ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
var objs [ ] runtime . Object
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , objs )
2021-12-18 09:12:46 -05:00
2022-03-24 05:38:49 -04:00
// Prepare activeQ/unschedulablePods/podBackoffQ according to the table
2021-12-18 09:12:46 -05:00
for _ , qPodInfo := range tt . qPodInfoInActiveQ {
q . activeQ . Add ( qPodInfo )
}
2022-03-24 05:38:49 -04:00
for _ , qPodInfo := range tt . qPodInfoInUnschedulablePods {
q . unschedulablePods . addOrUpdate ( qPodInfo )
2021-12-18 09:12:46 -05:00
}
for _ , qPodInfo := range tt . qPodInfoInPodBackoffQ {
q . podBackoffQ . Add ( qPodInfo )
}
// Activate specific pod according to the table
2023-03-22 07:48:04 -04:00
q . Activate ( logger , map [ string ] * v1 . Pod { "test_pod" : tt . qPodInfoToActivate . PodInfo . Pod } )
2021-12-18 09:12:46 -05:00
// Check the result after activation by the length of activeQ
if wantLen := len ( tt . want ) ; q . activeQ . Len ( ) != wantLen {
t . Errorf ( "length compare: want %v, got %v" , wantLen , q . activeQ . Len ( ) )
}
// Check if the specific pod exists in activeQ
for _ , want := range tt . want {
if _ , exist , _ := q . activeQ . Get ( newQueuedPodInfoForLookup ( want . PodInfo . Pod ) ) ; ! exist {
t . Errorf ( "podInfo not exist in activeQ: want %v" , want . PodInfo . Pod . Name )
}
}
} )
}
}
2022-11-07 17:02:22 -05:00
type preEnqueuePlugin struct {
allowlists [ ] string
}
func ( pl * preEnqueuePlugin ) Name ( ) string {
return "preEnqueuePlugin"
}
func ( pl * preEnqueuePlugin ) PreEnqueue ( ctx context . Context , p * v1 . Pod ) * framework . Status {
for _ , allowed := range pl . allowlists {
2022-11-22 00:33:16 -05:00
for label := range p . Labels {
if label == allowed {
return nil
}
2022-11-07 17:02:22 -05:00
}
}
return framework . NewStatus ( framework . UnschedulableAndUnresolvable , "pod name not in allowlists" )
}
func TestPriorityQueue_addToActiveQ ( t * testing . T ) {
tests := [ ] struct {
name string
plugins [ ] framework . PreEnqueuePlugin
pod * v1 . Pod
wantUnschedulablePods int
wantSuccess bool
} {
{
name : "no plugins registered" ,
2022-11-22 00:33:16 -05:00
pod : st . MakePod ( ) . Name ( "p" ) . Label ( "p" , "" ) . Obj ( ) ,
2022-11-07 17:02:22 -05:00
wantUnschedulablePods : 0 ,
wantSuccess : true ,
} ,
{
name : "preEnqueue plugin registered, pod name not in allowlists" ,
plugins : [ ] framework . PreEnqueuePlugin { & preEnqueuePlugin { } , & preEnqueuePlugin { } } ,
2022-11-22 00:33:16 -05:00
pod : st . MakePod ( ) . Name ( "p" ) . Label ( "p" , "" ) . Obj ( ) ,
2022-11-07 17:02:22 -05:00
wantUnschedulablePods : 1 ,
wantSuccess : false ,
} ,
{
name : "preEnqueue plugin registered, pod failed one preEnqueue plugin" ,
plugins : [ ] framework . PreEnqueuePlugin {
& preEnqueuePlugin { allowlists : [ ] string { "foo" , "bar" } } ,
& preEnqueuePlugin { allowlists : [ ] string { "foo" } } ,
} ,
2022-11-22 00:33:16 -05:00
pod : st . MakePod ( ) . Name ( "bar" ) . Label ( "bar" , "" ) . Obj ( ) ,
2022-11-07 17:02:22 -05:00
wantUnschedulablePods : 1 ,
wantSuccess : false ,
} ,
{
name : "preEnqueue plugin registered, pod passed all preEnqueue plugins" ,
plugins : [ ] framework . PreEnqueuePlugin {
& preEnqueuePlugin { allowlists : [ ] string { "foo" , "bar" } } ,
& preEnqueuePlugin { allowlists : [ ] string { "bar" } } ,
} ,
2022-11-22 00:33:16 -05:00
pod : st . MakePod ( ) . Name ( "bar" ) . Label ( "bar" , "" ) . Obj ( ) ,
2022-11-07 17:02:22 -05:00
wantUnschedulablePods : 0 ,
wantSuccess : true ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
ctx , cancel := context . WithCancel ( context . Background ( ) )
2023-03-22 07:48:04 -04:00
logger := klog . FromContext ( ctx )
2022-11-07 17:02:22 -05:00
defer cancel ( )
m := map [ string ] [ ] framework . PreEnqueuePlugin { "" : tt . plugins }
2022-11-08 02:11:34 -05:00
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , [ ] runtime . Object { tt . pod } , WithPreEnqueuePluginMap ( m ) ,
WithPodInitialBackoffDuration ( time . Second * 30 ) , WithPodMaxBackoffDuration ( time . Second * 60 ) )
2023-03-22 07:48:04 -04:00
got , _ := q . addToActiveQ ( logger , q . newQueuedPodInfo ( tt . pod ) )
2022-11-07 17:02:22 -05:00
if got != tt . wantSuccess {
t . Errorf ( "Unexpected result: want %v, but got %v" , tt . wantSuccess , got )
}
if tt . wantUnschedulablePods != len ( q . unschedulablePods . podInfoMap ) {
t . Errorf ( "Unexpected unschedulablePods: want %v, but got %v" , tt . wantUnschedulablePods , len ( q . unschedulablePods . podInfoMap ) )
}
2022-11-08 02:11:34 -05:00
// Simulate an update event.
clone := tt . pod . DeepCopy ( )
metav1 . SetMetaDataAnnotation ( & clone . ObjectMeta , "foo" , "" )
2023-03-22 07:48:04 -04:00
q . Update ( logger , tt . pod , clone )
2022-11-08 02:11:34 -05:00
// Ensure the pod is still located in unschedulablePods.
if tt . wantUnschedulablePods != len ( q . unschedulablePods . podInfoMap ) {
t . Errorf ( "Unexpected unschedulablePods: want %v, but got %v" , tt . wantUnschedulablePods , len ( q . unschedulablePods . podInfoMap ) )
}
2022-11-07 17:02:22 -05:00
} )
}
}
2021-02-19 22:18:33 -05:00
func BenchmarkMoveAllToActiveOrBackoffQueue ( b * testing . B ) {
tests := [ ] struct {
name string
2021-03-16 18:08:19 -04:00
moveEvent framework . ClusterEvent
2021-02-19 22:18:33 -05:00
} {
{
name : "baseline" ,
moveEvent : UnschedulableTimeout ,
} ,
{
name : "worst" ,
moveEvent : NodeAdd ,
} ,
{
name : "random" ,
// leave "moveEvent" unspecified
} ,
}
podTemplates := [ ] * v1 . Pod {
highPriorityPodInfo . Pod , highPriNominatedPodInfo . Pod ,
medPriorityPodInfo . Pod , unschedulablePodInfo . Pod ,
}
2021-03-16 18:08:19 -04:00
events := [ ] framework . ClusterEvent {
NodeAdd ,
NodeTaintChange ,
NodeAllocatableChange ,
NodeConditionChange ,
NodeLabelChange ,
PvcAdd ,
PvcUpdate ,
PvAdd ,
PvUpdate ,
StorageClassAdd ,
2021-03-09 06:42:20 -05:00
StorageClassUpdate ,
2021-03-16 18:08:19 -04:00
CSINodeAdd ,
CSINodeUpdate ,
2021-03-09 06:42:20 -05:00
CSIDriverAdd ,
CSIDriverUpdate ,
CSIStorageCapacityAdd ,
CSIStorageCapacityUpdate ,
2021-03-16 18:08:19 -04:00
}
2021-02-19 22:18:33 -05:00
pluginNum := 20
var plugins [ ] string
// Mimic that we have 20 plugins loaded in runtime.
for i := 0 ; i < pluginNum ; i ++ {
plugins = append ( plugins , fmt . Sprintf ( "fake-plugin-%v" , i ) )
}
for _ , tt := range tests {
2022-03-24 05:38:49 -04:00
for _ , podsInUnschedulablePods := range [ ] int { 1000 , 5000 } {
b . Run ( fmt . Sprintf ( "%v-%v" , tt . name , podsInUnschedulablePods ) , func ( b * testing . B ) {
2023-03-22 07:48:04 -04:00
logger , _ := ktesting . NewTestContext ( b )
2021-02-19 22:18:33 -05:00
for i := 0 ; i < b . N ; i ++ {
b . StopTimer ( )
2021-09-17 05:48:22 -04:00
c := testingclock . NewFakeClock ( time . Now ( ) )
2021-02-19 22:18:33 -05:00
2023-06-08 00:54:30 -04:00
m := makeEmptyQueueingHintMapPerProfile ( )
2021-02-19 22:18:33 -05:00
// - All plugins registered for events[0], which is NodeAdd.
// - 1/2 of plugins registered for events[1]
// - 1/3 of plugins registered for events[2]
// - ...
for j := 0 ; j < len ( events ) ; j ++ {
for k := 0 ; k < len ( plugins ) ; k ++ {
if ( k + 1 ) % ( j + 1 ) == 0 {
2023-06-08 00:54:30 -04:00
m [ "" ] [ events [ j ] ] = append ( m [ "" ] [ events [ j ] ] , & QueueingHintFunction {
PluginName : plugins [ k ] ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} )
2021-02-19 22:18:33 -05:00
}
}
}
2022-01-22 14:00:56 -05:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
2023-06-08 00:54:30 -04:00
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( c ) , WithQueueingHintMapPerProfile ( m ) )
2021-02-19 22:18:33 -05:00
2022-03-24 05:38:49 -04:00
// Init pods in unschedulablePods.
for j := 0 ; j < podsInUnschedulablePods ; j ++ {
2021-02-19 22:18:33 -05:00
p := podTemplates [ j % len ( podTemplates ) ] . DeepCopy ( )
p . Name , p . UID = fmt . Sprintf ( "%v-%v" , p . Name , j ) , types . UID ( fmt . Sprintf ( "%v-%v" , p . UID , j ) )
var podInfo * framework . QueuedPodInfo
// The ultimate goal of composing each PodInfo is to cover the path that intersects
// (unschedulable) plugin names with the plugins that register the moveEvent,
// here the rational is:
// - in baseline case, don't inject unschedulable plugin names, so podMatchesEvent()
// never gets executed.
// - in worst case, make both ends (of the intersection) a big number,i.e.,
// M intersected with N instead of M with 1 (or 1 with N)
// - in random case, each pod failed by a random plugin, and also the moveEvent
// is randomized.
if tt . name == "baseline" {
podInfo = q . newQueuedPodInfo ( p )
} else if tt . name == "worst" {
// Each pod failed by all plugins.
podInfo = q . newQueuedPodInfo ( p , plugins ... )
} else {
// Random case.
podInfo = q . newQueuedPodInfo ( p , plugins [ j % len ( plugins ) ] )
}
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , podInfo , q . SchedulingCycle ( ) )
if err != nil {
b . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2021-02-19 22:18:33 -05:00
}
b . StartTimer ( )
2021-03-16 18:08:19 -04:00
if tt . moveEvent . Resource != "" {
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , tt . moveEvent , nil , nil , nil )
2021-02-19 22:18:33 -05:00
} else {
// Random case.
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , events [ i % len ( events ) ] , nil , nil , nil )
2021-02-19 22:18:33 -05:00
}
}
} )
}
}
}
2023-06-08 00:54:30 -04:00
func TestPriorityQueue_MoveAllToActiveOrBackoffQueueWithQueueingHint ( t * testing . T ) {
now := time . Now ( )
p := st . MakePod ( ) . Name ( "pod1" ) . Namespace ( "ns1" ) . UID ( "1" ) . Obj ( )
tests := [ ] struct {
name string
podInfo * framework . QueuedPodInfo
hint framework . QueueingHintFn
// duration is the duration that the Pod has been in the unschedulable queue.
duration time . Duration
// expectedQ is the queue name (activeQ, backoffQ, or unschedulablePods) that this Pod should be quened to.
expectedQ string
} {
{
2023-10-19 07:02:11 -04:00
name : "Queue queues pod to activeQ" ,
podInfo : & framework . QueuedPodInfo { PodInfo : mustNewPodInfo ( p ) , PendingPlugins : sets . New ( "foo" ) } ,
hint : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
expectedQ : activeQ ,
} ,
{
2023-10-19 07:02:11 -04:00
name : "Queue queues pod to backoffQ if Pod is backing off" ,
podInfo : & framework . QueuedPodInfo { PodInfo : mustNewPodInfo ( p ) , UnschedulablePlugins : sets . New ( "foo" ) } ,
hint : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
expectedQ : backoffQ ,
} ,
{
2023-10-19 07:02:11 -04:00
name : "Queue queues pod to activeQ if Pod is not backing off" ,
podInfo : & framework . QueuedPodInfo { PodInfo : mustNewPodInfo ( p ) , UnschedulablePlugins : sets . New ( "foo" ) } ,
hint : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
duration : DefaultPodInitialBackoffDuration , // backoff is finished
expectedQ : activeQ ,
} ,
{
2023-10-19 07:02:11 -04:00
name : "Skip queues pod to unschedulablePods" ,
podInfo : & framework . QueuedPodInfo { PodInfo : mustNewPodInfo ( p ) , UnschedulablePlugins : sets . New ( "foo" ) } ,
hint : queueHintReturnSkip ,
2023-06-08 00:54:30 -04:00
expectedQ : unschedulablePods ,
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
logger , ctx := ktesting . NewTestContext ( t )
m := makeEmptyQueueingHintMapPerProfile ( )
m [ "" ] [ NodeAdd ] = [ ] * QueueingHintFunction {
{
PluginName : "foo" ,
QueueingHintFn : test . hint ,
} ,
}
test . podInfo . UnschedulablePlugins = sets . New ( "foo" )
cl := testingclock . NewFakeClock ( now )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithQueueingHintMapPerProfile ( m ) , WithClock ( cl ) )
// add to unsched pod pool
2023-07-17 18:53:07 -04:00
q . activeQ . Add ( q . newQueuedPodInfo ( test . podInfo . Pod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != test . podInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , test . podInfo . Pod . Name , p . Pod . Name )
}
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , test . podInfo , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2023-06-08 00:54:30 -04:00
cl . Step ( test . duration )
q . MoveAllToActiveOrBackoffQueue ( logger , NodeAdd , nil , nil , nil )
if q . podBackoffQ . Len ( ) == 0 && test . expectedQ == backoffQ {
t . Fatalf ( "expected pod to be queued to backoffQ, but it was not" )
}
if q . activeQ . Len ( ) == 0 && test . expectedQ == activeQ {
t . Fatalf ( "expected pod to be queued to activeQ, but it was not" )
}
if q . unschedulablePods . get ( test . podInfo . Pod ) == nil && test . expectedQ == unschedulablePods {
t . Fatalf ( "expected pod to be queued to unschedulablePods, but it was not" )
}
} )
}
}
2020-02-13 19:31:07 -05:00
func TestPriorityQueue_MoveAllToActiveOrBackoffQueue ( t * testing . T ) {
2021-09-17 05:48:22 -04:00
c := testingclock . NewFakeClock ( time . Now ( ) )
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
2023-06-08 00:54:30 -04:00
m := makeEmptyQueueingHintMapPerProfile ( )
m [ "" ] [ NodeAdd ] = [ ] * QueueingHintFunction {
{
PluginName : "fooPlugin" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} ,
}
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( c ) , WithQueueingHintMapPerProfile ( m ) )
2023-07-17 18:53:07 -04:00
// To simulate the pod is failed in scheduling in the real world, Pop() the pod from activeQ before AddUnschedulableIfNotPresent()s below.
q . activeQ . Add ( q . newQueuedPodInfo ( unschedulablePodInfo . Pod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != unschedulablePodInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , unschedulablePodInfo . Pod . Name , p . Pod . Name )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , unschedulablePodInfo . Pod . UID )
2023-07-17 18:53:07 -04:00
q . activeQ . Add ( q . newQueuedPodInfo ( highPriorityPodInfo . Pod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != highPriorityPodInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , highPriorityPodInfo . Pod . Name , p . Pod . Name )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , unschedulablePodInfo . Pod . UID , highPriorityPodInfo . Pod . UID )
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( unschedulablePodInfo . Pod , "fooPlugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
err = q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( highPriorityPodInfo . Pod , "fooPlugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q )
2021-01-29 01:29:10 -05:00
// Construct a Pod, but don't associate its scheduler failure to any plugin
2023-09-04 09:48:45 -04:00
hpp1 := clonePod ( highPriorityPodInfo . Pod , "hpp1" )
2023-07-17 18:53:07 -04:00
q . activeQ . Add ( q . newQueuedPodInfo ( hpp1 ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != hpp1 {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , hpp1 , p . Pod . Name )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , hpp1 . UID )
2023-09-11 12:30:11 -04:00
// This Pod will go to backoffQ because no failure plugin is associated with it.
err = q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( hpp1 ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q )
2021-01-29 01:29:10 -05:00
// Construct another Pod, and associate its scheduler failure to plugin "barPlugin".
2023-09-04 09:48:45 -04:00
hpp2 := clonePod ( highPriorityPodInfo . Pod , "hpp2" )
2023-07-17 18:53:07 -04:00
q . activeQ . Add ( q . newQueuedPodInfo ( hpp2 ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != hpp2 {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , hpp2 , p . Pod . Name )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , hpp2 . UID )
2023-09-11 12:30:11 -04:00
// This Pod will go to the unschedulable Pod pool.
err = q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( hpp2 , "barPlugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q )
2023-09-11 12:30:11 -04:00
// This NodeAdd event moves unschedulablePodInfo and highPriorityPodInfo to the backoffQ,
// because of the queueing hint function registered for NodeAdd/fooPlugin.
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , NodeAdd , nil , nil , nil )
2023-07-17 18:53:07 -04:00
q . Add ( logger , medPriorityPodInfo . Pod )
2020-02-13 19:31:07 -05:00
if q . activeQ . Len ( ) != 1 {
2020-09-02 01:28:45 -04:00
t . Errorf ( "Expected 1 item to be in activeQ, but got: %v" , q . activeQ . Len ( ) )
2020-02-13 19:31:07 -05:00
}
2023-07-17 18:53:07 -04:00
// Pop out the medPriorityPodInfo in activeQ.
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != medPriorityPodInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , medPriorityPodInfo . Pod , p . Pod . Name )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , medPriorityPodInfo . Pod . UID )
2021-01-29 01:29:10 -05:00
// hpp2 won't be moved.
if q . podBackoffQ . Len ( ) != 3 {
t . Fatalf ( "Expected 3 items to be in podBackoffQ, but got: %v" , q . podBackoffQ . Len ( ) )
2020-09-02 01:28:45 -04:00
}
// pop out the pods in the backoffQ.
2023-09-04 09:48:45 -04:00
// This doesn't make them in-flight pods.
2021-01-29 01:29:10 -05:00
for q . podBackoffQ . Len ( ) != 0 {
q . podBackoffQ . Pop ( )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , medPriorityPodInfo . Pod . UID )
2020-09-02 01:28:45 -04:00
q . schedulingCycle ++
2023-07-17 18:53:07 -04:00
q . activeQ . Add ( q . newQueuedPodInfo ( unschedulablePodInfo . Pod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != unschedulablePodInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , unschedulablePodInfo . Pod . Name , p . Pod . Name )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , medPriorityPodInfo . Pod . UID , unschedulablePodInfo . Pod . UID )
2023-07-17 18:53:07 -04:00
q . activeQ . Add ( q . newQueuedPodInfo ( highPriorityPodInfo . Pod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != highPriorityPodInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , highPriorityPodInfo . Pod . Name , p . Pod . Name )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , medPriorityPodInfo . Pod . UID , unschedulablePodInfo . Pod . UID , highPriorityPodInfo . Pod . UID )
2023-07-17 18:53:07 -04:00
q . activeQ . Add ( q . newQueuedPodInfo ( hpp1 ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != hpp1 {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , hpp1 , p . Pod . Name )
}
2023-09-11 12:30:11 -04:00
unschedulableQueuedPodInfo := q . newQueuedPodInfo ( unschedulablePodInfo . Pod , "fooPlugin" )
highPriorityQueuedPodInfo := q . newQueuedPodInfo ( highPriorityPodInfo . Pod , "fooPlugin" )
hpp1QueuedPodInfo := q . newQueuedPodInfo ( hpp1 )
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , medPriorityPodInfo . Pod . UID , unschedulablePodInfo . Pod . UID , highPriorityPodInfo . Pod . UID , hpp1 . UID )
2023-09-11 12:30:11 -04:00
err = q . AddUnschedulableIfNotPresent ( logger , unschedulableQueuedPodInfo , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , medPriorityPodInfo . Pod . UID , highPriorityPodInfo . Pod . UID , hpp1 . UID )
2023-09-11 12:30:11 -04:00
err = q . AddUnschedulableIfNotPresent ( logger , highPriorityQueuedPodInfo , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , medPriorityPodInfo . Pod . UID , hpp1 . UID )
2023-09-11 12:30:11 -04:00
err = q . AddUnschedulableIfNotPresent ( logger , hpp1QueuedPodInfo , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , medPriorityPodInfo . Pod . UID )
2023-07-17 18:53:07 -04:00
q . Add ( logger , medPriorityPodInfo . Pod )
2023-09-11 12:30:11 -04:00
// hpp1 will go to backoffQ because no failure plugin is associated with it.
// All plugins other than hpp1 are enqueued to the unschedulable Pod pool.
for _ , pod := range [ ] * v1 . Pod { unschedulablePodInfo . Pod , highPriorityPodInfo . Pod , hpp2 } {
2022-03-24 05:38:49 -04:00
if q . unschedulablePods . get ( pod ) == nil {
t . Errorf ( "Expected %v in the unschedulablePods" , pod . Name )
2021-01-29 01:29:10 -05:00
}
2020-09-02 01:28:45 -04:00
}
2023-09-11 12:30:11 -04:00
if _ , ok , _ := q . podBackoffQ . Get ( hpp1QueuedPodInfo ) ; ! ok {
t . Errorf ( "Expected %v in the podBackoffQ" , hpp1 . Name )
}
2022-03-24 05:38:49 -04:00
// Move clock by podInitialBackoffDuration, so that pods in the unschedulablePods would pass the backing off,
2020-09-02 01:28:45 -04:00
// and the pods will be moved into activeQ.
c . Step ( q . podInitialBackoffDuration )
2023-09-11 12:30:11 -04:00
q . flushBackoffQCompleted ( logger ) // flush the completed backoffQ to move hpp1 to activeQ.
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , NodeAdd , nil , nil , nil )
2021-01-29 01:29:10 -05:00
if q . activeQ . Len ( ) != 4 {
t . Errorf ( "Expected 4 items to be in activeQ, but got: %v" , q . activeQ . Len ( ) )
2017-10-24 14:14:29 -04:00
}
2020-09-02 01:28:45 -04:00
if q . podBackoffQ . Len ( ) != 0 {
t . Errorf ( "Expected 0 item to be in podBackoffQ, but got: %v" , q . podBackoffQ . Len ( ) )
}
2023-09-04 09:48:45 -04:00
expectInFlightPods ( t , q , medPriorityPodInfo . Pod . UID )
2023-09-11 12:30:11 -04:00
if len ( q . unschedulablePods . podInfoMap ) != 1 {
// hpp2 won't be moved regardless of its backoff timer.
t . Errorf ( "Expected 1 item to be in unschedulablePods, but got: %v" , len ( q . unschedulablePods . podInfoMap ) )
}
2023-09-04 09:48:45 -04:00
}
func clonePod ( pod * v1 . Pod , newName string ) * v1 . Pod {
pod = pod . DeepCopy ( )
pod . Name = newName
pod . UID = types . UID ( pod . Name + pod . Namespace )
return pod
}
func expectInFlightPods ( t * testing . T , q * PriorityQueue , uids ... types . UID ) {
t . Helper ( )
var actualUIDs [ ] types . UID
for uid := range q . inFlightPods {
actualUIDs = append ( actualUIDs , uid )
}
sortUIDs := cmpopts . SortSlices ( func ( a , b types . UID ) bool { return a < b } )
if diff := cmp . Diff ( uids , actualUIDs , sortUIDs ) ; diff != "" {
t . Fatalf ( "Unexpected content of inFlightPods (-want, +have):\n%s" , diff )
}
actualUIDs = nil
for e := q . inFlightEvents . Front ( ) ; e != nil ; e = e . Next ( ) {
if pod , ok := e . Value . ( * v1 . Pod ) ; ok {
actualUIDs = append ( actualUIDs , pod . UID )
}
}
if diff := cmp . Diff ( uids , actualUIDs , sortUIDs ) ; diff != "" {
t . Fatalf ( "Unexpected pods in inFlightEvents (-want, +have):\n%s" , diff )
}
2017-10-24 14:14:29 -04:00
}
// TestPriorityQueue_AssignedPodAdded tests AssignedPodAdded. It checks that
2022-03-24 05:38:49 -04:00
// when a pod with pod affinity is in unschedulablePods and another pod with a
2017-10-24 14:14:29 -04:00
// matching label is added, the unschedulable pod is moved to activeQ.
func TestPriorityQueue_AssignedPodAdded ( t * testing . T ) {
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
defer cancel ( )
2023-07-17 18:53:07 -04:00
affinityPod := st . MakePod ( ) . Name ( "afp" ) . Namespace ( "ns1" ) . UID ( "afp" ) . Annotation ( "annot2" , "val2" ) . Priority ( mediumPriority ) . NominatedNodeName ( "node1" ) . PodAffinityExists ( "service" , "region" , st . PodAffinityWithRequiredReq ) . Obj ( )
2022-05-05 13:48:26 -04:00
labelPod := st . MakePod ( ) . Name ( "lbp" ) . Namespace ( affinityPod . Namespace ) . Label ( "service" , "securityscan" ) . Node ( "node1" ) . Obj ( )
2017-10-24 14:14:29 -04:00
2021-09-17 05:48:22 -04:00
c := testingclock . NewFakeClock ( time . Now ( ) )
2023-06-08 00:54:30 -04:00
m := makeEmptyQueueingHintMapPerProfile ( )
m [ "" ] [ AssignedPodAdd ] = [ ] * QueueingHintFunction {
{
PluginName : "fakePlugin" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} ,
}
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( c ) , WithQueueingHintMapPerProfile ( m ) )
2023-07-17 18:53:07 -04:00
// To simulate the pod is failed in scheduling in the real world, Pop() the pod from activeQ before AddUnschedulableIfNotPresent()s below.
q . activeQ . Add ( q . newQueuedPodInfo ( unschedulablePodInfo . Pod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != unschedulablePodInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , unschedulablePodInfo . Pod . Name , p . Pod . Name )
}
q . activeQ . Add ( q . newQueuedPodInfo ( affinityPod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != affinityPod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , affinityPod . Name , p . Pod . Name )
}
2023-03-22 07:48:04 -04:00
q . Add ( logger , medPriorityPodInfo . Pod )
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( unschedulablePodInfo . Pod , "fakePlugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
err = q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( affinityPod , "fakePlugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2020-02-13 19:31:07 -05:00
// Move clock to make the unschedulable pods complete backoff.
c . Step ( DefaultPodInitialBackoffDuration + time . Second )
2017-10-24 14:14:29 -04:00
// Simulate addition of an assigned pod. The pod has matching labels for
// affinityPod. So, affinityPod should go to activeQ.
2023-03-22 07:48:04 -04:00
q . AssignedPodAdded ( logger , labelPod )
2019-01-17 15:24:18 -05:00
if getUnschedulablePod ( q , affinityPod ) != nil {
2022-03-24 05:38:49 -04:00
t . Error ( "affinityPod is still in the unschedulablePods." )
2017-10-24 14:14:29 -04:00
}
2021-02-22 09:00:23 -05:00
if _ , exists , _ := q . activeQ . Get ( newQueuedPodInfoForLookup ( affinityPod ) ) ; ! exists {
2017-10-24 14:14:29 -04:00
t . Error ( "affinityPod is not moved to activeQ." )
}
2022-03-24 05:38:49 -04:00
// Check that the other pod is still in the unschedulablePods.
2021-02-22 09:00:23 -05:00
if getUnschedulablePod ( q , unschedulablePodInfo . Pod ) == nil {
2022-03-24 05:38:49 -04:00
t . Error ( "unschedulablePodInfo is not in the unschedulablePods." )
2017-10-24 14:14:29 -04:00
}
}
2018-12-18 02:41:53 -05:00
func TestPriorityQueue_NominatedPodsForNode ( t * testing . T ) {
2021-06-05 14:30:17 -04:00
objs := [ ] runtime . Object { medPriorityPodInfo . Pod , unschedulablePodInfo . Pod , highPriorityPodInfo . Pod }
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , objs )
2023-03-22 07:48:04 -04:00
q . Add ( logger , medPriorityPodInfo . Pod )
q . Add ( logger , unschedulablePodInfo . Pod )
q . Add ( logger , highPriorityPodInfo . Pod )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != highPriorityPodInfo . Pod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , highPriorityPodInfo . Pod . Name , p . Pod . Name )
2018-02-08 21:19:31 -05:00
}
2021-02-22 09:00:23 -05:00
expectedList := [ ] * framework . PodInfo { medPriorityPodInfo , unschedulablePodInfo }
2021-07-08 23:56:43 -04:00
podInfos := q . NominatedPodsForNode ( "node1" )
if diff := cmp . Diff ( expectedList , podInfos ) ; diff != "" {
t . Errorf ( "Unexpected list of nominated Pods for node: (-want, +got):\n%s" , diff )
2018-02-08 21:19:31 -05:00
}
2021-07-08 23:56:43 -04:00
podInfos [ 0 ] . Pod . Name = "not mpp"
if diff := cmp . Diff ( podInfos , q . NominatedPodsForNode ( "node1" ) ) ; diff == "" {
t . Error ( "Expected list of nominated Pods for node2 is different from podInfos" )
}
if len ( q . NominatedPodsForNode ( "node2" ) ) != 0 {
2018-02-08 21:19:31 -05:00
t . Error ( "Expected list of nominated Pods for node2 to be empty." )
}
}
2021-06-04 14:04:44 -04:00
func TestPriorityQueue_NominatedPodDeleted ( t * testing . T ) {
tests := [ ] struct {
name string
podInfo * framework . PodInfo
deletePod bool
want bool
} {
{
name : "alive pod gets added into PodNominator" ,
podInfo : medPriorityPodInfo ,
want : true ,
} ,
{
name : "deleted pod shouldn't be added into PodNominator" ,
podInfo : highPriNominatedPodInfo ,
deletePod : true ,
want : false ,
} ,
{
name : "pod without .status.nominatedPodName specified shouldn't be added into PodNominator" ,
podInfo : highPriorityPodInfo ,
want : false ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2023-03-22 07:48:04 -04:00
logger , _ := ktesting . NewTestContext ( t )
2021-06-04 14:04:44 -04:00
cs := fake . NewSimpleClientset ( tt . podInfo . Pod )
informerFactory := informers . NewSharedInformerFactory ( cs , 0 )
podLister := informerFactory . Core ( ) . V1 ( ) . Pods ( ) . Lister ( )
// Build a PriorityQueue.
2023-03-08 16:18:36 -05:00
q := NewPriorityQueue ( newDefaultQueueSort ( ) , informerFactory , WithPodLister ( podLister ) )
2022-01-22 14:00:56 -05:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
2021-06-04 14:04:44 -04:00
informerFactory . Start ( ctx . Done ( ) )
informerFactory . WaitForCacheSync ( ctx . Done ( ) )
if tt . deletePod {
// Simulate that the test pod gets deleted physically.
informerFactory . Core ( ) . V1 ( ) . Pods ( ) . Informer ( ) . GetStore ( ) . Delete ( tt . podInfo . Pod )
}
2023-03-22 07:48:04 -04:00
q . AddNominatedPod ( logger , tt . podInfo , nil )
2021-06-04 14:04:44 -04:00
if got := len ( q . NominatedPodsForNode ( tt . podInfo . Pod . Status . NominatedNodeName ) ) == 1 ; got != tt . want {
t . Errorf ( "Want %v, but got %v" , tt . want , got )
}
} )
}
}
2018-12-20 20:28:23 -05:00
func TestPriorityQueue_PendingPods ( t * testing . T ) {
2019-02-11 19:38:26 -05:00
makeSet := func ( pods [ ] * v1 . Pod ) map [ * v1 . Pod ] struct { } {
pendingSet := map [ * v1 . Pod ] struct { } { }
for _ , p := range pods {
pendingSet [ p ] = struct { } { }
}
return pendingSet
}
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) )
2023-07-17 18:53:07 -04:00
// To simulate the pod is failed in scheduling in the real world, Pop() the pod from activeQ before AddUnschedulableIfNotPresent()s below.
q . activeQ . Add ( q . newQueuedPodInfo ( unschedulablePodInfo . Pod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != unschedulablePodInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , unschedulablePodInfo . Pod . Name , p . Pod . Name )
}
q . activeQ . Add ( q . newQueuedPodInfo ( highPriorityPodInfo . Pod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != highPriorityPodInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , highPriorityPodInfo . Pod . Name , p . Pod . Name )
}
2023-03-22 07:48:04 -04:00
q . Add ( logger , medPriorityPodInfo . Pod )
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( unschedulablePodInfo . Pod , "plugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
err = q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( highPriorityPodInfo . Pod , "plugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2020-02-13 19:31:07 -05:00
2021-02-22 09:00:23 -05:00
expectedSet := makeSet ( [ ] * v1 . Pod { medPriorityPodInfo . Pod , unschedulablePodInfo . Pod , highPriorityPodInfo . Pod } )
2022-08-05 21:30:09 -04:00
gotPods , gotSummary := q . PendingPods ( )
2023-06-29 03:28:42 -04:00
if diff := cmp . Diff ( expectedSet , makeSet ( gotPods ) ) ; diff != "" {
t . Errorf ( "Unexpected list of pending Pods (-want, +got):\n%s" , diff )
2018-12-20 20:28:23 -05:00
}
2022-08-05 21:30:09 -04:00
if wantSummary := fmt . Sprintf ( pendingPodsSummary , 1 , 0 , 2 ) ; wantSummary != gotSummary {
t . Errorf ( "Unexpected pending pods summary: want %v, but got %v." , wantSummary , gotSummary )
}
2018-12-20 20:28:23 -05:00
// Move all to active queue. We should still see the same set of pods.
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , TestEvent , nil , nil , nil )
2022-08-05 21:30:09 -04:00
gotPods , gotSummary = q . PendingPods ( )
2023-06-29 03:28:42 -04:00
if diff := cmp . Diff ( expectedSet , makeSet ( gotPods ) ) ; diff != "" {
t . Errorf ( "Unexpected list of pending Pods (-want, +got):\n%s" , diff )
2022-08-05 21:30:09 -04:00
}
if wantSummary := fmt . Sprintf ( pendingPodsSummary , 1 , 2 , 0 ) ; wantSummary != gotSummary {
t . Errorf ( "Unexpected pending pods summary: want %v, but got %v." , wantSummary , gotSummary )
2018-12-20 20:28:23 -05:00
}
}
2018-12-18 02:41:53 -05:00
func TestPriorityQueue_UpdateNominatedPodForNode ( t * testing . T ) {
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
2023-03-22 07:48:04 -04:00
objs := [ ] runtime . Object { medPriorityPodInfo . Pod , unschedulablePodInfo . Pod , highPriorityPodInfo . Pod , scheduledPodInfo . Pod }
2022-01-22 14:00:56 -05:00
q := NewTestQueueWithObjects ( ctx , newDefaultQueueSort ( ) , objs )
2023-03-22 07:48:04 -04:00
if err := q . Add ( logger , medPriorityPodInfo . Pod ) ; err != nil {
2018-12-18 02:41:53 -05:00
t . Errorf ( "add failed: %v" , err )
}
2021-02-22 09:00:23 -05:00
// Update unschedulablePodInfo on a different node than specified in the pod.
2023-03-22 07:48:04 -04:00
q . AddNominatedPod ( logger , mustNewTestPodInfo ( t , unschedulablePodInfo . Pod ) ,
2021-12-16 13:54:58 -05:00
& framework . NominatingInfo { NominatingMode : framework . ModeOverride , NominatedNodeName : "node5" } )
2018-12-18 02:41:53 -05:00
// Update nominated node name of a pod on a node that is not specified in the pod object.
2023-03-22 07:48:04 -04:00
q . AddNominatedPod ( logger , mustNewTestPodInfo ( t , highPriorityPodInfo . Pod ) ,
2021-12-16 13:54:58 -05:00
& framework . NominatingInfo { NominatingMode : framework . ModeOverride , NominatedNodeName : "node2" } )
2021-06-05 14:30:17 -04:00
expectedNominatedPods := & nominator {
2018-12-18 02:41:53 -05:00
nominatedPodToNode : map [ types . UID ] string {
2021-02-22 09:00:23 -05:00
medPriorityPodInfo . Pod . UID : "node1" ,
highPriorityPodInfo . Pod . UID : "node2" ,
unschedulablePodInfo . Pod . UID : "node5" ,
2018-12-18 02:41:53 -05:00
} ,
2021-02-22 09:00:23 -05:00
nominatedPods : map [ string ] [ ] * framework . PodInfo {
"node1" : { medPriorityPodInfo } ,
"node2" : { highPriorityPodInfo } ,
"node5" : { unschedulablePodInfo } ,
2018-12-18 02:41:53 -05:00
} ,
}
2023-03-08 16:18:36 -05:00
if diff := cmp . Diff ( q . nominator , expectedNominatedPods , nominatorCmpOpts ... ) ; diff != "" {
2021-06-04 14:04:44 -04:00
t . Errorf ( "Unexpected diff after adding pods (-want, +got):\n%s" , diff )
2018-12-18 02:41:53 -05:00
}
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != medPriorityPodInfo . Pod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , medPriorityPodInfo . Pod . Name , p . Pod . Name )
2018-12-18 02:41:53 -05:00
}
// List of nominated pods shouldn't change after popping them from the queue.
2023-03-08 16:18:36 -05:00
if diff := cmp . Diff ( q . nominator , expectedNominatedPods , nominatorCmpOpts ... ) ; diff != "" {
2021-06-04 14:04:44 -04:00
t . Errorf ( "Unexpected diff after popping pods (-want, +got):\n%s" , diff )
2018-12-18 02:41:53 -05:00
}
// Update one of the nominated pods that doesn't have nominatedNodeName in the
// pod object. It should be updated correctly.
2023-03-22 07:48:04 -04:00
q . AddNominatedPod ( logger , highPriorityPodInfo , & framework . NominatingInfo { NominatingMode : framework . ModeOverride , NominatedNodeName : "node4" } )
2021-06-05 14:30:17 -04:00
expectedNominatedPods = & nominator {
2018-12-18 02:41:53 -05:00
nominatedPodToNode : map [ types . UID ] string {
2021-02-22 09:00:23 -05:00
medPriorityPodInfo . Pod . UID : "node1" ,
highPriorityPodInfo . Pod . UID : "node4" ,
unschedulablePodInfo . Pod . UID : "node5" ,
2018-12-18 02:41:53 -05:00
} ,
2021-02-22 09:00:23 -05:00
nominatedPods : map [ string ] [ ] * framework . PodInfo {
"node1" : { medPriorityPodInfo } ,
"node4" : { highPriorityPodInfo } ,
"node5" : { unschedulablePodInfo } ,
2018-12-18 02:41:53 -05:00
} ,
}
2023-03-08 16:18:36 -05:00
if diff := cmp . Diff ( q . nominator , expectedNominatedPods , nominatorCmpOpts ... ) ; diff != "" {
2021-06-04 14:04:44 -04:00
t . Errorf ( "Unexpected diff after updating pods (-want, +got):\n%s" , diff )
2018-12-18 02:41:53 -05:00
}
2022-04-01 15:21:10 -04:00
// Attempt to nominate a pod that was deleted from the informer cache.
// Nothing should change.
2023-03-22 07:48:04 -04:00
q . AddNominatedPod ( logger , nonExistentPodInfo , & framework . NominatingInfo { NominatingMode : framework . ModeOverride , NominatedNodeName : "node1" } )
2023-03-08 16:18:36 -05:00
if diff := cmp . Diff ( q . nominator , expectedNominatedPods , nominatorCmpOpts ... ) ; diff != "" {
2022-04-01 15:21:10 -04:00
t . Errorf ( "Unexpected diff after nominating a deleted pod (-want, +got):\n%s" , diff )
}
// Attempt to nominate a pod that was already scheduled in the informer cache.
// Nothing should change.
scheduledPodCopy := scheduledPodInfo . Pod . DeepCopy ( )
scheduledPodInfo . Pod . Spec . NodeName = ""
2023-03-22 07:48:04 -04:00
q . AddNominatedPod ( logger , mustNewTestPodInfo ( t , scheduledPodCopy ) , & framework . NominatingInfo { NominatingMode : framework . ModeOverride , NominatedNodeName : "node1" } )
2023-03-08 16:18:36 -05:00
if diff := cmp . Diff ( q . nominator , expectedNominatedPods , nominatorCmpOpts ... ) ; diff != "" {
2022-04-01 15:21:10 -04:00
t . Errorf ( "Unexpected diff after nominating a scheduled pod (-want, +got):\n%s" , diff )
}
2018-12-18 02:41:53 -05:00
// Delete a nominated pod that doesn't have nominatedNodeName in the pod
// object. It should be deleted.
2021-02-22 09:00:23 -05:00
q . DeleteNominatedPodIfExists ( highPriorityPodInfo . Pod )
2021-06-05 14:30:17 -04:00
expectedNominatedPods = & nominator {
2018-12-18 02:41:53 -05:00
nominatedPodToNode : map [ types . UID ] string {
2021-02-22 09:00:23 -05:00
medPriorityPodInfo . Pod . UID : "node1" ,
unschedulablePodInfo . Pod . UID : "node5" ,
2018-12-18 02:41:53 -05:00
} ,
2021-02-22 09:00:23 -05:00
nominatedPods : map [ string ] [ ] * framework . PodInfo {
"node1" : { medPriorityPodInfo } ,
"node5" : { unschedulablePodInfo } ,
2018-12-18 02:41:53 -05:00
} ,
}
2023-03-08 16:18:36 -05:00
if diff := cmp . Diff ( q . nominator , expectedNominatedPods , nominatorCmpOpts ... ) ; diff != "" {
2021-06-04 14:04:44 -04:00
t . Errorf ( "Unexpected diff after deleting pods (-want, +got):\n%s" , diff )
2018-12-18 02:41:53 -05:00
}
}
2019-08-11 20:26:32 -04:00
func TestPriorityQueue_NewWithOptions ( t * testing . T ) {
2022-01-22 14:00:56 -05:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
q := NewTestQueue ( ctx ,
2020-02-13 11:08:46 -05:00
newDefaultQueueSort ( ) ,
2019-08-11 20:26:32 -04:00
WithPodInitialBackoffDuration ( 2 * time . Second ) ,
WithPodMaxBackoffDuration ( 20 * time . Second ) ,
)
2020-02-13 19:31:07 -05:00
if q . podInitialBackoffDuration != 2 * time . Second {
t . Errorf ( "Unexpected pod backoff initial duration. Expected: %v, got: %v" , 2 * time . Second , q . podInitialBackoffDuration )
2019-08-11 20:26:32 -04:00
}
2020-02-13 19:31:07 -05:00
if q . podMaxBackoffDuration != 20 * time . Second {
t . Errorf ( "Unexpected pod backoff max duration. Expected: %v, got: %v" , 2 * time . Second , q . podMaxBackoffDuration )
2019-08-11 20:26:32 -04:00
}
}
2017-10-24 14:14:29 -04:00
func TestUnschedulablePodsMap ( t * testing . T ) {
var pods = [ ] * v1 . Pod {
2022-04-18 21:06:58 -04:00
st . MakePod ( ) . Name ( "p0" ) . Namespace ( "ns1" ) . Annotation ( "annot1" , "val1" ) . NominatedNodeName ( "node1" ) . Obj ( ) ,
st . MakePod ( ) . Name ( "p1" ) . Namespace ( "ns1" ) . Annotation ( "annot" , "val" ) . Obj ( ) ,
st . MakePod ( ) . Name ( "p2" ) . Namespace ( "ns2" ) . Annotation ( "annot2" , "val2" ) . Annotation ( "annot3" , "val3" ) . NominatedNodeName ( "node3" ) . Obj ( ) ,
st . MakePod ( ) . Name ( "p3" ) . Namespace ( "ns4" ) . Annotation ( "annot2" , "val2" ) . Annotation ( "annot3" , "val3" ) . NominatedNodeName ( "node1" ) . Obj ( ) ,
2017-10-24 14:14:29 -04:00
}
var updatedPods = make ( [ ] * v1 . Pod , len ( pods ) )
updatedPods [ 0 ] = pods [ 0 ] . DeepCopy ( )
updatedPods [ 1 ] = pods [ 1 ] . DeepCopy ( )
updatedPods [ 3 ] = pods [ 3 ] . DeepCopy ( )
tests := [ ] struct {
2018-06-01 10:16:50 -04:00
name string
2018-02-08 21:19:31 -05:00
podsToAdd [ ] * v1 . Pod
2020-04-20 20:36:26 -04:00
expectedMapAfterAdd map [ string ] * framework . QueuedPodInfo
2018-02-08 21:19:31 -05:00
podsToUpdate [ ] * v1 . Pod
2020-04-20 20:36:26 -04:00
expectedMapAfterUpdate map [ string ] * framework . QueuedPodInfo
2018-02-08 21:19:31 -05:00
podsToDelete [ ] * v1 . Pod
2020-04-20 20:36:26 -04:00
expectedMapAfterDelete map [ string ] * framework . QueuedPodInfo
2017-10-24 14:14:29 -04:00
} {
{
2018-06-01 10:16:50 -04:00
name : "create, update, delete subset of pods" ,
2017-10-24 14:14:29 -04:00
podsToAdd : [ ] * v1 . Pod { pods [ 0 ] , pods [ 1 ] , pods [ 2 ] , pods [ 3 ] } ,
2020-04-20 20:36:26 -04:00
expectedMapAfterAdd : map [ string ] * framework . QueuedPodInfo {
2023-03-27 03:46:13 -04:00
util . GetPodFullName ( pods [ 0 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 0 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 1 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 1 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 2 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 2 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 3 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 3 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
2017-10-24 14:14:29 -04:00
} ,
podsToUpdate : [ ] * v1 . Pod { updatedPods [ 0 ] } ,
2020-04-20 20:36:26 -04:00
expectedMapAfterUpdate : map [ string ] * framework . QueuedPodInfo {
2023-03-27 03:46:13 -04:00
util . GetPodFullName ( pods [ 0 ] ) : { PodInfo : mustNewTestPodInfo ( t , updatedPods [ 0 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 1 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 1 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 2 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 2 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 3 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 3 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
2017-10-24 14:14:29 -04:00
} ,
podsToDelete : [ ] * v1 . Pod { pods [ 0 ] , pods [ 1 ] } ,
2020-04-20 20:36:26 -04:00
expectedMapAfterDelete : map [ string ] * framework . QueuedPodInfo {
2023-03-27 03:46:13 -04:00
util . GetPodFullName ( pods [ 2 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 2 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 3 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 3 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
2017-10-24 14:14:29 -04:00
} ,
} ,
{
2018-06-01 10:16:50 -04:00
name : "create, update, delete all" ,
2017-10-24 14:14:29 -04:00
podsToAdd : [ ] * v1 . Pod { pods [ 0 ] , pods [ 3 ] } ,
2020-04-20 20:36:26 -04:00
expectedMapAfterAdd : map [ string ] * framework . QueuedPodInfo {
2023-03-27 03:46:13 -04:00
util . GetPodFullName ( pods [ 0 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 0 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 3 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 3 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
2017-10-24 14:14:29 -04:00
} ,
podsToUpdate : [ ] * v1 . Pod { updatedPods [ 3 ] } ,
2020-04-20 20:36:26 -04:00
expectedMapAfterUpdate : map [ string ] * framework . QueuedPodInfo {
2023-03-27 03:46:13 -04:00
util . GetPodFullName ( pods [ 0 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 0 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 3 ] ) : { PodInfo : mustNewTestPodInfo ( t , updatedPods [ 3 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
2017-10-24 14:14:29 -04:00
} ,
2018-02-08 21:19:31 -05:00
podsToDelete : [ ] * v1 . Pod { pods [ 0 ] , pods [ 3 ] } ,
2020-04-20 20:36:26 -04:00
expectedMapAfterDelete : map [ string ] * framework . QueuedPodInfo { } ,
2017-10-24 14:14:29 -04:00
} ,
{
2018-06-01 10:16:50 -04:00
name : "delete non-existing and existing pods" ,
2017-10-24 14:14:29 -04:00
podsToAdd : [ ] * v1 . Pod { pods [ 1 ] , pods [ 2 ] } ,
2020-04-20 20:36:26 -04:00
expectedMapAfterAdd : map [ string ] * framework . QueuedPodInfo {
2023-03-27 03:46:13 -04:00
util . GetPodFullName ( pods [ 1 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 1 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 2 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 2 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
2017-10-24 14:14:29 -04:00
} ,
podsToUpdate : [ ] * v1 . Pod { updatedPods [ 1 ] } ,
2020-04-20 20:36:26 -04:00
expectedMapAfterUpdate : map [ string ] * framework . QueuedPodInfo {
2023-03-27 03:46:13 -04:00
util . GetPodFullName ( pods [ 1 ] ) : { PodInfo : mustNewTestPodInfo ( t , updatedPods [ 1 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
util . GetPodFullName ( pods [ 2 ] ) : { PodInfo : mustNewTestPodInfo ( t , pods [ 2 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
2017-10-24 14:14:29 -04:00
} ,
podsToDelete : [ ] * v1 . Pod { pods [ 2 ] , pods [ 3 ] } ,
2020-04-20 20:36:26 -04:00
expectedMapAfterDelete : map [ string ] * framework . QueuedPodInfo {
2023-03-27 03:46:13 -04:00
util . GetPodFullName ( pods [ 1 ] ) : { PodInfo : mustNewTestPodInfo ( t , updatedPods [ 1 ] ) , UnschedulablePlugins : sets . New [ string ] ( ) } ,
2017-10-24 14:14:29 -04:00
} ,
} ,
}
2018-06-01 10:16:50 -04:00
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
2022-11-07 17:02:22 -05:00
upm := newUnschedulablePods ( nil , nil )
2018-06-01 10:16:50 -04:00
for _ , p := range test . podsToAdd {
2021-02-22 09:00:23 -05:00
upm . addOrUpdate ( newQueuedPodInfoForLookup ( p ) )
2017-10-24 14:14:29 -04:00
}
2023-06-29 03:28:42 -04:00
if diff := cmp . Diff ( test . expectedMapAfterAdd , upm . podInfoMap ) ; diff != "" {
t . Errorf ( "Unexpected map after adding pods(-want, +got):\n%s" , diff )
2017-10-24 14:14:29 -04:00
}
2018-06-01 10:16:50 -04:00
if len ( test . podsToUpdate ) > 0 {
for _ , p := range test . podsToUpdate {
2021-02-22 09:00:23 -05:00
upm . addOrUpdate ( newQueuedPodInfoForLookup ( p ) )
2018-06-01 10:16:50 -04:00
}
2023-06-29 03:28:42 -04:00
if diff := cmp . Diff ( test . expectedMapAfterUpdate , upm . podInfoMap ) ; diff != "" {
t . Errorf ( "Unexpected map after updating pods (-want, +got):\n%s" , diff )
2018-06-01 10:16:50 -04:00
}
}
for _ , p := range test . podsToDelete {
2022-11-22 00:33:16 -05:00
upm . delete ( p , false )
2018-06-01 10:16:50 -04:00
}
2023-06-29 03:28:42 -04:00
if diff := cmp . Diff ( test . expectedMapAfterDelete , upm . podInfoMap ) ; diff != "" {
t . Errorf ( "Unexpected map after deleting pods (-want, +got):\n%s" , diff )
2018-06-01 10:16:50 -04:00
}
upm . clear ( )
2019-02-21 20:46:18 -05:00
if len ( upm . podInfoMap ) != 0 {
t . Errorf ( "Expected the map to be empty, but has %v elements." , len ( upm . podInfoMap ) )
2018-06-01 10:16:50 -04:00
}
} )
2017-10-24 14:14:29 -04:00
}
}
2018-09-14 19:59:54 -04:00
func TestSchedulingQueue_Close ( t * testing . T ) {
2023-09-28 22:59:22 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) )
2020-06-07 09:52:16 -04:00
wg := sync . WaitGroup { }
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
2023-09-28 22:59:22 -04:00
pod , err := q . Pop ( logger )
2023-03-14 08:22:22 -04:00
if err != nil {
t . Errorf ( "Expected nil err from Pop() if queue is closed, but got %q" , err . Error ( ) )
2020-06-07 09:52:16 -04:00
}
if pod != nil {
t . Errorf ( "Expected pod nil from Pop() if queue is closed, but got: %v" , pod )
}
} ( )
q . Close ( )
wg . Wait ( )
2018-09-14 19:59:54 -04:00
}
2018-11-27 19:46:24 -05:00
// TestRecentlyTriedPodsGoBack tests that pods which are recently tried and are
// unschedulable go behind other pods with the same priority. This behavior
// ensures that an unschedulable pod does not block head of the queue when there
// are frequent events that move pods to the active queue.
func TestRecentlyTriedPodsGoBack ( t * testing . T ) {
2021-09-17 05:48:22 -04:00
c := testingclock . NewFakeClock ( time . Now ( ) )
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( c ) )
2018-11-27 19:46:24 -05:00
// Add a few pods to priority queue.
for i := 0 ; i < 5 ; i ++ {
2022-04-18 21:06:58 -04:00
p := st . MakePod ( ) . Name ( fmt . Sprintf ( "test-pod-%v" , i ) ) . Namespace ( "ns1" ) . UID ( fmt . Sprintf ( "tp00%v" , i ) ) . Priority ( highPriority ) . Node ( "node1" ) . NominatedNodeName ( "node1" ) . Obj ( )
2023-03-22 07:48:04 -04:00
q . Add ( logger , p )
2018-11-27 19:46:24 -05:00
}
2020-05-05 03:11:04 -04:00
c . Step ( time . Microsecond )
2018-11-27 19:46:24 -05:00
// Simulate a pod being popped by the scheduler, determined unschedulable, and
// then moved back to the active queue.
2023-09-28 22:59:22 -04:00
p1 , err := q . Pop ( logger )
2018-11-27 19:46:24 -05:00
if err != nil {
t . Errorf ( "Error while popping the head of the queue: %v" , err )
}
// Update pod condition to unschedulable.
2021-02-22 09:00:23 -05:00
podutil . UpdatePodCondition ( & p1 . PodInfo . Pod . Status , & v1 . PodCondition {
2018-12-29 09:24:51 -05:00
Type : v1 . PodScheduled ,
Status : v1 . ConditionFalse ,
Reason : v1 . PodReasonUnschedulable ,
Message : "fake scheduling failure" ,
LastProbeTime : metav1 . Now ( ) ,
2018-11-27 19:46:24 -05:00
} )
2023-09-11 12:30:11 -04:00
p1 . UnschedulablePlugins = sets . New ( "plugin" )
2018-11-27 19:46:24 -05:00
// Put in the unschedulable queue.
2023-09-11 12:30:11 -04:00
err = q . AddUnschedulableIfNotPresent ( logger , p1 , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2020-05-05 03:11:04 -04:00
c . Step ( DefaultPodInitialBackoffDuration )
2018-11-27 19:46:24 -05:00
// Move all unschedulable pods to the active queue.
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , UnschedulableTimeout , nil , nil , nil )
2018-11-27 19:46:24 -05:00
// Simulation is over. Now let's pop all pods. The pod popped first should be
// the last one we pop here.
for i := 0 ; i < 5 ; i ++ {
2023-09-28 22:59:22 -04:00
p , err := q . Pop ( logger )
2018-11-27 19:46:24 -05:00
if err != nil {
t . Errorf ( "Error while popping pods from the queue: %v" , err )
}
if ( i == 4 ) != ( p1 == p ) {
2021-02-22 09:00:23 -05:00
t . Errorf ( "A pod tried before is not the last pod popped: i: %v, pod name: %v" , i , p . PodInfo . Pod . Name )
2018-11-27 19:46:24 -05:00
}
}
}
2018-07-24 16:46:40 -04:00
2018-12-29 09:24:51 -05:00
// TestPodFailedSchedulingMultipleTimesDoesNotBlockNewerPod tests
// that a pod determined as unschedulable multiple times doesn't block any newer pod.
// This behavior ensures that an unschedulable pod does not block head of the queue when there
// are frequent events that move pods to the active queue.
func TestPodFailedSchedulingMultipleTimesDoesNotBlockNewerPod ( t * testing . T ) {
2021-09-17 05:48:22 -04:00
c := testingclock . NewFakeClock ( time . Now ( ) )
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( c ) )
2018-12-29 09:24:51 -05:00
// Add an unschedulable pod to a priority queue.
// This makes a situation that the pod was tried to schedule
2019-09-17 14:19:56 -04:00
// and had been determined unschedulable so far
2023-03-08 16:18:36 -05:00
unschedulablePod := st . MakePod ( ) . Name ( "test-pod-unscheduled" ) . Namespace ( "ns1" ) . UID ( "tp001" ) . Priority ( highPriority ) . NominatedNodeName ( "node1" ) . Obj ( )
2019-02-21 20:46:18 -05:00
2018-12-29 09:24:51 -05:00
// Update pod condition to unschedulable.
podutil . UpdatePodCondition ( & unschedulablePod . Status , & v1 . PodCondition {
2019-02-21 20:46:18 -05:00
Type : v1 . PodScheduled ,
Status : v1 . ConditionFalse ,
Reason : v1 . PodReasonUnschedulable ,
Message : "fake scheduling failure" ,
2018-12-29 09:24:51 -05:00
} )
2019-02-21 20:46:18 -05:00
2023-07-17 18:53:07 -04:00
// To simulate the pod is failed in scheduling in the real world, Pop() the pod from activeQ before AddUnschedulableIfNotPresent() below.
q . activeQ . Add ( q . newQueuedPodInfo ( unschedulablePod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != unschedulablePod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , unschedulablePod . Name , p . Pod . Name )
}
2018-12-29 09:24:51 -05:00
// Put in the unschedulable queue
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , newQueuedPodInfoForLookup ( unschedulablePod , "plugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2020-02-13 19:31:07 -05:00
// Move clock to make the unschedulable pods complete backoff.
c . Step ( DefaultPodInitialBackoffDuration + time . Second )
2018-12-29 09:24:51 -05:00
// Move all unschedulable pods to the active queue.
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , UnschedulableTimeout , nil , nil , nil )
2018-12-29 09:24:51 -05:00
// Simulate a pod being popped by the scheduler,
// At this time, unschedulable pod should be popped.
2023-09-28 22:59:22 -04:00
p1 , err := q . Pop ( logger )
2018-12-29 09:24:51 -05:00
if err != nil {
t . Errorf ( "Error while popping the head of the queue: %v" , err )
}
2022-04-18 21:06:58 -04:00
if p1 . Pod != unschedulablePod {
2019-10-07 19:06:00 -04:00
t . Errorf ( "Expected that test-pod-unscheduled was popped, got %v" , p1 . Pod . Name )
2018-12-29 09:24:51 -05:00
}
// Assume newer pod was added just after unschedulable pod
// being popped and before being pushed back to the queue.
2022-04-18 21:06:58 -04:00
newerPod := st . MakePod ( ) . Name ( "test-newer-pod" ) . Namespace ( "ns1" ) . UID ( "tp002" ) . CreationTimestamp ( metav1 . Now ( ) ) . Priority ( highPriority ) . NominatedNodeName ( "node1" ) . Obj ( )
2023-03-22 07:48:04 -04:00
q . Add ( logger , newerPod )
2018-12-29 09:24:51 -05:00
2021-02-22 09:00:23 -05:00
// And then unschedulablePodInfo was determined as unschedulable AGAIN.
2018-12-29 09:24:51 -05:00
podutil . UpdatePodCondition ( & unschedulablePod . Status , & v1 . PodCondition {
2019-02-21 20:46:18 -05:00
Type : v1 . PodScheduled ,
Status : v1 . ConditionFalse ,
Reason : v1 . PodReasonUnschedulable ,
Message : "fake scheduling failure" ,
2018-12-29 09:24:51 -05:00
} )
2019-02-21 20:46:18 -05:00
2018-12-29 09:24:51 -05:00
// And then, put unschedulable pod to the unschedulable queue
2023-09-11 12:30:11 -04:00
err = q . AddUnschedulableIfNotPresent ( logger , newQueuedPodInfoForLookup ( unschedulablePod , "plugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2020-02-13 19:31:07 -05:00
// Move clock to make the unschedulable pods complete backoff.
c . Step ( DefaultPodInitialBackoffDuration + time . Second )
2018-12-29 09:24:51 -05:00
// Move all unschedulable pods to the active queue.
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , UnschedulableTimeout , nil , nil , nil )
2018-12-29 09:24:51 -05:00
// At this time, newerPod should be popped
// because it is the oldest tried pod.
2023-09-28 22:59:22 -04:00
p2 , err2 := q . Pop ( logger )
2018-12-29 09:24:51 -05:00
if err2 != nil {
t . Errorf ( "Error while popping the head of the queue: %v" , err2 )
}
2022-04-18 21:06:58 -04:00
if p2 . Pod != newerPod {
2019-10-07 19:06:00 -04:00
t . Errorf ( "Expected that test-newer-pod was popped, got %v" , p2 . Pod . Name )
2018-12-29 09:24:51 -05:00
}
}
2018-07-24 16:46:40 -04:00
// TestHighPriorityBackoff tests that a high priority pod does not block
// other pods if it is unschedulable
2019-06-05 16:04:22 -04:00
func TestHighPriorityBackoff ( t * testing . T ) {
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) )
2018-07-24 16:46:40 -04:00
2022-04-18 21:06:58 -04:00
midPod := st . MakePod ( ) . Name ( "test-midpod" ) . Namespace ( "ns1" ) . UID ( "tp-mid" ) . Priority ( midPriority ) . NominatedNodeName ( "node1" ) . Obj ( )
highPod := st . MakePod ( ) . Name ( "test-highpod" ) . Namespace ( "ns1" ) . UID ( "tp-high" ) . Priority ( highPriority ) . NominatedNodeName ( "node1" ) . Obj ( )
2023-03-22 07:48:04 -04:00
q . Add ( logger , midPod )
q . Add ( logger , highPod )
2018-07-24 16:46:40 -04:00
// Simulate a pod being popped by the scheduler, determined unschedulable, and
// then moved back to the active queue.
2023-09-28 22:59:22 -04:00
p , err := q . Pop ( logger )
2018-07-24 16:46:40 -04:00
if err != nil {
t . Errorf ( "Error while popping the head of the queue: %v" , err )
}
2022-04-18 21:06:58 -04:00
if p . Pod != highPod {
2019-06-05 16:04:22 -04:00
t . Errorf ( "Expected to get high priority pod, got: %v" , p )
2018-07-24 16:46:40 -04:00
}
// Update pod condition to unschedulable.
2019-10-07 19:06:00 -04:00
podutil . UpdatePodCondition ( & p . Pod . Status , & v1 . PodCondition {
2018-07-24 16:46:40 -04:00
Type : v1 . PodScheduled ,
Status : v1 . ConditionFalse ,
Reason : v1 . PodReasonUnschedulable ,
Message : "fake scheduling failure" ,
} )
// Put in the unschedulable queue.
2023-09-11 12:30:11 -04:00
err = q . AddUnschedulableIfNotPresent ( logger , p , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2018-07-24 16:46:40 -04:00
// Move all unschedulable pods to the active queue.
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , TestEvent , nil , nil , nil )
2018-07-24 16:46:40 -04:00
2023-09-28 22:59:22 -04:00
p , err = q . Pop ( logger )
2018-07-24 16:46:40 -04:00
if err != nil {
t . Errorf ( "Error while popping the head of the queue: %v" , err )
}
2022-04-18 21:06:58 -04:00
if p . Pod != midPod {
2019-06-05 16:04:22 -04:00
t . Errorf ( "Expected to get mid priority pod, got: %v" , p )
2018-07-24 16:46:40 -04:00
}
}
2019-01-15 23:08:19 -05:00
2022-03-24 05:38:49 -04:00
// TestHighPriorityFlushUnschedulablePodsLeftover tests that pods will be moved to
// activeQ after one minutes if it is in unschedulablePods.
func TestHighPriorityFlushUnschedulablePodsLeftover ( t * testing . T ) {
2021-09-17 05:48:22 -04:00
c := testingclock . NewFakeClock ( time . Now ( ) )
2023-06-08 00:54:30 -04:00
m := makeEmptyQueueingHintMapPerProfile ( )
m [ "" ] [ NodeAdd ] = [ ] * QueueingHintFunction {
{
PluginName : "fakePlugin" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} ,
2021-01-29 01:29:10 -05:00
}
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
2023-06-08 00:54:30 -04:00
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( c ) , WithQueueingHintMapPerProfile ( m ) )
2022-04-18 21:06:58 -04:00
midPod := st . MakePod ( ) . Name ( "test-midpod" ) . Namespace ( "ns1" ) . UID ( "tp-mid" ) . Priority ( midPriority ) . NominatedNodeName ( "node1" ) . Obj ( )
highPod := st . MakePod ( ) . Name ( "test-highpod" ) . Namespace ( "ns1" ) . UID ( "tp-high" ) . Priority ( highPriority ) . NominatedNodeName ( "node1" ) . Obj ( )
2019-01-15 23:08:19 -05:00
// Update pod condition to highPod.
podutil . UpdatePodCondition ( & highPod . Status , & v1 . PodCondition {
2019-02-21 20:46:18 -05:00
Type : v1 . PodScheduled ,
Status : v1 . ConditionFalse ,
Reason : v1 . PodReasonUnschedulable ,
Message : "fake scheduling failure" ,
2019-01-15 23:08:19 -05:00
} )
// Update pod condition to midPod.
podutil . UpdatePodCondition ( & midPod . Status , & v1 . PodCondition {
2019-02-21 20:46:18 -05:00
Type : v1 . PodScheduled ,
Status : v1 . ConditionFalse ,
Reason : v1 . PodReasonUnschedulable ,
Message : "fake scheduling failure" ,
2019-01-15 23:08:19 -05:00
} )
2023-07-17 18:53:07 -04:00
// To simulate the pod is failed in scheduling in the real world, Pop() the pod from activeQ before AddUnschedulableIfNotPresent()s below.
q . activeQ . Add ( q . newQueuedPodInfo ( highPod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != highPod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , highPod . Name , p . Pod . Name )
}
q . activeQ . Add ( q . newQueuedPodInfo ( midPod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != midPod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , midPod . Name , p . Pod . Name )
}
2023-09-11 12:30:11 -04:00
err := q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( highPod , "fakePlugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
err = q . AddUnschedulableIfNotPresent ( logger , q . newQueuedPodInfo ( midPod , "fakePlugin" ) , q . SchedulingCycle ( ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2022-03-24 05:38:49 -04:00
c . Step ( DefaultPodMaxInUnschedulablePodsDuration + time . Second )
2023-03-22 07:48:04 -04:00
q . flushUnschedulablePodsLeftover ( logger )
2019-02-21 20:46:18 -05:00
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != highPod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , highPriorityPodInfo . Pod . Name , p . Pod . Name )
2019-01-15 23:08:19 -05:00
}
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != midPod {
2021-02-22 09:00:23 -05:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , medPriorityPodInfo . Pod . Name , p . Pod . Name )
2019-01-15 23:08:19 -05:00
}
}
2019-02-21 20:46:18 -05:00
2022-03-24 05:38:49 -04:00
func TestPriorityQueue_initPodMaxInUnschedulablePodsDuration ( t * testing . T ) {
2022-04-18 21:06:58 -04:00
pod1 := st . MakePod ( ) . Name ( "test-pod-1" ) . Namespace ( "ns1" ) . UID ( "tp-1" ) . NominatedNodeName ( "node1" ) . Obj ( )
pod2 := st . MakePod ( ) . Name ( "test-pod-2" ) . Namespace ( "ns2" ) . UID ( "tp-2" ) . NominatedNodeName ( "node2" ) . Obj ( )
2022-02-15 22:05:52 -05:00
var timestamp = time . Now ( )
pInfo1 := & framework . QueuedPodInfo {
2022-10-12 10:11:04 -04:00
PodInfo : mustNewTestPodInfo ( t , pod1 ) ,
2022-02-15 22:05:52 -05:00
Timestamp : timestamp . Add ( - time . Second ) ,
}
pInfo2 := & framework . QueuedPodInfo {
2022-10-12 10:11:04 -04:00
PodInfo : mustNewTestPodInfo ( t , pod2 ) ,
2022-02-15 22:05:52 -05:00
Timestamp : timestamp . Add ( - 2 * time . Second ) ,
}
tests := [ ] struct {
2022-03-24 05:38:49 -04:00
name string
podMaxInUnschedulablePodsDuration time . Duration
operations [ ] operation
operands [ ] * framework . QueuedPodInfo
expected [ ] * framework . QueuedPodInfo
2022-02-15 22:05:52 -05:00
} {
{
2022-03-24 05:38:49 -04:00
name : "New priority queue by the default value of podMaxInUnschedulablePodsDuration" ,
2022-02-15 22:05:52 -05:00
operations : [ ] operation {
2022-03-24 05:38:49 -04:00
addPodUnschedulablePods ,
addPodUnschedulablePods ,
2022-02-15 22:05:52 -05:00
flushUnschedulerQ ,
} ,
operands : [ ] * framework . QueuedPodInfo { pInfo1 , pInfo2 , nil } ,
expected : [ ] * framework . QueuedPodInfo { pInfo2 , pInfo1 } ,
} ,
{
2022-03-24 05:38:49 -04:00
name : "New priority queue by user-defined value of podMaxInUnschedulablePodsDuration" ,
podMaxInUnschedulablePodsDuration : 30 * time . Second ,
2022-02-15 22:05:52 -05:00
operations : [ ] operation {
2022-03-24 05:38:49 -04:00
addPodUnschedulablePods ,
addPodUnschedulablePods ,
2022-02-15 22:05:52 -05:00
flushUnschedulerQ ,
} ,
operands : [ ] * framework . QueuedPodInfo { pInfo1 , pInfo2 , nil } ,
expected : [ ] * framework . QueuedPodInfo { pInfo2 , pInfo1 } ,
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-02-15 22:05:52 -05:00
defer cancel ( )
var queue * PriorityQueue
2022-03-24 05:38:49 -04:00
if test . podMaxInUnschedulablePodsDuration > 0 {
2022-02-15 22:05:52 -05:00
queue = NewTestQueue ( ctx , newDefaultQueueSort ( ) ,
WithClock ( testingclock . NewFakeClock ( timestamp ) ) ,
2022-03-24 05:38:49 -04:00
WithPodMaxInUnschedulablePodsDuration ( test . podMaxInUnschedulablePodsDuration ) )
2022-02-15 22:05:52 -05:00
} else {
queue = NewTestQueue ( ctx , newDefaultQueueSort ( ) ,
WithClock ( testingclock . NewFakeClock ( timestamp ) ) )
}
var podInfoList [ ] * framework . QueuedPodInfo
for i , op := range test . operations {
2023-09-05 14:46:33 -04:00
op ( t , logger , queue , test . operands [ i ] )
2022-02-15 22:05:52 -05:00
}
expectedLen := len ( test . expected )
if queue . activeQ . Len ( ) != expectedLen {
t . Fatalf ( "Expected %v items to be in activeQ, but got: %v" , expectedLen , queue . activeQ . Len ( ) )
}
for i := 0 ; i < expectedLen ; i ++ {
if pInfo , err := queue . activeQ . Pop ( ) ; err != nil {
t . Errorf ( "Error while popping the head of the queue: %v" , err )
} else {
podInfoList = append ( podInfoList , pInfo . ( * framework . QueuedPodInfo ) )
}
}
if diff := cmp . Diff ( test . expected , podInfoList ) ; diff != "" {
t . Errorf ( "Unexpected QueuedPodInfo list (-want, +got):\n%s" , diff )
}
} )
}
}
2023-09-05 14:46:33 -04:00
type operation func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , pInfo * framework . QueuedPodInfo )
2019-03-19 20:02:45 -04:00
var (
2023-09-05 14:46:33 -04:00
add = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , pInfo * framework . QueuedPodInfo ) {
if err := queue . Add ( logger , pInfo . Pod ) ; err != nil {
t . Fatalf ( "Unexpected error during Add: %v" , err )
}
2019-10-07 13:29:53 -04:00
}
2023-09-05 14:46:33 -04:00
popAndRequeueAsUnschedulable = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , pInfo * framework . QueuedPodInfo ) {
2023-07-17 18:53:07 -04:00
// To simulate the pod is failed in scheduling in the real world, Pop() the pod from activeQ before AddUnschedulableIfNotPresent() below.
2023-09-05 14:46:33 -04:00
// UnschedulablePlugins will get cleared by Pop, so make a copy first.
unschedulablePlugins := pInfo . UnschedulablePlugins . Clone ( )
if err := queue . activeQ . Add ( queue . newQueuedPodInfo ( pInfo . Pod ) ) ; err != nil {
t . Fatalf ( "Unexpected error during Add: %v" , err )
}
2023-09-28 22:59:22 -04:00
p , err := queue . Pop ( logger )
2023-09-05 14:46:33 -04:00
if err != nil {
t . Fatalf ( "Unexpected error during Pop: %v" , err )
}
if p . Pod != pInfo . Pod {
t . Fatalf ( "Expected: %v after Pop, but got: %v" , pInfo . Pod . Name , p . Pod . Name )
}
// Simulate plugins that are waiting for some events.
p . UnschedulablePlugins = unschedulablePlugins
if err := queue . AddUnschedulableIfNotPresent ( logger , p , 1 ) ; err != nil {
t . Fatalf ( "Unexpected error during AddUnschedulableIfNotPresent: %v" , err )
2023-07-17 18:53:07 -04:00
}
2019-10-07 13:29:53 -04:00
}
2023-09-05 14:46:33 -04:00
popAndRequeueAsBackoff = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , pInfo * framework . QueuedPodInfo ) {
// To simulate the pod is failed in scheduling in the real world, Pop() the pod from activeQ before AddUnschedulableIfNotPresent() below.
if err := queue . activeQ . Add ( queue . newQueuedPodInfo ( pInfo . Pod ) ) ; err != nil {
t . Fatalf ( "Unexpected error during Add: %v" , err )
}
2023-09-28 22:59:22 -04:00
p , err := queue . Pop ( logger )
2023-09-05 14:46:33 -04:00
if err != nil {
t . Fatalf ( "Unexpected error during Pop: %v" , err )
}
if p . Pod != pInfo . Pod {
t . Fatalf ( "Expected: %v after Pop, but got: %v" , pInfo . Pod . Name , p . Pod . Name )
}
// When there is no known unschedulable plugin, pods always go to the backoff queue.
if err := queue . AddUnschedulableIfNotPresent ( logger , p , 1 ) ; err != nil {
t . Fatalf ( "Unexpected error during AddUnschedulableIfNotPresent: %v" , err )
}
2019-10-07 13:29:53 -04:00
}
2023-09-05 14:46:33 -04:00
addPodActiveQ = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , pInfo * framework . QueuedPodInfo ) {
2019-03-19 20:02:45 -04:00
queue . activeQ . Add ( pInfo )
}
2023-09-05 14:46:33 -04:00
updatePodActiveQ = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , pInfo * framework . QueuedPodInfo ) {
2019-03-19 20:02:45 -04:00
queue . activeQ . Update ( pInfo )
}
2023-09-05 14:46:33 -04:00
addPodUnschedulablePods = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , pInfo * framework . QueuedPodInfo ) {
2022-11-07 17:02:22 -05:00
if ! pInfo . Gated {
// Update pod condition to unschedulable.
podutil . UpdatePodCondition ( & pInfo . Pod . Status , & v1 . PodCondition {
Type : v1 . PodScheduled ,
Status : v1 . ConditionFalse ,
Reason : v1 . PodReasonUnschedulable ,
Message : "fake scheduling failure" ,
} )
}
2022-03-24 05:38:49 -04:00
queue . unschedulablePods . addOrUpdate ( pInfo )
2019-03-19 20:02:45 -04:00
}
2023-09-05 14:46:33 -04:00
deletePod = func ( t * testing . T , _ klog . Logger , queue * PriorityQueue , pInfo * framework . QueuedPodInfo ) {
2022-11-22 00:33:16 -05:00
queue . Delete ( pInfo . Pod )
}
2023-09-05 14:46:33 -04:00
updatePodQueueable = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , pInfo * framework . QueuedPodInfo ) {
2022-11-22 00:33:16 -05:00
newPod := pInfo . Pod . DeepCopy ( )
newPod . Labels = map [ string ] string { "queueable" : "" }
2023-03-22 07:48:04 -04:00
queue . Update ( logger , pInfo . Pod , newPod )
2022-11-22 00:33:16 -05:00
}
2023-09-05 14:46:33 -04:00
addPodBackoffQ = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , pInfo * framework . QueuedPodInfo ) {
2019-03-19 20:02:45 -04:00
queue . podBackoffQ . Add ( pInfo )
}
2023-09-05 14:46:33 -04:00
moveAllToActiveOrBackoffQ = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , _ * framework . QueuedPodInfo ) {
2023-06-08 00:54:30 -04:00
queue . MoveAllToActiveOrBackoffQueue ( logger , UnschedulableTimeout , nil , nil , nil )
2019-03-19 20:02:45 -04:00
}
2023-09-05 14:46:33 -04:00
flushBackoffQ = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , _ * framework . QueuedPodInfo ) {
2021-09-17 05:48:22 -04:00
queue . clock . ( * testingclock . FakeClock ) . Step ( 2 * time . Second )
2023-03-22 07:48:04 -04:00
queue . flushBackoffQCompleted ( logger )
2019-03-19 20:02:45 -04:00
}
2023-09-05 14:46:33 -04:00
moveClockForward = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , _ * framework . QueuedPodInfo ) {
2021-09-17 05:48:22 -04:00
queue . clock . ( * testingclock . FakeClock ) . Step ( 2 * time . Second )
2019-10-07 13:29:53 -04:00
}
2023-09-05 14:46:33 -04:00
flushUnschedulerQ = func ( t * testing . T , logger klog . Logger , queue * PriorityQueue , _ * framework . QueuedPodInfo ) {
2022-03-24 05:38:49 -04:00
queue . clock . ( * testingclock . FakeClock ) . Step ( queue . podMaxInUnschedulablePodsDuration )
2023-03-22 07:48:04 -04:00
queue . flushUnschedulablePodsLeftover ( logger )
2022-02-15 22:05:52 -05:00
}
2019-03-19 20:02:45 -04:00
)
2020-04-20 20:36:26 -04:00
// TestPodTimestamp tests the operations related to QueuedPodInfo.
2019-02-21 20:46:18 -05:00
func TestPodTimestamp ( t * testing . T ) {
2022-04-18 21:06:58 -04:00
pod1 := st . MakePod ( ) . Name ( "test-pod-1" ) . Namespace ( "ns1" ) . UID ( "tp-1" ) . NominatedNodeName ( "node1" ) . Obj ( )
pod2 := st . MakePod ( ) . Name ( "test-pod-2" ) . Namespace ( "ns2" ) . UID ( "tp-2" ) . NominatedNodeName ( "node2" ) . Obj ( )
2019-02-21 20:46:18 -05:00
2019-02-27 18:23:27 -05:00
var timestamp = time . Now ( )
2020-04-20 20:36:26 -04:00
pInfo1 := & framework . QueuedPodInfo {
2022-10-12 10:11:04 -04:00
PodInfo : mustNewTestPodInfo ( t , pod1 ) ,
2019-05-06 21:03:00 -04:00
Timestamp : timestamp ,
2019-02-21 20:46:18 -05:00
}
2020-04-20 20:36:26 -04:00
pInfo2 := & framework . QueuedPodInfo {
2022-10-12 10:11:04 -04:00
PodInfo : mustNewTestPodInfo ( t , pod2 ) ,
2019-05-06 21:03:00 -04:00
Timestamp : timestamp . Add ( time . Second ) ,
2019-02-21 20:46:18 -05:00
}
tests := [ ] struct {
name string
operations [ ] operation
2020-04-20 20:36:26 -04:00
operands [ ] * framework . QueuedPodInfo
expected [ ] * framework . QueuedPodInfo
2019-02-21 20:46:18 -05:00
} {
{
name : "add two pod to activeQ and sort them by the timestamp" ,
operations : [ ] operation {
2019-03-19 20:02:45 -04:00
addPodActiveQ ,
addPodActiveQ ,
2019-02-21 20:46:18 -05:00
} ,
2020-04-20 20:36:26 -04:00
operands : [ ] * framework . QueuedPodInfo { pInfo2 , pInfo1 } ,
expected : [ ] * framework . QueuedPodInfo { pInfo1 , pInfo2 } ,
2019-02-21 20:46:18 -05:00
} ,
{
name : "update two pod to activeQ and sort them by the timestamp" ,
operations : [ ] operation {
2019-03-19 20:02:45 -04:00
updatePodActiveQ ,
updatePodActiveQ ,
2019-02-21 20:46:18 -05:00
} ,
2020-04-20 20:36:26 -04:00
operands : [ ] * framework . QueuedPodInfo { pInfo2 , pInfo1 } ,
expected : [ ] * framework . QueuedPodInfo { pInfo1 , pInfo2 } ,
2019-02-21 20:46:18 -05:00
} ,
{
2022-03-24 05:38:49 -04:00
name : "add two pod to unschedulablePods then move them to activeQ and sort them by the timestamp" ,
2019-02-21 20:46:18 -05:00
operations : [ ] operation {
2022-03-24 05:38:49 -04:00
addPodUnschedulablePods ,
addPodUnschedulablePods ,
2020-02-13 19:31:07 -05:00
moveClockForward ,
2019-10-07 13:29:53 -04:00
moveAllToActiveOrBackoffQ ,
2019-02-21 20:46:18 -05:00
} ,
2020-04-20 20:36:26 -04:00
operands : [ ] * framework . QueuedPodInfo { pInfo2 , pInfo1 , nil , nil } ,
expected : [ ] * framework . QueuedPodInfo { pInfo1 , pInfo2 } ,
2019-02-21 20:46:18 -05:00
} ,
{
name : "add one pod to BackoffQ and move it to activeQ" ,
operations : [ ] operation {
2019-03-19 20:02:45 -04:00
addPodActiveQ ,
addPodBackoffQ ,
flushBackoffQ ,
2019-10-07 13:29:53 -04:00
moveAllToActiveOrBackoffQ ,
2019-02-21 20:46:18 -05:00
} ,
2020-04-20 20:36:26 -04:00
operands : [ ] * framework . QueuedPodInfo { pInfo2 , pInfo1 , nil , nil } ,
expected : [ ] * framework . QueuedPodInfo { pInfo1 , pInfo2 } ,
2019-02-21 20:46:18 -05:00
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
queue := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( testingclock . NewFakeClock ( timestamp ) ) )
2020-04-20 20:36:26 -04:00
var podInfoList [ ] * framework . QueuedPodInfo
2019-02-21 20:46:18 -05:00
2019-03-19 20:02:45 -04:00
for i , op := range test . operations {
2023-09-05 14:46:33 -04:00
op ( t , logger , queue , test . operands [ i ] )
2019-02-21 20:46:18 -05:00
}
2022-02-08 04:40:45 -05:00
expectedLen := len ( test . expected )
if queue . activeQ . Len ( ) != expectedLen {
t . Fatalf ( "Expected %v items to be in activeQ, but got: %v" , expectedLen , queue . activeQ . Len ( ) )
}
for i := 0 ; i < expectedLen ; i ++ {
2019-02-21 20:46:18 -05:00
if pInfo , err := queue . activeQ . Pop ( ) ; err != nil {
t . Errorf ( "Error while popping the head of the queue: %v" , err )
} else {
2020-04-20 20:36:26 -04:00
podInfoList = append ( podInfoList , pInfo . ( * framework . QueuedPodInfo ) )
2019-02-21 20:46:18 -05:00
}
}
2023-06-29 03:28:42 -04:00
if diff := cmp . Diff ( test . expected , podInfoList ) ; diff != "" {
t . Errorf ( "Unexpected QueuedPodInfo list (-want, +got):\n%s" , diff )
2019-02-21 20:46:18 -05:00
}
} )
}
}
2019-03-20 02:53:33 -04:00
// TestPendingPodsMetric tests Prometheus metrics related with pending pods
func TestPendingPodsMetric ( t * testing . T ) {
timestamp := time . Now ( )
2019-08-22 21:00:06 -04:00
metrics . Register ( )
2022-11-07 17:02:22 -05:00
total := 60
queueableNum := 50
2022-11-22 00:33:16 -05:00
queueable , failme := "queueable" , "failme"
2022-11-07 17:02:22 -05:00
// First 50 Pods are queueable.
2022-11-22 00:33:16 -05:00
pInfos := makeQueuedPodInfos ( queueableNum , "x" , queueable , timestamp )
2022-11-07 17:02:22 -05:00
// The last 10 Pods are not queueable.
2022-11-22 00:33:16 -05:00
gated := makeQueuedPodInfos ( total - queueableNum , "y" , failme , timestamp )
2022-11-07 17:02:22 -05:00
// Manually mark them as gated=true.
for _ , pInfo := range gated {
pInfo . Gated = true
}
pInfos = append ( pInfos , gated ... )
2020-02-13 19:31:07 -05:00
totalWithDelay := 20
2022-11-22 00:33:16 -05:00
pInfosWithDelay := makeQueuedPodInfos ( totalWithDelay , "z" , queueable , timestamp . Add ( 2 * time . Second ) )
2020-02-13 19:31:07 -05:00
2019-03-20 02:53:33 -04:00
tests := [ ] struct {
2023-03-01 21:57:50 -05:00
name string
operations [ ] operation
operands [ ] [ ] * framework . QueuedPodInfo
metricsName string
pluginMetricsSamplePercent int
wants string
2019-03-20 02:53:33 -04:00
} {
{
2022-03-24 05:38:49 -04:00
name : "add pods to activeQ and unschedulablePods" ,
2019-03-20 02:53:33 -04:00
operations : [ ] operation {
addPodActiveQ ,
2022-03-24 05:38:49 -04:00
addPodUnschedulablePods ,
2019-03-20 02:53:33 -04:00
} ,
2020-04-20 20:36:26 -04:00
operands : [ ] [ ] * framework . QueuedPodInfo {
2019-03-20 02:53:33 -04:00
pInfos [ : 30 ] ,
pInfos [ 30 : ] ,
} ,
2019-09-29 22:05:50 -04:00
metricsName : "scheduler_pending_pods" ,
wants : `
2022-11-07 17:02:22 -05:00
# HELP scheduler_pending_pods [ STABLE ] Number of pending pods , by the queue type . ' active ' means number of pods in activeQ ; ' backoff ' means number of pods in backoffQ ; ' unschedulable ' means number of pods in unschedulablePods that the scheduler attempted to schedule and failed ; ' gated ' is the number of unschedulable pods that the scheduler never attempted to schedule because they are gated .
2019-09-29 22:05:50 -04:00
# TYPE scheduler_pending_pods gauge
scheduler_pending_pods { queue = "active" } 30
scheduler_pending_pods { queue = "backoff" } 0
2022-11-07 17:02:22 -05:00
scheduler_pending_pods { queue = "gated" } 10
2019-09-29 22:05:50 -04:00
scheduler_pending_pods { queue = "unschedulable" } 20
` ,
2019-03-20 02:53:33 -04:00
} ,
{
name : "add pods to all kinds of queues" ,
operations : [ ] operation {
addPodActiveQ ,
addPodBackoffQ ,
2022-03-24 05:38:49 -04:00
addPodUnschedulablePods ,
2019-03-20 02:53:33 -04:00
} ,
2020-04-20 20:36:26 -04:00
operands : [ ] [ ] * framework . QueuedPodInfo {
2019-03-20 02:53:33 -04:00
pInfos [ : 15 ] ,
pInfos [ 15 : 40 ] ,
pInfos [ 40 : ] ,
} ,
2019-09-29 22:05:50 -04:00
metricsName : "scheduler_pending_pods" ,
wants : `
2022-11-07 17:02:22 -05:00
# HELP scheduler_pending_pods [ STABLE ] Number of pending pods , by the queue type . ' active ' means number of pods in activeQ ; ' backoff ' means number of pods in backoffQ ; ' unschedulable ' means number of pods in unschedulablePods that the scheduler attempted to schedule and failed ; ' gated ' is the number of unschedulable pods that the scheduler never attempted to schedule because they are gated .
2019-09-29 22:05:50 -04:00
# TYPE scheduler_pending_pods gauge
scheduler_pending_pods { queue = "active" } 15
scheduler_pending_pods { queue = "backoff" } 25
2022-11-07 17:02:22 -05:00
scheduler_pending_pods { queue = "gated" } 10
2019-09-29 22:05:50 -04:00
scheduler_pending_pods { queue = "unschedulable" } 10
` ,
2019-03-20 02:53:33 -04:00
} ,
{
2022-03-24 05:38:49 -04:00
name : "add pods to unschedulablePods and then move all to activeQ" ,
2019-03-20 02:53:33 -04:00
operations : [ ] operation {
2022-03-24 05:38:49 -04:00
addPodUnschedulablePods ,
2020-02-13 19:31:07 -05:00
moveClockForward ,
2019-10-07 13:29:53 -04:00
moveAllToActiveOrBackoffQ ,
2019-03-20 02:53:33 -04:00
} ,
2020-04-20 20:36:26 -04:00
operands : [ ] [ ] * framework . QueuedPodInfo {
2019-03-20 02:53:33 -04:00
pInfos [ : total ] ,
{ nil } ,
2020-02-13 19:31:07 -05:00
{ nil } ,
2019-03-20 02:53:33 -04:00
} ,
2019-09-29 22:05:50 -04:00
metricsName : "scheduler_pending_pods" ,
wants : `
2022-11-07 17:02:22 -05:00
# HELP scheduler_pending_pods [ STABLE ] Number of pending pods , by the queue type . ' active ' means number of pods in activeQ ; ' backoff ' means number of pods in backoffQ ; ' unschedulable ' means number of pods in unschedulablePods that the scheduler attempted to schedule and failed ; ' gated ' is the number of unschedulable pods that the scheduler never attempted to schedule because they are gated .
2019-09-29 22:05:50 -04:00
# TYPE scheduler_pending_pods gauge
scheduler_pending_pods { queue = "active" } 50
scheduler_pending_pods { queue = "backoff" } 0
2022-11-07 17:02:22 -05:00
scheduler_pending_pods { queue = "gated" } 10
2019-09-29 22:05:50 -04:00
scheduler_pending_pods { queue = "unschedulable" } 0
` ,
2019-03-20 02:53:33 -04:00
} ,
{
2022-03-24 05:38:49 -04:00
name : "make some pods subject to backoff, add pods to unschedulablePods, and then move all to activeQ" ,
2019-03-20 02:53:33 -04:00
operations : [ ] operation {
2022-03-24 05:38:49 -04:00
addPodUnschedulablePods ,
2020-02-13 19:31:07 -05:00
moveClockForward ,
2022-03-24 05:38:49 -04:00
addPodUnschedulablePods ,
2019-10-07 13:29:53 -04:00
moveAllToActiveOrBackoffQ ,
2019-03-20 02:53:33 -04:00
} ,
2020-04-20 20:36:26 -04:00
operands : [ ] [ ] * framework . QueuedPodInfo {
2020-02-13 19:31:07 -05:00
pInfos [ 20 : total ] ,
{ nil } ,
pInfosWithDelay [ : 20 ] ,
2019-03-20 02:53:33 -04:00
{ nil } ,
} ,
2019-09-29 22:05:50 -04:00
metricsName : "scheduler_pending_pods" ,
wants : `
2022-11-07 17:02:22 -05:00
# HELP scheduler_pending_pods [ STABLE ] Number of pending pods , by the queue type . ' active ' means number of pods in activeQ ; ' backoff ' means number of pods in backoffQ ; ' unschedulable ' means number of pods in unschedulablePods that the scheduler attempted to schedule and failed ; ' gated ' is the number of unschedulable pods that the scheduler never attempted to schedule because they are gated .
2019-09-29 22:05:50 -04:00
# TYPE scheduler_pending_pods gauge
scheduler_pending_pods { queue = "active" } 30
scheduler_pending_pods { queue = "backoff" } 20
2022-11-07 17:02:22 -05:00
scheduler_pending_pods { queue = "gated" } 10
2019-09-29 22:05:50 -04:00
scheduler_pending_pods { queue = "unschedulable" } 0
` ,
2019-03-20 02:53:33 -04:00
} ,
{
2022-03-24 05:38:49 -04:00
name : "make some pods subject to backoff, add pods to unschedulablePods/activeQ, move all to activeQ, and finally flush backoffQ" ,
2019-03-20 02:53:33 -04:00
operations : [ ] operation {
2022-03-24 05:38:49 -04:00
addPodUnschedulablePods ,
2019-03-20 02:53:33 -04:00
addPodActiveQ ,
2019-10-07 13:29:53 -04:00
moveAllToActiveOrBackoffQ ,
2019-03-20 02:53:33 -04:00
flushBackoffQ ,
} ,
2020-04-20 20:36:26 -04:00
operands : [ ] [ ] * framework . QueuedPodInfo {
2019-03-20 02:53:33 -04:00
pInfos [ : 40 ] ,
2022-11-07 17:02:22 -05:00
pInfos [ 40 : 50 ] ,
2019-03-20 02:53:33 -04:00
{ nil } ,
{ nil } ,
} ,
2019-09-29 22:05:50 -04:00
metricsName : "scheduler_pending_pods" ,
wants : `
2022-11-07 17:02:22 -05:00
# HELP scheduler_pending_pods [ STABLE ] Number of pending pods , by the queue type . ' active ' means number of pods in activeQ ; ' backoff ' means number of pods in backoffQ ; ' unschedulable ' means number of pods in unschedulablePods that the scheduler attempted to schedule and failed ; ' gated ' is the number of unschedulable pods that the scheduler never attempted to schedule because they are gated .
2019-09-29 22:05:50 -04:00
# TYPE scheduler_pending_pods gauge
scheduler_pending_pods { queue = "active" } 50
scheduler_pending_pods { queue = "backoff" } 0
2022-11-07 17:02:22 -05:00
scheduler_pending_pods { queue = "gated" } 0
2019-09-29 22:05:50 -04:00
scheduler_pending_pods { queue = "unschedulable" } 0
2022-11-22 00:33:16 -05:00
` ,
} ,
{
name : "add pods to activeQ/unschedulablePods and then delete some Pods" ,
operations : [ ] operation {
addPodActiveQ ,
addPodUnschedulablePods ,
deletePod ,
deletePod ,
deletePod ,
} ,
operands : [ ] [ ] * framework . QueuedPodInfo {
pInfos [ : 30 ] ,
pInfos [ 30 : ] ,
pInfos [ : 2 ] ,
pInfos [ 30 : 33 ] ,
pInfos [ 50 : 54 ] ,
} ,
metricsName : "scheduler_pending_pods" ,
wants : `
# HELP scheduler_pending_pods [ STABLE ] Number of pending pods , by the queue type . ' active ' means number of pods in activeQ ; ' backoff ' means number of pods in backoffQ ; ' unschedulable ' means number of pods in unschedulablePods that the scheduler attempted to schedule and failed ; ' gated ' is the number of unschedulable pods that the scheduler never attempted to schedule because they are gated .
# TYPE scheduler_pending_pods gauge
scheduler_pending_pods { queue = "active" } 28
scheduler_pending_pods { queue = "backoff" } 0
scheduler_pending_pods { queue = "gated" } 6
scheduler_pending_pods { queue = "unschedulable" } 17
` ,
} ,
{
name : "add pods to activeQ/unschedulablePods and then update some Pods as queueable" ,
operations : [ ] operation {
addPodActiveQ ,
addPodUnschedulablePods ,
updatePodQueueable ,
} ,
operands : [ ] [ ] * framework . QueuedPodInfo {
pInfos [ : 30 ] ,
pInfos [ 30 : ] ,
pInfos [ 50 : 55 ] ,
} ,
metricsName : "scheduler_pending_pods" ,
wants : `
# HELP scheduler_pending_pods [ STABLE ] Number of pending pods , by the queue type . ' active ' means number of pods in activeQ ; ' backoff ' means number of pods in backoffQ ; ' unschedulable ' means number of pods in unschedulablePods that the scheduler attempted to schedule and failed ; ' gated ' is the number of unschedulable pods that the scheduler never attempted to schedule because they are gated .
# TYPE scheduler_pending_pods gauge
scheduler_pending_pods { queue = "active" } 35
scheduler_pending_pods { queue = "backoff" } 0
scheduler_pending_pods { queue = "gated" } 5
scheduler_pending_pods { queue = "unschedulable" } 20
2019-09-29 22:05:50 -04:00
` ,
2019-03-20 02:53:33 -04:00
} ,
2023-03-01 21:57:50 -05:00
{
name : "the metrics should not be recorded (pluginMetricsSamplePercent=0)" ,
operations : [ ] operation {
add ,
} ,
operands : [ ] [ ] * framework . QueuedPodInfo {
pInfos [ : 1 ] ,
} ,
metricsName : "scheduler_plugin_execution_duration_seconds" ,
pluginMetricsSamplePercent : 0 ,
wants : `
# HELP scheduler_plugin_execution_duration_seconds [ ALPHA ] Duration for running a plugin at a specific extension point .
# TYPE scheduler_plugin_execution_duration_seconds histogram
` , // the observed value will always be 0, because we don't proceed the fake clock.
} ,
{
name : "the metrics should be recorded (pluginMetricsSamplePercent=100)" ,
operations : [ ] operation {
add ,
} ,
operands : [ ] [ ] * framework . QueuedPodInfo {
pInfos [ : 1 ] ,
} ,
metricsName : "scheduler_plugin_execution_duration_seconds" ,
pluginMetricsSamplePercent : 100 ,
wants : `
# HELP scheduler_plugin_execution_duration_seconds [ ALPHA ] Duration for running a plugin at a specific extension point .
# TYPE scheduler_plugin_execution_duration_seconds histogram
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "1e-05" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "1.5000000000000002e-05" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "2.2500000000000005e-05" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "3.375000000000001e-05" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "5.062500000000001e-05" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "7.593750000000002e-05" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.00011390625000000003" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.00017085937500000006" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.0002562890625000001" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.00038443359375000017" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.0005766503906250003" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.0008649755859375004" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.0012974633789062506" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.0019461950683593758" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.0029192926025390638" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.004378938903808595" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.006568408355712893" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.009852612533569338" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.014778918800354007" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "0.02216837820053101" } 1
scheduler_plugin_execution_duration_seconds_bucket { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" , le = "+Inf" } 1
scheduler_plugin_execution_duration_seconds_sum { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" } 0
scheduler_plugin_execution_duration_seconds_count { extension_point = "PreEnqueue" , plugin = "preEnqueuePlugin" , status = "Success" } 1
` , // the observed value will always be 0, because we don't proceed the fake clock.
} ,
2019-03-20 02:53:33 -04:00
}
resetMetrics := func ( ) {
2019-08-22 21:00:06 -04:00
metrics . ActivePods ( ) . Set ( 0 )
metrics . BackoffPods ( ) . Set ( 0 )
metrics . UnschedulablePods ( ) . Set ( 0 )
2022-11-07 17:02:22 -05:00
metrics . GatedPods ( ) . Set ( 0 )
2023-03-01 21:57:50 -05:00
metrics . PluginExecutionDuration . Reset ( )
2019-03-20 02:53:33 -04:00
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
resetMetrics ( )
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
2022-11-07 17:02:22 -05:00
m := map [ string ] [ ] framework . PreEnqueuePlugin { "" : { & preEnqueuePlugin { allowlists : [ ] string { queueable } } } }
2023-03-01 21:57:50 -05:00
recorder := metrics . NewMetricsAsyncRecorder ( 3 , 20 * time . Microsecond , ctx . Done ( ) )
queue := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( testingclock . NewFakeClock ( timestamp ) ) , WithPreEnqueuePluginMap ( m ) , WithPluginMetricsSamplePercent ( test . pluginMetricsSamplePercent ) , WithMetricsRecorder ( * recorder ) )
2019-03-20 02:53:33 -04:00
for i , op := range test . operations {
for _ , pInfo := range test . operands [ i ] {
2023-09-05 14:46:33 -04:00
op ( t , logger , queue , pInfo )
2019-03-20 02:53:33 -04:00
}
}
2023-03-01 21:57:50 -05:00
recorder . FlushMetrics ( )
2019-09-29 22:05:50 -04:00
if err := testutil . GatherAndCompare ( metrics . GetGather ( ) , strings . NewReader ( test . wants ) , test . metricsName ) ; err != nil {
t . Fatal ( err )
2019-03-20 02:53:33 -04:00
}
} )
}
}
2019-10-07 19:06:00 -04:00
// TestPerPodSchedulingMetrics makes sure pod schedule attempts is updated correctly while
// initialAttemptTimestamp stays the same during multiple add/pop operations.
func TestPerPodSchedulingMetrics ( t * testing . T ) {
timestamp := time . Now ( )
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
2019-10-07 19:06:00 -04:00
2023-06-23 20:18:32 -04:00
tests := [ ] struct {
name string
perPodSchedulingMetricsScenario func ( * testingclock . FakeClock , * PriorityQueue , * v1 . Pod )
wantAttempts int
wantInitialAttemptTs time . Time
} {
{
// The queue operations are Add -> Pop.
name : "pod is created and scheduled after 1 attempt" ,
perPodSchedulingMetricsScenario : func ( c * testingclock . FakeClock , queue * PriorityQueue , pod * v1 . Pod ) {
queue . Add ( logger , pod )
} ,
wantAttempts : 1 ,
wantInitialAttemptTs : timestamp ,
} ,
{
// The queue operations are Add -> Pop -> AddUnschedulableIfNotPresent -> flushUnschedulablePodsLeftover -> Pop.
name : "pod is created and scheduled after 2 attempts" ,
perPodSchedulingMetricsScenario : func ( c * testingclock . FakeClock , queue * PriorityQueue , pod * v1 . Pod ) {
queue . Add ( logger , pod )
2023-09-28 22:59:22 -04:00
pInfo , err := queue . Pop ( logger )
2023-06-23 20:18:32 -04:00
if err != nil {
t . Fatalf ( "Failed to pop a pod %v" , err )
}
2019-10-07 19:06:00 -04:00
2023-09-11 12:30:11 -04:00
pInfo . UnschedulablePlugins = sets . New ( "plugin" )
2023-06-23 20:18:32 -04:00
queue . AddUnschedulableIfNotPresent ( logger , pInfo , 1 )
// Override clock to exceed the DefaultPodMaxInUnschedulablePodsDuration so that unschedulable pods
// will be moved to activeQ
c . SetTime ( timestamp . Add ( DefaultPodMaxInUnschedulablePodsDuration + 1 ) )
queue . flushUnschedulablePodsLeftover ( logger )
} ,
wantAttempts : 2 ,
wantInitialAttemptTs : timestamp ,
} ,
{
// The queue operations are Add -> Pop -> AddUnschedulableIfNotPresent -> flushUnschedulablePodsLeftover -> Update -> Pop.
name : "pod is created and scheduled after 2 attempts but before the second pop, call update" ,
perPodSchedulingMetricsScenario : func ( c * testingclock . FakeClock , queue * PriorityQueue , pod * v1 . Pod ) {
queue . Add ( logger , pod )
2023-09-28 22:59:22 -04:00
pInfo , err := queue . Pop ( logger )
2023-06-23 20:18:32 -04:00
if err != nil {
t . Fatalf ( "Failed to pop a pod %v" , err )
}
2023-05-15 15:20:44 -04:00
2023-09-11 12:30:11 -04:00
pInfo . UnschedulablePlugins = sets . New ( "plugin" )
2023-06-23 20:18:32 -04:00
queue . AddUnschedulableIfNotPresent ( logger , pInfo , 1 )
// Override clock to exceed the DefaultPodMaxInUnschedulablePodsDuration so that unschedulable pods
// will be moved to activeQ
updatedTimestamp := timestamp
c . SetTime ( updatedTimestamp . Add ( DefaultPodMaxInUnschedulablePodsDuration + 1 ) )
queue . flushUnschedulablePodsLeftover ( logger )
newPod := pod . DeepCopy ( )
newPod . Generation = 1
queue . Update ( logger , pod , newPod )
} ,
wantAttempts : 2 ,
wantInitialAttemptTs : timestamp ,
} ,
{
// The queue operations are Add gated pod -> check unschedulablePods -> lift gate & update pod -> Pop.
name : "A gated pod is created and scheduled after lifting gate" ,
perPodSchedulingMetricsScenario : func ( c * testingclock . FakeClock , queue * PriorityQueue , pod * v1 . Pod ) {
// Create a queue with PreEnqueuePlugin
queue . preEnqueuePluginMap = map [ string ] [ ] framework . PreEnqueuePlugin { "" : { & preEnqueuePlugin { allowlists : [ ] string { "foo" } } } }
queue . pluginMetricsSamplePercent = 0
queue . Add ( logger , pod )
// Check pod is added to the unschedulablePods queue.
if getUnschedulablePod ( queue , pod ) != pod {
t . Errorf ( "Pod %v was not found in the unschedulablePods." , pod . Name )
}
// Override clock to get different InitialAttemptTimestamp
c . Step ( 1 * time . Minute )
2023-05-15 15:20:44 -04:00
2023-06-23 20:18:32 -04:00
// Update pod with the required label to get it out of unschedulablePods queue.
updateGatedPod := pod . DeepCopy ( )
updateGatedPod . Labels = map [ string ] string { "foo" : "" }
queue . Update ( logger , pod , updateGatedPod )
} ,
wantAttempts : 1 ,
wantInitialAttemptTs : timestamp . Add ( 1 * time . Minute ) ,
} ,
2023-05-15 15:20:44 -04:00
}
2023-06-23 20:18:32 -04:00
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
2023-05-15 15:20:44 -04:00
2023-06-23 20:18:32 -04:00
c := testingclock . NewFakeClock ( timestamp )
pod := st . MakePod ( ) . Name ( "test-pod" ) . Namespace ( "test-ns" ) . UID ( "test-uid" ) . Obj ( )
queue := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( c ) )
2023-05-15 15:20:44 -04:00
2023-06-23 20:18:32 -04:00
test . perPodSchedulingMetricsScenario ( c , queue , pod )
2023-09-28 22:59:22 -04:00
podInfo , err := queue . Pop ( logger )
2023-06-23 20:18:32 -04:00
if err != nil {
t . Fatal ( err )
}
if podInfo . Attempts != test . wantAttempts {
t . Errorf ( "Pod schedule attempt unexpected, got %v, want %v" , podInfo . Attempts , test . wantAttempts )
}
if * podInfo . InitialAttemptTimestamp != test . wantInitialAttemptTs {
t . Errorf ( "Pod initial schedule attempt timestamp unexpected, got %v, want %v" , * podInfo . InitialAttemptTimestamp , test . wantInitialAttemptTs )
}
} )
2023-05-15 15:20:44 -04:00
}
2019-10-07 19:06:00 -04:00
}
2019-10-07 13:29:53 -04:00
func TestIncomingPodsMetrics ( t * testing . T ) {
timestamp := time . Now ( )
2023-07-17 18:53:07 -04:00
unschedulablePlg := "unschedulable_plugin"
2019-10-07 13:29:53 -04:00
metrics . Register ( )
2020-04-20 20:36:26 -04:00
var pInfos = make ( [ ] * framework . QueuedPodInfo , 0 , 3 )
2019-10-07 13:29:53 -04:00
for i := 1 ; i <= 3 ; i ++ {
2020-04-20 20:36:26 -04:00
p := & framework . QueuedPodInfo {
2022-10-12 10:11:04 -04:00
PodInfo : mustNewTestPodInfo ( t ,
2022-04-18 21:06:58 -04:00
st . MakePod ( ) . Name ( fmt . Sprintf ( "test-pod-%d" , i ) ) . Namespace ( fmt . Sprintf ( "ns%d" , i ) ) . UID ( fmt . Sprintf ( "tp-%d" , i ) ) . Obj ( ) ) ,
2023-07-17 18:53:07 -04:00
Timestamp : timestamp ,
UnschedulablePlugins : sets . New ( unschedulablePlg ) ,
2019-10-07 13:29:53 -04:00
}
pInfos = append ( pInfos , p )
}
tests := [ ] struct {
name string
operations [ ] operation
want string
} {
{
name : "add pods to activeQ" ,
operations : [ ] operation {
add ,
} ,
want : `
scheduler_queue_incoming_pods_total { event = "PodAdd" , queue = "active" } 3
` ,
} ,
{
2022-03-24 05:38:49 -04:00
name : "add pods to unschedulablePods" ,
2019-10-07 13:29:53 -04:00
operations : [ ] operation {
2023-09-05 14:46:33 -04:00
popAndRequeueAsUnschedulable ,
2019-10-07 13:29:53 -04:00
} ,
want : `
scheduler_queue_incoming_pods_total { event = "ScheduleAttemptFailure" , queue = "unschedulable" } 3
` ,
} ,
{
2022-03-24 05:38:49 -04:00
name : "add pods to unschedulablePods and then move all to backoffQ" ,
2019-10-07 13:29:53 -04:00
operations : [ ] operation {
2023-09-05 14:46:33 -04:00
popAndRequeueAsUnschedulable ,
2019-10-07 13:29:53 -04:00
moveAllToActiveOrBackoffQ ,
} ,
want : ` scheduler_queue_incoming_pods_total { event = "ScheduleAttemptFailure" , queue = "unschedulable" } 3
2021-01-29 01:29:10 -05:00
scheduler_queue_incoming_pods_total { event = "UnschedulableTimeout" , queue = "backoff" } 3
2019-10-07 13:29:53 -04:00
` ,
} ,
{
2022-03-24 05:38:49 -04:00
name : "add pods to unschedulablePods and then move all to activeQ" ,
2019-10-07 13:29:53 -04:00
operations : [ ] operation {
2023-09-05 14:46:33 -04:00
popAndRequeueAsUnschedulable ,
2019-10-07 13:29:53 -04:00
moveClockForward ,
moveAllToActiveOrBackoffQ ,
} ,
want : ` scheduler_queue_incoming_pods_total { event = "ScheduleAttemptFailure" , queue = "unschedulable" } 3
2021-01-29 01:29:10 -05:00
scheduler_queue_incoming_pods_total { event = "UnschedulableTimeout" , queue = "active" } 3
2019-10-07 13:29:53 -04:00
` ,
} ,
{
name : "make some pods subject to backoff and add them to backoffQ, then flush backoffQ" ,
operations : [ ] operation {
2023-09-05 14:46:33 -04:00
popAndRequeueAsBackoff ,
2019-10-07 13:29:53 -04:00
moveClockForward ,
flushBackoffQ ,
} ,
want : ` scheduler_queue_incoming_pods_total { event = "BackoffComplete" , queue = "active" } 3
scheduler_queue_incoming_pods_total { event = "ScheduleAttemptFailure" , queue = "backoff" } 3
` ,
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
metrics . SchedulerQueueIncomingPods . Reset ( )
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
queue := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( testingclock . NewFakeClock ( timestamp ) ) )
2019-10-07 13:29:53 -04:00
for _ , op := range test . operations {
for _ , pInfo := range pInfos {
2023-09-05 14:46:33 -04:00
op ( t , logger , queue , pInfo )
2019-10-07 13:29:53 -04:00
}
}
metricName := metrics . SchedulerSubsystem + "_" + metrics . SchedulerQueueIncomingPods . Name
if err := testutil . CollectAndCompare ( metrics . SchedulerQueueIncomingPods , strings . NewReader ( queueMetricMetadata + test . want ) , metricName ) ; err != nil {
t . Errorf ( "unexpected collecting result:\n%s" , err )
}
} )
}
}
2020-01-28 13:29:44 -05:00
func TestBackOffFlow ( t * testing . T ) {
2021-09-17 05:48:22 -04:00
cl := testingclock . NewFakeClock ( time . Now ( ) )
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithClock ( cl ) )
2020-01-28 13:29:44 -05:00
steps := [ ] struct {
wantBackoff time . Duration
} {
{ wantBackoff : time . Second } ,
{ wantBackoff : 2 * time . Second } ,
{ wantBackoff : 4 * time . Second } ,
{ wantBackoff : 8 * time . Second } ,
{ wantBackoff : 10 * time . Second } ,
{ wantBackoff : 10 * time . Second } ,
{ wantBackoff : 10 * time . Second } ,
}
2022-04-18 21:06:58 -04:00
pod := st . MakePod ( ) . Name ( "test-pod" ) . Namespace ( "test-ns" ) . UID ( "test-uid" ) . Obj ( )
2021-01-25 02:07:39 -05:00
2021-04-09 03:31:32 -04:00
podID := types . NamespacedName {
2021-01-25 02:07:39 -05:00
Namespace : pod . Namespace ,
Name : pod . Name ,
}
2023-03-22 07:48:04 -04:00
if err := q . Add ( logger , pod ) ; err != nil {
2020-01-28 13:29:44 -05:00
t . Fatal ( err )
}
for i , step := range steps {
t . Run ( fmt . Sprintf ( "step %d" , i ) , func ( t * testing . T ) {
timestamp := cl . Now ( )
// Simulate schedule attempt.
2023-09-28 22:59:22 -04:00
podInfo , err := q . Pop ( logger )
2020-01-28 13:29:44 -05:00
if err != nil {
t . Fatal ( err )
}
if podInfo . Attempts != i + 1 {
t . Errorf ( "got attempts %d, want %d" , podInfo . Attempts , i + 1 )
}
2023-09-11 12:30:11 -04:00
err = q . AddUnschedulableIfNotPresent ( logger , podInfo , int64 ( i ) )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
2020-01-28 13:29:44 -05:00
}
// An event happens.
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , UnschedulableTimeout , nil , nil , nil )
2020-01-28 13:29:44 -05:00
if _ , ok , _ := q . podBackoffQ . Get ( podInfo ) ; ! ok {
t . Errorf ( "pod %v is not in the backoff queue" , podID )
}
// Check backoff duration.
2020-02-13 19:31:07 -05:00
deadline := q . getBackoffTime ( podInfo )
2020-01-28 13:29:44 -05:00
backoff := deadline . Sub ( timestamp )
if backoff != step . wantBackoff {
t . Errorf ( "got backoff %s, want %s" , backoff , step . wantBackoff )
}
// Simulate routine that continuously flushes the backoff queue.
cl . Step ( time . Millisecond )
2023-03-22 07:48:04 -04:00
q . flushBackoffQCompleted ( logger )
2020-01-28 13:29:44 -05:00
// Still in backoff queue after an early flush.
if _ , ok , _ := q . podBackoffQ . Get ( podInfo ) ; ! ok {
t . Errorf ( "pod %v is not in the backoff queue" , podID )
}
// Moved out of the backoff queue after timeout.
cl . Step ( backoff )
2023-03-22 07:48:04 -04:00
q . flushBackoffQCompleted ( logger )
2020-01-28 13:29:44 -05:00
if _ , ok , _ := q . podBackoffQ . Get ( podInfo ) ; ok {
t . Errorf ( "pod %v is still in the backoff queue" , podID )
}
} )
}
2020-02-13 19:31:07 -05:00
}
2021-03-11 15:31:33 -05:00
func TestMoveAllToActiveOrBackoffQueue_PreEnqueueChecks ( t * testing . T ) {
var podInfos [ ] * framework . QueuedPodInfo
for i := 0 ; i < 5 ; i ++ {
2022-04-18 21:06:58 -04:00
pInfo := newQueuedPodInfoForLookup (
st . MakePod ( ) . Name ( fmt . Sprintf ( "p%d" , i ) ) . Priority ( int32 ( i ) ) . Obj ( ) ,
)
2021-03-11 15:31:33 -05:00
podInfos = append ( podInfos , pInfo )
}
tests := [ ] struct {
name string
preEnqueueCheck PreEnqueueCheck
podInfos [ ] * framework . QueuedPodInfo
want [ ] string
} {
{
name : "nil PreEnqueueCheck" ,
podInfos : podInfos ,
want : [ ] string { "p0" , "p1" , "p2" , "p3" , "p4" } ,
} ,
{
name : "move Pods with priority greater than 2" ,
podInfos : podInfos ,
preEnqueueCheck : func ( pod * v1 . Pod ) bool { return * pod . Spec . Priority >= 2 } ,
want : [ ] string { "p2" , "p3" , "p4" } ,
} ,
{
name : "move Pods with even priority and greater than 2" ,
podInfos : podInfos ,
preEnqueueCheck : func ( pod * v1 . Pod ) bool {
return * pod . Spec . Priority % 2 == 0 && * pod . Spec . Priority >= 2
} ,
want : [ ] string { "p2" , "p4" } ,
} ,
{
name : "move Pods with even and negative priority" ,
podInfos : podInfos ,
preEnqueueCheck : func ( pod * v1 . Pod ) bool {
return * pod . Spec . Priority % 2 == 0 && * pod . Spec . Priority < 0
} ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2023-03-22 07:48:04 -04:00
logger , ctx := ktesting . NewTestContext ( t )
ctx , cancel := context . WithCancel ( ctx )
2022-01-22 14:00:56 -05:00
defer cancel ( )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) )
2022-06-06 03:51:42 -04:00
for i , podInfo := range tt . podInfos {
2023-07-17 18:53:07 -04:00
// To simulate the pod is failed in scheduling in the real world, Pop() the pod from activeQ before AddUnschedulableIfNotPresent() below.
q . activeQ . Add ( q . newQueuedPodInfo ( podInfo . Pod ) )
2023-09-28 22:59:22 -04:00
if p , err := q . Pop ( logger ) ; err != nil || p . Pod != podInfo . Pod {
2023-07-17 18:53:07 -04:00
t . Errorf ( "Expected: %v after Pop, but got: %v" , podInfo . Pod . Name , p . Pod . Name )
}
2023-09-11 12:30:11 -04:00
podInfo . UnschedulablePlugins = sets . New ( "plugin" )
err := q . AddUnschedulableIfNotPresent ( logger , podInfo , q . schedulingCycle )
if err != nil {
t . Fatalf ( "unexpected error from AddUnschedulableIfNotPresent: %v" , err )
}
2022-06-06 03:51:42 -04:00
// NOTE: On Windows, time.Now() is not as precise, 2 consecutive calls may return the same timestamp,
// resulting in 0 time delta / latency. This will cause the pods to be backed off in a random
// order, which would cause this test to fail, since the expectation is for them to be backed off
// in a certain order.
// See: https://github.com/golang/go/issues/8687
podInfo . Timestamp = podInfo . Timestamp . Add ( time . Duration ( ( i - len ( tt . podInfos ) ) ) * time . Millisecond )
2021-03-11 15:31:33 -05:00
}
2023-06-08 00:54:30 -04:00
q . MoveAllToActiveOrBackoffQueue ( logger , TestEvent , nil , nil , tt . preEnqueueCheck )
2021-03-11 15:31:33 -05:00
var got [ ] string
for q . podBackoffQ . Len ( ) != 0 {
obj , err := q . podBackoffQ . Pop ( )
if err != nil {
t . Fatalf ( "Fail to pop pod from backoffQ: %v" , err )
}
queuedPodInfo , ok := obj . ( * framework . QueuedPodInfo )
if ! ok {
2022-03-24 07:56:10 -04:00
t . Fatalf ( "Fail to convert popped obj (type %T) to *framework.QueuedPodInfo" , obj )
2021-03-11 15:31:33 -05:00
}
got = append ( got , queuedPodInfo . Pod . Name )
}
if diff := cmp . Diff ( tt . want , got ) ; diff != "" {
t . Errorf ( "Unexpected diff (-want, +got):\n%s" , diff )
}
} )
}
}
2022-11-22 00:33:16 -05:00
func makeQueuedPodInfos ( num int , namePrefix , label string , timestamp time . Time ) [ ] * framework . QueuedPodInfo {
2020-04-20 20:36:26 -04:00
var pInfos = make ( [ ] * framework . QueuedPodInfo , 0 , num )
2020-02-13 19:31:07 -05:00
for i := 1 ; i <= num ; i ++ {
2020-04-20 20:36:26 -04:00
p := & framework . QueuedPodInfo {
2022-11-22 00:33:16 -05:00
PodInfo : mustNewPodInfo (
st . MakePod ( ) . Name ( fmt . Sprintf ( "%v-%d" , namePrefix , i ) ) . Namespace ( fmt . Sprintf ( "ns%d" , i ) ) . Label ( label , "" ) . UID ( fmt . Sprintf ( "tp-%d" , i ) ) . Obj ( ) ) ,
2022-11-07 17:02:22 -05:00
Timestamp : timestamp ,
2023-03-27 03:46:13 -04:00
UnschedulablePlugins : sets . New [ string ] ( ) ,
2020-02-13 19:31:07 -05:00
}
pInfos = append ( pInfos , p )
2020-01-28 13:29:44 -05:00
}
2020-02-13 19:31:07 -05:00
return pInfos
2020-01-28 13:29:44 -05:00
}
2021-09-18 22:09:06 -04:00
func TestPriorityQueue_calculateBackoffDuration ( t * testing . T ) {
tests := [ ] struct {
name string
initialBackoffDuration time . Duration
maxBackoffDuration time . Duration
podInfo * framework . QueuedPodInfo
want time . Duration
} {
{
name : "normal" ,
initialBackoffDuration : 1 * time . Nanosecond ,
maxBackoffDuration : 32 * time . Nanosecond ,
podInfo : & framework . QueuedPodInfo { Attempts : 16 } ,
want : 32 * time . Nanosecond ,
} ,
{
name : "overflow_32bit" ,
initialBackoffDuration : 1 * time . Nanosecond ,
maxBackoffDuration : math . MaxInt32 * time . Nanosecond ,
podInfo : & framework . QueuedPodInfo { Attempts : 32 } ,
want : math . MaxInt32 * time . Nanosecond ,
} ,
{
name : "overflow_64bit" ,
initialBackoffDuration : 1 * time . Nanosecond ,
maxBackoffDuration : math . MaxInt64 * time . Nanosecond ,
podInfo : & framework . QueuedPodInfo { Attempts : 64 } ,
want : math . MaxInt64 * time . Nanosecond ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2022-01-22 14:00:56 -05:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithPodInitialBackoffDuration ( tt . initialBackoffDuration ) , WithPodMaxBackoffDuration ( tt . maxBackoffDuration ) )
2021-09-18 22:09:06 -04:00
if got := q . calculateBackoffDuration ( tt . podInfo ) ; got != tt . want {
t . Errorf ( "PriorityQueue.calculateBackoffDuration() = %v, want %v" , got , tt . want )
}
} )
}
}
2022-10-12 10:11:04 -04:00
func mustNewTestPodInfo ( t * testing . T , pod * v1 . Pod ) * framework . PodInfo {
podInfo , err := framework . NewPodInfo ( pod )
if err != nil {
t . Fatal ( err )
}
return podInfo
}
func mustNewPodInfo ( pod * v1 . Pod ) * framework . PodInfo {
podInfo , err := framework . NewPodInfo ( pod )
if err != nil {
panic ( err )
}
return podInfo
}
2023-06-08 00:54:30 -04:00
// Test_isPodWorthRequeuing tests isPodWorthRequeuing function.
func Test_isPodWorthRequeuing ( t * testing . T ) {
count := 0
2023-10-19 07:02:11 -04:00
queueHintReturnQueue := func ( logger klog . Logger , pod * v1 . Pod , oldObj , newObj interface { } ) ( framework . QueueingHint , error ) {
2023-06-08 00:54:30 -04:00
count ++
2023-10-19 07:02:11 -04:00
return framework . Queue , nil
2023-06-08 00:54:30 -04:00
}
2023-10-19 07:02:11 -04:00
queueHintReturnSkip := func ( logger klog . Logger , pod * v1 . Pod , oldObj , newObj interface { } ) ( framework . QueueingHint , error ) {
2023-06-08 00:54:30 -04:00
count ++
2023-07-13 09:45:26 -04:00
return framework . QueueSkip , nil
2023-06-08 00:54:30 -04:00
}
2023-07-13 09:45:26 -04:00
queueHintReturnErr := func ( logger klog . Logger , pod * v1 . Pod , oldObj , newObj interface { } ) ( framework . QueueingHint , error ) {
count ++
return framework . QueueSkip , fmt . Errorf ( "unexpected error" )
2023-06-08 00:54:30 -04:00
}
tests := [ ] struct {
name string
podInfo * framework . QueuedPodInfo
event framework . ClusterEvent
oldObj interface { }
newObj interface { }
2023-10-19 07:02:11 -04:00
expected queueingStrategy
2023-06-08 00:54:30 -04:00
expectedExecutionCount int // expected total execution count of queueing hint function
queueingHintMap QueueingHintMapPerProfile
} {
{
2023-10-19 07:02:11 -04:00
name : "return Queue when no queueing hint function is registered for the event" ,
2023-06-08 00:54:30 -04:00
podInfo : & framework . QueuedPodInfo {
UnschedulablePlugins : sets . New ( "fooPlugin1" ) ,
PodInfo : mustNewPodInfo ( st . MakePod ( ) . Name ( "pod1" ) . Namespace ( "ns1" ) . UID ( "1" ) . Obj ( ) ) ,
} ,
event : NodeAdd ,
oldObj : nil ,
2023-07-13 09:45:26 -04:00
newObj : st . MakeNode ( ) . Obj ( ) ,
2023-10-19 07:02:11 -04:00
expected : queueSkip ,
2023-06-08 00:54:30 -04:00
expectedExecutionCount : 0 ,
queueingHintMap : QueueingHintMapPerProfile {
"" : {
// no queueing hint function for NodeAdd.
AssignedPodAdd : {
{
// It will be ignored because the event is not NodeAdd.
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} ,
} ,
} ,
} ,
} ,
2023-07-13 09:45:26 -04:00
{
2023-10-19 07:02:11 -04:00
name : "Treat the event as Queue when QueueHintFn returns error" ,
2023-07-13 09:45:26 -04:00
podInfo : & framework . QueuedPodInfo {
UnschedulablePlugins : sets . New ( "fooPlugin1" ) ,
PodInfo : mustNewPodInfo ( st . MakePod ( ) . Name ( "pod1" ) . Namespace ( "ns1" ) . UID ( "1" ) . Obj ( ) ) ,
} ,
event : NodeAdd ,
oldObj : nil ,
newObj : st . MakeNode ( ) . Obj ( ) ,
2023-10-19 07:02:11 -04:00
expected : queueAfterBackoff ,
2023-07-13 09:45:26 -04:00
expectedExecutionCount : 1 ,
queueingHintMap : QueueingHintMapPerProfile {
"" : {
NodeAdd : {
{
PluginName : "fooPlugin1" ,
QueueingHintFn : queueHintReturnErr ,
} ,
} ,
} ,
} ,
} ,
2023-06-08 00:54:30 -04:00
{
2023-10-19 07:02:11 -04:00
name : "return Queue when the event is wildcard" ,
2023-06-08 00:54:30 -04:00
podInfo : & framework . QueuedPodInfo {
UnschedulablePlugins : sets . New ( "fooPlugin1" ) ,
PodInfo : mustNewPodInfo ( st . MakePod ( ) . Name ( "pod1" ) . Namespace ( "ns1" ) . UID ( "1" ) . Obj ( ) ) ,
} ,
event : WildCardEvent ,
oldObj : nil ,
2023-07-13 09:45:26 -04:00
newObj : st . MakeNode ( ) . Obj ( ) ,
2023-10-19 07:02:11 -04:00
expected : queueAfterBackoff ,
2023-06-08 00:54:30 -04:00
expectedExecutionCount : 0 ,
queueingHintMap : QueueingHintMapPerProfile { } ,
} ,
{
2023-10-19 07:02:11 -04:00
name : "interprets Queue from the Pending plugin as queueImmediately" ,
2023-06-08 00:54:30 -04:00
podInfo : & framework . QueuedPodInfo {
2023-10-19 07:02:11 -04:00
UnschedulablePlugins : sets . New ( "fooPlugin1" , "fooPlugin3" ) ,
PendingPlugins : sets . New ( "fooPlugin2" ) ,
2023-06-08 00:54:30 -04:00
PodInfo : mustNewPodInfo ( st . MakePod ( ) . Name ( "pod1" ) . Namespace ( "ns1" ) . UID ( "1" ) . Obj ( ) ) ,
} ,
event : NodeAdd ,
oldObj : nil ,
2023-10-19 07:02:11 -04:00
newObj : st . MakeNode ( ) . Node ,
expected : queueImmediately ,
expectedExecutionCount : 2 ,
2023-06-08 00:54:30 -04:00
queueingHintMap : QueueingHintMapPerProfile {
"" : {
NodeAdd : {
{
2023-10-19 07:02:11 -04:00
PluginName : "fooPlugin1" ,
// It returns Queue and it's interpreted as queueAfterBackoff.
// But, the function continues to run other hints because the Pod has PendingPlugins, which can result in queueImmediately.
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} ,
2023-07-13 09:45:26 -04:00
{
2023-10-19 07:02:11 -04:00
PluginName : "fooPlugin2" ,
// It's interpreted as queueImmediately.
// The function doesn't run other hints because queueImmediately is the highest priority.
QueueingHintFn : queueHintReturnQueue ,
2023-07-13 09:45:26 -04:00
} ,
2023-06-08 00:54:30 -04:00
{
2023-07-13 09:45:26 -04:00
PluginName : "fooPlugin3" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} ,
{
2023-07-13 09:45:26 -04:00
PluginName : "fooPlugin4" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnErr ,
2023-06-08 00:54:30 -04:00
} ,
} ,
} ,
} ,
} ,
{
2023-10-19 07:02:11 -04:00
name : "interprets Queue from the Unschedulable plugin as queueAfterBackoff" ,
2023-06-08 00:54:30 -04:00
podInfo : & framework . QueuedPodInfo {
2023-10-19 07:02:11 -04:00
UnschedulablePlugins : sets . New ( "fooPlugin1" , "fooPlugin2" ) ,
2023-06-08 00:54:30 -04:00
PodInfo : mustNewPodInfo ( st . MakePod ( ) . Name ( "pod1" ) . Namespace ( "ns1" ) . UID ( "1" ) . Obj ( ) ) ,
} ,
event : NodeAdd ,
oldObj : nil ,
2023-07-13 09:45:26 -04:00
newObj : st . MakeNode ( ) . Obj ( ) ,
2023-10-19 07:02:11 -04:00
expected : queueAfterBackoff ,
expectedExecutionCount : 2 ,
2023-06-08 00:54:30 -04:00
queueingHintMap : QueueingHintMapPerProfile {
"" : {
NodeAdd : {
{
2023-10-19 07:02:11 -04:00
// Skip will be ignored
2023-06-08 00:54:30 -04:00
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnSkip ,
2023-06-08 00:54:30 -04:00
} ,
{
2023-10-19 07:02:11 -04:00
// Skip will be ignored
2023-06-08 00:54:30 -04:00
PluginName : "fooPlugin2" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-07-13 09:45:26 -04:00
} ,
2023-06-08 00:54:30 -04:00
} ,
} ,
} ,
} ,
{
2023-10-19 07:02:11 -04:00
name : "Queueing hint function that isn't from the plugin in UnschedulablePlugins/PendingPlugins is ignored" ,
2023-06-08 00:54:30 -04:00
podInfo : & framework . QueuedPodInfo {
UnschedulablePlugins : sets . New ( "fooPlugin1" , "fooPlugin2" ) ,
PodInfo : mustNewPodInfo ( st . MakePod ( ) . Name ( "pod1" ) . Namespace ( "ns1" ) . UID ( "1" ) . Obj ( ) ) ,
} ,
event : NodeAdd ,
oldObj : nil ,
2023-10-19 07:02:11 -04:00
newObj : st . MakeNode ( ) . Node ,
expected : queueSkip ,
2023-06-08 00:54:30 -04:00
expectedExecutionCount : 2 ,
queueingHintMap : QueueingHintMapPerProfile {
"" : {
NodeAdd : {
{
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnSkip ,
2023-06-08 00:54:30 -04:00
} ,
{
PluginName : "fooPlugin2" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnSkip ,
2023-06-08 00:54:30 -04:00
} ,
{
PluginName : "fooPlugin3" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue , // It'll be ignored.
2023-06-08 00:54:30 -04:00
} ,
} ,
} ,
} ,
} ,
{
2023-10-19 07:02:11 -04:00
name : "If event is specific Node update event, queueing hint function for NodeUpdate/UpdateNodeLabel is also executed" ,
2023-06-08 00:54:30 -04:00
podInfo : & framework . QueuedPodInfo {
UnschedulablePlugins : sets . New ( "fooPlugin1" , "fooPlugin2" ) ,
PodInfo : mustNewPodInfo ( st . MakePod ( ) . Name ( "pod1" ) . Namespace ( "ns1" ) . UID ( "1" ) . Obj ( ) ) ,
} ,
event : framework . ClusterEvent { Resource : framework . Node , ActionType : framework . UpdateNodeLabel } ,
oldObj : nil ,
2023-07-13 09:45:26 -04:00
newObj : st . MakeNode ( ) . Obj ( ) ,
2023-10-19 07:02:11 -04:00
expected : queueAfterBackoff ,
expectedExecutionCount : 1 ,
2023-06-08 00:54:30 -04:00
queueingHintMap : QueueingHintMapPerProfile {
"" : {
framework . ClusterEvent { Resource : framework . Node , ActionType : framework . UpdateNodeLabel } : {
{
2023-10-19 07:02:11 -04:00
PluginName : "fooPlugin1" ,
// It's only executed and interpreted as queueAfterBackoff.
// The function doesn't run other hints because this Pod doesn't have PendingPlugins.
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} ,
{
PluginName : "fooPlugin2" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} ,
} ,
framework . ClusterEvent { Resource : framework . Node , ActionType : framework . Update } : {
{
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} ,
} ,
NodeAdd : { // not executed because NodeAdd is unrelated.
{
PluginName : "fooPlugin1" ,
2023-10-19 07:02:11 -04:00
QueueingHintFn : queueHintReturnQueue ,
2023-06-08 00:54:30 -04:00
} ,
} ,
} ,
} ,
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
count = 0 // reset count every time
logger , ctx := ktesting . NewTestContext ( t )
q := NewTestQueue ( ctx , newDefaultQueueSort ( ) , WithQueueingHintMapPerProfile ( test . queueingHintMap ) )
actual := q . isPodWorthRequeuing ( logger , test . podInfo , test . event , test . oldObj , test . newObj )
if actual != test . expected {
t . Errorf ( "isPodWorthRequeuing() = %v, want %v" , actual , test . expected )
}
if count != test . expectedExecutionCount {
t . Errorf ( "isPodWorthRequeuing() executed queueing hint functions %v times, expected: %v" , count , test . expectedExecutionCount )
}
} )
}
}