volcano.sh/volcano@v1.9.0/pkg/scheduler/plugins/drf/hdrf_test.go (about)

     1  package drf
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/agiledragon/gomonkey/v2"
    10  	v1 "k8s.io/api/core/v1"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/client-go/tools/record"
    13  	"k8s.io/klog/v2"
    14  
    15  	schedulingv1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1"
    16  	"volcano.sh/volcano/cmd/scheduler/app/options"
    17  	"volcano.sh/volcano/pkg/scheduler/actions/allocate"
    18  	"volcano.sh/volcano/pkg/scheduler/api"
    19  	"volcano.sh/volcano/pkg/scheduler/cache"
    20  	"volcano.sh/volcano/pkg/scheduler/conf"
    21  	"volcano.sh/volcano/pkg/scheduler/framework"
    22  	"volcano.sh/volcano/pkg/scheduler/plugins/proportion"
    23  	"volcano.sh/volcano/pkg/scheduler/util"
    24  )
    25  
    26  func makePods(num int, cpu, mem, podGroupName string) []*v1.Pod {
    27  	pods := []*v1.Pod{}
    28  	for i := 0; i < num; i++ {
    29  		pods = append(pods, util.BuildPod("default",
    30  			fmt.Sprintf("%s-p%d", podGroupName, i), "",
    31  			v1.PodPending, api.BuildResourceList(cpu, mem),
    32  			podGroupName, make(map[string]string), make(map[string]string)))
    33  	}
    34  	return pods
    35  }
    36  
    37  type queueSpec struct {
    38  	name      string
    39  	hierarchy string
    40  	weights   string
    41  }
    42  
    43  type pgSpec struct {
    44  	taskNum int
    45  	cpu     string
    46  	mem     string
    47  	pg      string
    48  	queue   string
    49  }
    50  
    51  func TestHDRF(t *testing.T) {
    52  	klog.InitFlags(nil)
    53  	flag.Set("v", "4")
    54  	flag.Set("alsologtostderr", "true")
    55  	var tmp *cache.SchedulerCache
    56  	patches := gomonkey.ApplyMethod(reflect.TypeOf(tmp), "AddBindTask", func(scCache *cache.SchedulerCache, task *api.TaskInfo) error {
    57  		scCache.Binder.Bind(nil, []*api.TaskInfo{task})
    58  		return nil
    59  	})
    60  	defer patches.Reset()
    61  
    62  	patchUpdateQueueStatus := gomonkey.ApplyMethod(reflect.TypeOf(tmp), "UpdateQueueStatus", func(scCache *cache.SchedulerCache, queue *api.QueueInfo) error {
    63  		return nil
    64  	})
    65  	defer patchUpdateQueueStatus.Reset()
    66  
    67  	s := options.NewServerOption()
    68  	s.MinNodesToFind = 100
    69  	s.PercentageOfNodesToFind = 100
    70  	s.RegisterOptions()
    71  
    72  	framework.RegisterPluginBuilder(PluginName, New)
    73  	framework.RegisterPluginBuilder("proportion", proportion.New)
    74  	defer framework.CleanupPluginBuilders()
    75  
    76  	tests := []struct {
    77  		name       string
    78  		pgSpecs    []pgSpec
    79  		nodes      []*v1.Node
    80  		queues     []*schedulingv1.Queue
    81  		queueSpecs []queueSpec
    82  		expected   map[string]*api.Resource
    83  	}{
    84  		{
    85  			name: "rescaling test",
    86  			pgSpecs: []pgSpec{
    87  				{
    88  					taskNum: 10,
    89  					cpu:     "1",
    90  					mem:     "1G",
    91  					pg:      "pg1",
    92  					queue:   "root-sci",
    93  				},
    94  				{
    95  					taskNum: 10,
    96  					cpu:     "1",
    97  					mem:     "0G",
    98  					pg:      "pg21",
    99  					queue:   "root-eng-dev",
   100  				},
   101  				{
   102  					taskNum: 10,
   103  					cpu:     "0",
   104  					mem:     "1G",
   105  					pg:      "pg22",
   106  					queue:   "root-eng-prod",
   107  				},
   108  			},
   109  			nodes: []*v1.Node{util.BuildNode("n",
   110  				api.BuildResourceList("10", "10G", []api.ScalarResource{{Name: "pods", Value: "50"}}...),
   111  				make(map[string]string))},
   112  			queueSpecs: []queueSpec{
   113  				{
   114  					name:      "root-sci",
   115  					hierarchy: "root/sci",
   116  					weights:   "100/50",
   117  				},
   118  				{
   119  					name:      "root-eng-dev",
   120  					hierarchy: "root/eng/dev",
   121  					weights:   "100/50/50",
   122  				},
   123  				{
   124  					name:      "root-eng-prod",
   125  					hierarchy: "root/eng/prod",
   126  					weights:   "100/50/50",
   127  				},
   128  			},
   129  			expected: map[string]*api.Resource{
   130  				"pg1": {
   131  					MilliCPU:        5000,
   132  					Memory:          5000000000,
   133  					ScalarResources: map[v1.ResourceName]float64{"pods": 5},
   134  				},
   135  				"pg21": {
   136  					MilliCPU:        5000,
   137  					Memory:          0,
   138  					ScalarResources: map[v1.ResourceName]float64{"pods": 5},
   139  				},
   140  				"pg22": {
   141  					MilliCPU:        0,
   142  					Memory:          5000000000,
   143  					ScalarResources: map[v1.ResourceName]float64{"pods": 5},
   144  				},
   145  			},
   146  		},
   147  		{
   148  			name: "blocking nodes test",
   149  			pgSpecs: []pgSpec{
   150  				{
   151  					taskNum: 30,
   152  					cpu:     "1",
   153  					mem:     "0G",
   154  					pg:      "pg1",
   155  					queue:   "root-pg1",
   156  				},
   157  				{
   158  					taskNum: 30,
   159  					cpu:     "1",
   160  					mem:     "0G",
   161  					pg:      "pg2",
   162  					queue:   "root-pg2",
   163  				},
   164  				{
   165  					taskNum: 30,
   166  					cpu:     "1",
   167  					mem:     "0G",
   168  					pg:      "pg31",
   169  					queue:   "root-pg3-pg31",
   170  				},
   171  				{
   172  					taskNum: 30,
   173  					cpu:     "0",
   174  					mem:     "1G",
   175  					pg:      "pg32",
   176  					queue:   "root-pg3-pg32",
   177  				},
   178  				{
   179  					taskNum: 30,
   180  					cpu:     "0",
   181  					mem:     "1G",
   182  					pg:      "pg4",
   183  					queue:   "root-pg4",
   184  				},
   185  			},
   186  			nodes: []*v1.Node{util.BuildNode("n",
   187  				api.BuildResourceList("30", "30G", []api.ScalarResource{{Name: "pods", Value: "500"}}...),
   188  				make(map[string]string))},
   189  			queueSpecs: []queueSpec{
   190  				{
   191  					name:      "root-pg1",
   192  					hierarchy: "root/pg1",
   193  					weights:   "100/25",
   194  				},
   195  				{
   196  					name:      "root-pg2",
   197  					hierarchy: "root/pg2",
   198  					weights:   "100/25",
   199  				},
   200  				{
   201  					name:      "root-pg3-pg31",
   202  					hierarchy: "root/pg3/pg31",
   203  					weights:   "100/25/50",
   204  				},
   205  				{
   206  					name:      "root-pg3-pg32",
   207  					hierarchy: "root/pg3/pg32",
   208  					weights:   "100/25/50",
   209  				},
   210  				{
   211  					name:      "root-pg4",
   212  					hierarchy: "root/pg4",
   213  					weights:   "100/25",
   214  				},
   215  			},
   216  			expected: map[string]*api.Resource{
   217  
   218  				"pg1": {
   219  					MilliCPU:        10000,
   220  					Memory:          0,
   221  					ScalarResources: map[v1.ResourceName]float64{"pods": 10},
   222  				},
   223  				"pg": {
   224  					MilliCPU:        10000,
   225  					Memory:          0,
   226  					ScalarResources: map[v1.ResourceName]float64{"pods": 10},
   227  				},
   228  				"pg31": {
   229  					MilliCPU:        10000,
   230  					Memory:          0,
   231  					ScalarResources: map[v1.ResourceName]float64{"pods": 10},
   232  				},
   233  				"pg32": {
   234  					MilliCPU:        0,
   235  					Memory:          15000000000,
   236  					ScalarResources: map[v1.ResourceName]float64{"pods": 15},
   237  				},
   238  				"pg4": {
   239  					MilliCPU:        0,
   240  					Memory:          15000000000,
   241  					ScalarResources: map[v1.ResourceName]float64{"pods": 15},
   242  				},
   243  			},
   244  		},
   245  	}
   246  	for _, test := range tests {
   247  		if test.name == "blocking nodes test" {
   248  			// TODO(wangyang0616): First make sure that ut can run, and then fix the failed ut later
   249  			// See issue for details: https://github.com/volcano-sh/volcano/issues/2810
   250  			t.Skip("Test cases are not as expected, fixed later. see issue: #2810")
   251  		}
   252  
   253  		binder := &util.FakeBinder{
   254  			Binds:   map[string]string{},
   255  			Channel: make(chan string, 300),
   256  		}
   257  		schedulerCache := &cache.SchedulerCache{
   258  			Nodes:         make(map[string]*api.NodeInfo),
   259  			Jobs:          make(map[api.JobID]*api.JobInfo),
   260  			Queues:        make(map[api.QueueID]*api.QueueInfo),
   261  			Binder:        binder,
   262  			StatusUpdater: &util.FakeStatusUpdater{},
   263  			VolumeBinder:  &util.FakeVolumeBinder{},
   264  			Recorder:      record.NewFakeRecorder(100),
   265  		}
   266  		for _, node := range test.nodes {
   267  			schedulerCache.AddOrUpdateNode(node)
   268  		}
   269  		for _, q := range test.queueSpecs {
   270  			schedulerCache.AddQueueV1beta1(
   271  				&schedulingv1.Queue{
   272  					ObjectMeta: metav1.ObjectMeta{
   273  						Name: q.name,
   274  						Annotations: map[string]string{
   275  							schedulingv1.KubeHierarchyAnnotationKey:       q.hierarchy,
   276  							schedulingv1.KubeHierarchyWeightAnnotationKey: q.weights,
   277  						},
   278  					},
   279  					Spec: schedulingv1.QueueSpec{
   280  						Weight: 1,
   281  					},
   282  				})
   283  		}
   284  		for _, pgSpec := range test.pgSpecs {
   285  			pods := makePods(pgSpec.taskNum, pgSpec.cpu, pgSpec.mem, pgSpec.pg)
   286  			for _, pod := range pods {
   287  				schedulerCache.AddPod(pod)
   288  			}
   289  			schedulerCache.AddPodGroupV1beta1(&schedulingv1.PodGroup{
   290  				ObjectMeta: metav1.ObjectMeta{
   291  					Name:      pgSpec.pg,
   292  					Namespace: "default",
   293  				},
   294  				Spec: schedulingv1.PodGroupSpec{
   295  					Queue: pgSpec.queue,
   296  				},
   297  				Status: schedulingv1.PodGroupStatus{
   298  					Phase: schedulingv1.PodGroupInqueue,
   299  				},
   300  			})
   301  		}
   302  		trueValue := true
   303  		ssn := framework.OpenSession(schedulerCache, []conf.Tier{
   304  			{
   305  				Plugins: []conf.PluginOption{
   306  					{
   307  						Name:              PluginName,
   308  						EnabledHierarchy:  &trueValue,
   309  						EnabledQueueOrder: &trueValue,
   310  						EnabledJobOrder:   &trueValue,
   311  					},
   312  					{
   313  						Name:               "proportion",
   314  						EnabledJobEnqueued: &trueValue,
   315  						EnabledQueueOrder:  &trueValue,
   316  						EnabledReclaimable: &trueValue,
   317  					},
   318  				},
   319  			},
   320  		}, nil)
   321  		defer framework.CloseSession(ssn)
   322  		allocateAction := allocate.New()
   323  
   324  		allocateAction.Execute(ssn)
   325  
   326  		for _, job := range ssn.Jobs {
   327  			if reflect.DeepEqual(test.expected, job.Allocated) {
   328  				t.Fatalf("%s: job %s expected resource %s, but got %s", test.name, job.Name, test.expected[job.Name], job.Allocated)
   329  			}
   330  		}
   331  
   332  	}
   333  }