volcano.sh/volcano@v1.9.0/pkg/scheduler/plugins/tdm/tdm_test.go (about)

     1  /*
     2  Copyright 2021 The Volcano Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tdm
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/agiledragon/gomonkey/v2"
    27  	v1 "k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/client-go/tools/record"
    30  
    31  	schedulingv2 "volcano.sh/apis/pkg/apis/scheduling/v1beta1"
    32  	"volcano.sh/volcano/pkg/scheduler/api"
    33  	"volcano.sh/volcano/pkg/scheduler/cache"
    34  	"volcano.sh/volcano/pkg/scheduler/conf"
    35  	"volcano.sh/volcano/pkg/scheduler/framework"
    36  	"volcano.sh/volcano/pkg/scheduler/util"
    37  )
    38  
    39  const (
    40  	eps = 1e-8
    41  )
    42  
    43  func Test_parseRevocableZone(t *testing.T) {
    44  	tests := []struct {
    45  		rz    string
    46  		delta int64
    47  		err   bool
    48  	}{
    49  		{
    50  			rz:    "00:00_01:00",
    51  			delta: 0,
    52  			err:   true,
    53  		},
    54  		{
    55  			rz:    "00:00-01:00",
    56  			delta: 60 * 60,
    57  			err:   false,
    58  		},
    59  		{
    60  			rz:    "0:00-23:59",
    61  			delta: 23*60*60 + 59*60,
    62  			err:   false,
    63  		},
    64  		{
    65  			rz:    "0:00",
    66  			delta: 0,
    67  			err:   true,
    68  		},
    69  		{
    70  			rz:    "1:00-0:00",
    71  			delta: 23 * 60 * 60,
    72  			err:   false,
    73  		},
    74  		{
    75  			rz:    "1:0-0:0",
    76  			delta: 0,
    77  			err:   true,
    78  		},
    79  		{
    80  			rz:    "   1:00-0:00    ",
    81  			delta: 23 * 60 * 60,
    82  			err:   false,
    83  		},
    84  		{
    85  			rz:    "23:59-23:59",
    86  			delta: 24 * 60 * 60,
    87  			err:   false,
    88  		},
    89  		{
    90  			rz:    "63:59-23:59",
    91  			delta: 0,
    92  			err:   true,
    93  		},
    94  	}
    95  
    96  	for i, c := range tests {
    97  		t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
    98  			start, end, err := parseRevocableZone(c.rz)
    99  			if (err != nil) != c.err {
   100  				t.Errorf("want %v ,got %v, err: %v", c.err, err != nil, err)
   101  			}
   102  
   103  			if end.Unix()-start.Unix() != c.delta {
   104  				t.Errorf("want %v, got %v", c.delta, end.Unix()-start.Unix())
   105  			}
   106  
   107  		})
   108  	}
   109  }
   110  
   111  func Test_TDM(t *testing.T) {
   112  	var tmp *cache.SchedulerCache
   113  	patchUpdateQueueStatus := gomonkey.ApplyMethod(reflect.TypeOf(tmp), "UpdateQueueStatus", func(scCache *cache.SchedulerCache, queue *api.QueueInfo) error {
   114  		return nil
   115  	})
   116  	defer patchUpdateQueueStatus.Reset()
   117  
   118  	framework.RegisterPluginBuilder(PluginName, New)
   119  	defer framework.CleanupPluginBuilders()
   120  
   121  	p1 := util.BuildPod("c1", "p1", "", v1.PodPending, api.BuildResourceList("1", "1Gi"), "pg1", make(map[string]string), make(map[string]string))
   122  	p2 := util.BuildPod("c1", "p2", "", v1.PodPending, api.BuildResourceList("1", "1Gi"), "pg1", make(map[string]string), make(map[string]string))
   123  	p3 := util.BuildPod("c1", "p3", "", v1.PodPending, api.BuildResourceList("1", "1Gi"), "pg1", make(map[string]string), make(map[string]string))
   124  
   125  	p1.Annotations[schedulingv2.RevocableZone] = "*"
   126  	p3.Annotations[schedulingv2.RevocableZone] = "*"
   127  
   128  	n1 := util.BuildNode("n1", api.BuildResourceList("16", "64Gi", []api.ScalarResource{{Name: "pods", Value: "10"}}...), map[string]string{
   129  		schedulingv2.RevocableZone: "rz1",
   130  	})
   131  
   132  	n2 := util.BuildNode("n2", api.BuildResourceList("16", "64Gi", []api.ScalarResource{{Name: "pods", Value: "10"}}...), map[string]string{
   133  		schedulingv2.RevocableZone: "rz1",
   134  	})
   135  
   136  	n3 := util.BuildNode("n3", api.BuildResourceList("16", "64Gi", []api.ScalarResource{{Name: "pods", Value: "10"}}...), map[string]string{})
   137  	n4 := util.BuildNode("n4", api.BuildResourceList("16", "64Gi", []api.ScalarResource{{Name: "pods", Value: "10"}}...), map[string]string{})
   138  
   139  	n5 := util.BuildNode("n5", api.BuildResourceList("16", "64Gi", []api.ScalarResource{{Name: "pods", Value: "10"}}...), map[string]string{
   140  		schedulingv2.RevocableZone: "rz2",
   141  	})
   142  
   143  	pg1 := &schedulingv2.PodGroup{
   144  		ObjectMeta: metav1.ObjectMeta{
   145  			Name:      "pg1",
   146  			Namespace: "c1",
   147  		},
   148  		Spec: schedulingv2.PodGroupSpec{
   149  			Queue: "c1",
   150  		},
   151  	}
   152  
   153  	queue1 := &schedulingv2.Queue{
   154  		ObjectMeta: metav1.ObjectMeta{
   155  			Name: "c1",
   156  		},
   157  		Spec: schedulingv2.QueueSpec{
   158  			Weight: 1,
   159  		},
   160  	}
   161  
   162  	tests := []struct {
   163  		name               string
   164  		podGroups          []*schedulingv2.PodGroup
   165  		pod                *v1.Pod
   166  		nodes              []*v1.Node
   167  		queues             []*schedulingv2.Queue
   168  		predicatedExpected map[string]bool
   169  		scoreExpected      map[string]map[string]float64
   170  	}{
   171  		{
   172  			name: "preemptable task rz available",
   173  			podGroups: []*schedulingv2.PodGroup{
   174  				pg1,
   175  			},
   176  			queues: []*schedulingv2.Queue{
   177  				queue1,
   178  			},
   179  			pod: p1,
   180  			nodes: []*v1.Node{
   181  				n1, n2, n3, n4, n5,
   182  			},
   183  			predicatedExpected: map[string]bool{"n1": true, "n2": true, "n3": true, "n4": true},
   184  			scoreExpected: map[string]map[string]float64{
   185  				"c1/p1": {
   186  					"n1": 100,
   187  					"n2": 100,
   188  					"n3": 0,
   189  					"n4": 0,
   190  				},
   191  			},
   192  		},
   193  		{
   194  			name: "not preemptable task",
   195  			podGroups: []*schedulingv2.PodGroup{
   196  				pg1,
   197  			},
   198  			queues: []*schedulingv2.Queue{
   199  				queue1,
   200  			},
   201  			pod: p2,
   202  			nodes: []*v1.Node{
   203  				n1, n2, n3, n4, n5,
   204  			},
   205  			predicatedExpected: map[string]bool{"n3": true, "n4": true},
   206  			scoreExpected: map[string]map[string]float64{
   207  				"c1/p2": {
   208  					"n3": 0,
   209  					"n4": 0,
   210  				},
   211  			},
   212  		},
   213  		{
   214  			name: "preemptable task and multiple rz",
   215  			podGroups: []*schedulingv2.PodGroup{
   216  				pg1,
   217  			},
   218  			queues: []*schedulingv2.Queue{
   219  				queue1,
   220  			},
   221  			pod: p3,
   222  			nodes: []*v1.Node{
   223  				n1, n2, n3, n4, n5,
   224  			},
   225  			predicatedExpected: map[string]bool{"n1": true, "n2": true, "n3": true, "n4": true},
   226  			scoreExpected: map[string]map[string]float64{
   227  				"c1/p3": {
   228  					"n1": 100,
   229  					"n2": 100,
   230  					"n3": 0,
   231  					"n4": 0,
   232  				},
   233  			},
   234  		},
   235  	}
   236  
   237  	for i, test := range tests {
   238  		t.Run(fmt.Sprintf("case %v %v", i, test.name), func(t *testing.T) {
   239  			binder := &util.FakeBinder{
   240  				Binds:   map[string]string{},
   241  				Channel: make(chan string),
   242  			}
   243  			schedulerCache := &cache.SchedulerCache{
   244  				Nodes:         make(map[string]*api.NodeInfo),
   245  				Jobs:          make(map[api.JobID]*api.JobInfo),
   246  				Queues:        make(map[api.QueueID]*api.QueueInfo),
   247  				Binder:        binder,
   248  				StatusUpdater: &util.FakeStatusUpdater{},
   249  				VolumeBinder:  &util.FakeVolumeBinder{},
   250  
   251  				Recorder: record.NewFakeRecorder(100),
   252  			}
   253  			for _, node := range test.nodes {
   254  				schedulerCache.AddOrUpdateNode(node)
   255  			}
   256  
   257  			schedulerCache.AddPod(test.pod)
   258  
   259  			for _, ss := range test.podGroups {
   260  				schedulerCache.AddPodGroupV1beta1(ss)
   261  			}
   262  			for _, q := range test.queues {
   263  				schedulerCache.AddQueueV1beta1(q)
   264  			}
   265  
   266  			trueValue := true
   267  			ssn := framework.OpenSession(schedulerCache, []conf.Tier{
   268  				{
   269  					Plugins: []conf.PluginOption{
   270  						{
   271  							Name:             PluginName,
   272  							EnabledNodeOrder: &trueValue,
   273  							EnabledPredicate: &trueValue,
   274  							Arguments: map[string]interface{}{
   275  								"tdm.revocable-zone.rz1": "0:00-0:00",
   276  								"tdm.revocable-zone.rz2": "0:00-0:01",
   277  							},
   278  						},
   279  					},
   280  				},
   281  			}, nil)
   282  
   283  			defer framework.CloseSession(ssn)
   284  
   285  			for _, job := range ssn.Jobs {
   286  				for _, task := range job.Tasks {
   287  					taskID := fmt.Sprintf("%s/%s", task.Namespace, task.Name)
   288  
   289  					predicatedNode := make([]*api.NodeInfo, 0)
   290  					for _, node := range ssn.Nodes {
   291  						predicateStatus, err := ssn.PredicateFn(task, node)
   292  						if err != nil {
   293  							continue
   294  						}
   295  						predicateIsSuccess := true
   296  						for _, status := range predicateStatus {
   297  							if status != nil && status.Code != api.Success {
   298  								predicateIsSuccess = false
   299  								break
   300  							}
   301  						}
   302  						if predicateIsSuccess == false {
   303  							continue
   304  						}
   305  						predicatedNode = append(predicatedNode, node)
   306  					}
   307  
   308  					if len(predicatedNode) != len(test.predicatedExpected) {
   309  						t.Errorf("want %v nodes,but got %v", len(test.predicatedExpected), len(predicatedNode))
   310  					}
   311  
   312  					for _, node := range predicatedNode {
   313  						if !test.predicatedExpected[node.Name] {
   314  							t.Errorf("want node: %v,but not found", node.Name)
   315  						}
   316  					}
   317  
   318  					for _, node := range predicatedNode {
   319  						score, err := ssn.NodeOrderFn(task, node)
   320  						if err != nil {
   321  							t.Errorf("task %s on node %s has err %v", taskID, node.Name, err)
   322  							continue
   323  						}
   324  						if expectScore := test.scoreExpected[taskID][node.Name]; math.Abs(expectScore-score) > eps {
   325  							t.Errorf("task %s on node %s expect have score %v, but get %v", taskID, node.Name, expectScore, score)
   326  						}
   327  					}
   328  				}
   329  			}
   330  		})
   331  	}
   332  }
   333  func Test_TDM_victimsFn(t *testing.T) {
   334  	var tmp *cache.SchedulerCache
   335  	patchUpdateQueueStatus := gomonkey.ApplyMethod(reflect.TypeOf(tmp), "UpdateQueueStatus", func(scCache *cache.SchedulerCache, queue *api.QueueInfo) error {
   336  		return nil
   337  	})
   338  	defer patchUpdateQueueStatus.Reset()
   339  
   340  	framework.RegisterPluginBuilder(PluginName, New)
   341  	defer framework.CleanupPluginBuilders()
   342  
   343  	p1 := util.BuildPod("c1", "p1", "n1", v1.PodRunning, api.BuildResourceList("1", "1Gi"), "pg1", make(map[string]string), make(map[string]string))
   344  	p2 := util.BuildPod("c1", "p2", "n1", v1.PodRunning, api.BuildResourceList("1", "1Gi"), "pg1", make(map[string]string), make(map[string]string))
   345  	p3 := util.BuildPod("c1", "p3", "n1", v1.PodRunning, api.BuildResourceList("1", "1Gi"), "pg1", make(map[string]string), make(map[string]string))
   346  	p4 := util.BuildPod("c1", "p4", "n1", v1.PodRunning, api.BuildResourceList("1", "1Gi"), "pg1", make(map[string]string), make(map[string]string))
   347  	p5 := util.BuildPod("c1", "p5", "n1", v1.PodRunning, api.BuildResourceList("1", "1Gi"), "pg1", make(map[string]string), make(map[string]string))
   348  	p6 := util.BuildPod("c2", "p6", "n2", v1.PodRunning, api.BuildResourceList("1", "1Gi"), "pg2", make(map[string]string), make(map[string]string))
   349  	p7 := util.BuildPod("c2", "p7", "n2", v1.PodRunning, api.BuildResourceList("1", "1Gi"), "pg2", make(map[string]string), make(map[string]string))
   350  	p8 := util.BuildPod("c2", "p8", "n2", v1.PodRunning, api.BuildResourceList("1", "1Gi"), "pg2", make(map[string]string), make(map[string]string))
   351  	p9 := util.BuildPod("c2", "p9", "n2", v1.PodRunning, api.BuildResourceList("1", "1Gi"), "pg2", make(map[string]string), make(map[string]string))
   352  	p10 := util.BuildPod("c2", "p10", "n2", v1.PodRunning, api.BuildResourceList("1", "1Gi"), "pg2", make(map[string]string), make(map[string]string))
   353  
   354  	p1.Annotations[schedulingv2.PodPreemptable] = "true"
   355  	p2.Annotations[schedulingv2.PodPreemptable] = "true"
   356  	p3.Annotations[schedulingv2.PodPreemptable] = "true"
   357  
   358  	p4.Annotations[schedulingv2.PodPreemptable] = "false"
   359  	p5.Annotations[schedulingv2.PodPreemptable] = "false"
   360  
   361  	p6.Annotations[schedulingv2.PodPreemptable] = "true"
   362  	p7.Annotations[schedulingv2.PodPreemptable] = "true"
   363  	p8.Annotations[schedulingv2.PodPreemptable] = "true"
   364  	p9.Annotations[schedulingv2.PodPreemptable] = "true"
   365  	p10.Annotations[schedulingv2.PodPreemptable] = "true"
   366  
   367  	n1 := util.BuildNode("n1", api.BuildResourceList("16", "64Gi", []api.ScalarResource{{Name: "pods", Value: "10"}}...), map[string]string{
   368  		schedulingv2.RevocableZone: "rz1",
   369  	})
   370  
   371  	n2 := util.BuildNode("n2", api.BuildResourceList("16", "64Gi", []api.ScalarResource{{Name: "pods", Value: "10"}}...), map[string]string{
   372  		schedulingv2.RevocableZone: "rz1",
   373  	})
   374  
   375  	queue1 := &schedulingv2.Queue{
   376  		ObjectMeta: metav1.ObjectMeta{
   377  			Name: "c1",
   378  		},
   379  		Spec: schedulingv2.QueueSpec{
   380  			Weight: 1,
   381  		},
   382  	}
   383  
   384  	queue2 := &schedulingv2.Queue{
   385  		ObjectMeta: metav1.ObjectMeta{
   386  			Name: "c2",
   387  		},
   388  		Spec: schedulingv2.QueueSpec{
   389  			Weight: 1,
   390  		},
   391  	}
   392  
   393  	tests := []struct {
   394  		podGroups []*schedulingv2.PodGroup
   395  		pods      []*v1.Pod
   396  		nodes     []*v1.Node
   397  		queues    []*schedulingv2.Queue
   398  		args      framework.Arguments
   399  		want      int
   400  	}{
   401  		{
   402  			podGroups: []*schedulingv2.PodGroup{
   403  				{
   404  					ObjectMeta: metav1.ObjectMeta{
   405  						Name:      "pg1",
   406  						Namespace: "c1",
   407  						Annotations: map[string]string{
   408  							schedulingv2.JDBMaxUnavailable: "30%",
   409  						},
   410  					},
   411  					Spec: schedulingv2.PodGroupSpec{
   412  						Queue: "c1",
   413  					},
   414  				},
   415  			},
   416  			queues: []*schedulingv2.Queue{
   417  				queue1,
   418  			},
   419  			pods: []*v1.Pod{
   420  				p1, p2, p3, p4, p5,
   421  			},
   422  			nodes: []*v1.Node{
   423  				n1,
   424  			},
   425  			args: framework.Arguments{
   426  				"tdm.revocable-zone.rz1": "0:00-0:01",
   427  				"tdm.evict.period":       "100ms",
   428  			},
   429  			want: 2,
   430  		},
   431  		{
   432  			podGroups: []*schedulingv2.PodGroup{
   433  				{
   434  					ObjectMeta: metav1.ObjectMeta{
   435  						Name:      "pg1",
   436  						Namespace: "c1",
   437  						Annotations: map[string]string{
   438  							schedulingv2.JDBMaxUnavailable: "30%",
   439  						},
   440  					},
   441  					Spec: schedulingv2.PodGroupSpec{
   442  						Queue: "c1",
   443  					},
   444  				},
   445  			},
   446  			queues: []*schedulingv2.Queue{
   447  				queue1,
   448  			},
   449  			pods: []*v1.Pod{
   450  				p1, p2, p3, p4, p5,
   451  			},
   452  			nodes: []*v1.Node{
   453  				n1,
   454  			},
   455  			args: framework.Arguments{
   456  				"tdm.revocable-zone.rz1": "0:00-0:00",
   457  				"tdm.evict.period":       "100ms",
   458  			},
   459  			want: 0,
   460  		},
   461  		{
   462  			podGroups: []*schedulingv2.PodGroup{
   463  				{
   464  					ObjectMeta: metav1.ObjectMeta{
   465  						Name:      "pg1",
   466  						Namespace: "c1",
   467  						Annotations: map[string]string{
   468  							schedulingv2.JDBMaxUnavailable: "99%",
   469  						},
   470  					},
   471  					Spec: schedulingv2.PodGroupSpec{
   472  						Queue: "c1",
   473  					},
   474  				},
   475  			},
   476  			queues: []*schedulingv2.Queue{
   477  				queue1,
   478  			},
   479  			pods: []*v1.Pod{
   480  				p1, p2, p3, p4, p5,
   481  			},
   482  			nodes: []*v1.Node{
   483  				n1,
   484  			},
   485  			args: framework.Arguments{
   486  				"tdm.revocable-zone.rz1": "0:00-0:01",
   487  				"tdm.evict.period":       "1s",
   488  			},
   489  			want: 3,
   490  		},
   491  		{
   492  			podGroups: []*schedulingv2.PodGroup{
   493  				{
   494  					ObjectMeta: metav1.ObjectMeta{
   495  						Name:      "pg2",
   496  						Namespace: "c2",
   497  						Annotations: map[string]string{
   498  							schedulingv2.JDBMaxUnavailable: "50%",
   499  						},
   500  					},
   501  					Spec: schedulingv2.PodGroupSpec{
   502  						Queue: "c2",
   503  					},
   504  				},
   505  			},
   506  			queues: []*schedulingv2.Queue{
   507  				queue2,
   508  			},
   509  			pods: []*v1.Pod{
   510  				p6, p7, p8, p9, p10,
   511  			},
   512  			nodes: []*v1.Node{
   513  				n2,
   514  			},
   515  			args: framework.Arguments{
   516  				"tdm.revocable-zone.rz1": "0:00-0:01",
   517  				"tdm.evict.period":       "1s",
   518  			},
   519  			want: 3,
   520  		},
   521  		{
   522  			podGroups: []*schedulingv2.PodGroup{
   523  				{
   524  					ObjectMeta: metav1.ObjectMeta{
   525  						Name:      "pg2",
   526  						Namespace: "c2",
   527  						Annotations: map[string]string{
   528  							schedulingv2.JDBMaxUnavailable: "50%",
   529  						},
   530  					},
   531  					Spec: schedulingv2.PodGroupSpec{
   532  						Queue: "c2",
   533  					},
   534  				},
   535  				{
   536  					ObjectMeta: metav1.ObjectMeta{
   537  						Name:      "pg1",
   538  						Namespace: "c1",
   539  						Annotations: map[string]string{
   540  							schedulingv2.JDBMaxUnavailable: "90%",
   541  						},
   542  					},
   543  					Spec: schedulingv2.PodGroupSpec{
   544  						Queue: "c1",
   545  					},
   546  				},
   547  			},
   548  			queues: []*schedulingv2.Queue{
   549  				queue1,
   550  				queue2,
   551  			},
   552  			pods: []*v1.Pod{
   553  				p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
   554  			},
   555  			nodes: []*v1.Node{
   556  				n1, n2,
   557  			},
   558  			args: framework.Arguments{
   559  				"tdm.revocable-zone.rz1": "0:00-0:01",
   560  				"tdm.evict.period":       "1s",
   561  			},
   562  			want: 6,
   563  		},
   564  		{
   565  			podGroups: []*schedulingv2.PodGroup{
   566  				{
   567  					ObjectMeta: metav1.ObjectMeta{
   568  						Name:      "pg2",
   569  						Namespace: "c2",
   570  						Annotations: map[string]string{
   571  							schedulingv2.JDBMaxUnavailable: "3",
   572  						},
   573  					},
   574  					Spec: schedulingv2.PodGroupSpec{
   575  						Queue: "c2",
   576  					},
   577  				},
   578  			},
   579  			queues: []*schedulingv2.Queue{
   580  				queue2,
   581  			},
   582  			pods: []*v1.Pod{
   583  				p6, p7, p8, p9, p10,
   584  			},
   585  			nodes: []*v1.Node{
   586  				n2,
   587  			},
   588  			args: framework.Arguments{
   589  				"tdm.revocable-zone.rz1": "0:00-0:01",
   590  				"tdm.evict.period":       "1s",
   591  			},
   592  			want: 3,
   593  		},
   594  		{
   595  			podGroups: []*schedulingv2.PodGroup{
   596  				{
   597  					ObjectMeta: metav1.ObjectMeta{
   598  						Name:      "pg2",
   599  						Namespace: "c2",
   600  						Annotations: map[string]string{
   601  							schedulingv2.JDBMinAvailable: "3",
   602  						},
   603  					},
   604  					Spec: schedulingv2.PodGroupSpec{
   605  						Queue: "c2",
   606  					},
   607  				},
   608  			},
   609  			queues: []*schedulingv2.Queue{
   610  				queue2,
   611  			},
   612  			pods: []*v1.Pod{
   613  				p6, p7, p8, p9, p10,
   614  			},
   615  			nodes: []*v1.Node{
   616  				n2,
   617  			},
   618  			args: framework.Arguments{
   619  				"tdm.revocable-zone.rz1": "0:00-0:01",
   620  				"tdm.evict.period":       "1s",
   621  			},
   622  			want: 2,
   623  		},
   624  		{
   625  			podGroups: []*schedulingv2.PodGroup{
   626  				{
   627  					ObjectMeta: metav1.ObjectMeta{
   628  						Name:      "pg2",
   629  						Namespace: "c2",
   630  						Annotations: map[string]string{
   631  							schedulingv2.JDBMinAvailable: "30%",
   632  						},
   633  					},
   634  					Spec: schedulingv2.PodGroupSpec{
   635  						Queue: "c2",
   636  					},
   637  				},
   638  			},
   639  			queues: []*schedulingv2.Queue{
   640  				queue2,
   641  			},
   642  			pods: []*v1.Pod{
   643  				p6, p7, p8, p9, p10,
   644  			},
   645  			nodes: []*v1.Node{
   646  				n2,
   647  			},
   648  			args: framework.Arguments{
   649  				"tdm.revocable-zone.rz1": "0:00-0:01",
   650  				"tdm.evict.period":       "1s",
   651  			},
   652  			want: 3,
   653  		},
   654  		{
   655  			podGroups: []*schedulingv2.PodGroup{
   656  				{
   657  					ObjectMeta: metav1.ObjectMeta{
   658  						Name:      "pg2",
   659  						Namespace: "c2",
   660  						Annotations: map[string]string{
   661  							schedulingv2.JDBMinAvailable: "2",
   662  						},
   663  					},
   664  					Spec: schedulingv2.PodGroupSpec{
   665  						Queue: "c2",
   666  					},
   667  				},
   668  				{
   669  					ObjectMeta: metav1.ObjectMeta{
   670  						Name:      "pg1",
   671  						Namespace: "c1",
   672  						Annotations: map[string]string{
   673  							schedulingv2.JDBMaxUnavailable: "3",
   674  						},
   675  					},
   676  					Spec: schedulingv2.PodGroupSpec{
   677  						Queue: "c1",
   678  					},
   679  				},
   680  			},
   681  			queues: []*schedulingv2.Queue{
   682  				queue1,
   683  				queue2,
   684  			},
   685  			pods: []*v1.Pod{
   686  				p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
   687  			},
   688  			nodes: []*v1.Node{
   689  				n1, n2,
   690  			},
   691  			args: framework.Arguments{
   692  				"tdm.revocable-zone.rz1": "0:00-0:01",
   693  				"tdm.evict.period":       "1s",
   694  			},
   695  			want: 6,
   696  		},
   697  	}
   698  
   699  	for i, test := range tests {
   700  		t.Run(fmt.Sprintf("case %v", i), func(t *testing.T) {
   701  			binder := &util.FakeBinder{
   702  				Binds:   map[string]string{},
   703  				Channel: make(chan string),
   704  			}
   705  			schedulerCache := &cache.SchedulerCache{
   706  				Nodes:         make(map[string]*api.NodeInfo),
   707  				Jobs:          make(map[api.JobID]*api.JobInfo),
   708  				Queues:        make(map[api.QueueID]*api.QueueInfo),
   709  				Binder:        binder,
   710  				StatusUpdater: &util.FakeStatusUpdater{},
   711  				VolumeBinder:  &util.FakeVolumeBinder{},
   712  
   713  				Recorder: record.NewFakeRecorder(100),
   714  			}
   715  			for _, node := range test.nodes {
   716  				schedulerCache.AddOrUpdateNode(node)
   717  			}
   718  
   719  			for _, pod := range test.pods {
   720  				schedulerCache.AddPod(pod)
   721  			}
   722  
   723  			for _, ss := range test.podGroups {
   724  				schedulerCache.AddPodGroupV1beta1(ss)
   725  			}
   726  			for _, q := range test.queues {
   727  				schedulerCache.AddQueueV1beta1(q)
   728  			}
   729  
   730  			trueValue := true
   731  			ssn := framework.OpenSession(schedulerCache, []conf.Tier{
   732  				{
   733  					Plugins: []conf.PluginOption{
   734  						{
   735  							Name:          PluginName,
   736  							EnabledVictim: &trueValue,
   737  							Arguments:     test.args,
   738  						},
   739  					},
   740  				},
   741  			}, nil)
   742  
   743  			defer framework.CloseSession(ssn)
   744  
   745  			d, _ := time.ParseDuration(test.args[evictPeriodLabel].(string))
   746  			time.Sleep(d)
   747  			tasks := make([]*api.TaskInfo, 0)
   748  			res := ssn.VictimTasks(tasks)
   749  			if len(res) != test.want {
   750  				t.Errorf("want %v, got %v", test.want, len(res))
   751  				return
   752  			}
   753  
   754  		})
   755  	}
   756  }