volcano.sh/volcano@v1.9.0/pkg/scheduler/plugins/binpack/binpack_test.go (about)

     1  /*
     2  Copyright 2019 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 binpack
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"github.com/agiledragon/gomonkey/v2"
    26  	v1 "k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/api/resource"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/client-go/tools/record"
    30  
    31  	schedulingv1 "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 TestArguments(t *testing.T) {
    44  	framework.RegisterPluginBuilder(PluginName, New)
    45  	defer framework.CleanupPluginBuilders()
    46  
    47  	arguments := framework.Arguments{
    48  		"binpack.weight":                    10,
    49  		"binpack.cpu":                       5,
    50  		"binpack.memory":                    2,
    51  		"binpack.resources":                 "nvidia.com/gpu, example.com/foo",
    52  		"binpack.resources.nvidia.com/gpu":  7,
    53  		"binpack.resources.example.com/foo": -3,
    54  	}
    55  
    56  	builder, ok := framework.GetPluginBuilder(PluginName)
    57  	if !ok {
    58  		t.Fatalf("should have plugin named %s", PluginName)
    59  	}
    60  
    61  	plugin := builder(arguments)
    62  	binpack, ok := plugin.(*binpackPlugin)
    63  	if !ok {
    64  		t.Fatalf("plugin should be %T, but not %T", binpack, plugin)
    65  	}
    66  
    67  	weight := binpack.weight
    68  	if weight.BinPackingWeight != 10 {
    69  		t.Errorf("weight should be 10, but not %v", weight.BinPackingWeight)
    70  	}
    71  	if weight.BinPackingCPU != 5 {
    72  		t.Errorf("cpu should be 5, but not %v", weight.BinPackingCPU)
    73  	}
    74  	if weight.BinPackingMemory != 2 {
    75  		t.Errorf("memory should be 2, but not %v", weight.BinPackingMemory)
    76  	}
    77  	for name, weight := range weight.BinPackingResources {
    78  		switch name {
    79  		case "nvidia.com/gpu":
    80  			if weight != 7 {
    81  				t.Errorf("gpu should be 7, but not %v", weight)
    82  			}
    83  		case "example.com/foo":
    84  			if weight != 1 {
    85  				t.Errorf("example.com/foo should be 1, but not %v", weight)
    86  			}
    87  		case v1.ResourceCPU:
    88  			if weight != 5 {
    89  				t.Errorf("%v should be 5, but not %v", v1.ResourceCPU, weight)
    90  			}
    91  		case v1.ResourceMemory:
    92  			if weight != 2 {
    93  				t.Errorf("%v should be 2, but not %v", v1.ResourceMemory, weight)
    94  			}
    95  		default:
    96  			t.Errorf("resource %s with weight %d should not appear", name, weight)
    97  		}
    98  	}
    99  }
   100  
   101  func addResource(resourceList v1.ResourceList, name v1.ResourceName, need string) {
   102  	resourceList[name] = resource.MustParse(need)
   103  }
   104  
   105  func TestNode(t *testing.T) {
   106  	var tmp *cache.SchedulerCache
   107  	patchUpdateQueueStatus := gomonkey.ApplyMethod(reflect.TypeOf(tmp), "UpdateQueueStatus", func(scCache *cache.SchedulerCache, queue *api.QueueInfo) error {
   108  		return nil
   109  	})
   110  	defer patchUpdateQueueStatus.Reset()
   111  
   112  	// TODO(wangyang0616): First make sure that ut can run, and then fix the failed ut later
   113  	// See issue for details: https://github.com/volcano-sh/volcano/issues/2810
   114  	t.Skip("Test cases are not as expected, fixed later. see issue: #2810")
   115  	framework.RegisterPluginBuilder(PluginName, New)
   116  	defer framework.CleanupPluginBuilders()
   117  
   118  	GPU := v1.ResourceName("nvidia.com/gpu")
   119  	FOO := v1.ResourceName("example.com/foo")
   120  
   121  	p1 := util.BuildPod("c1", "p1", "n1", v1.PodPending, api.BuildResourceList("1", "1Gi"), "pg1", make(map[string]string), make(map[string]string))
   122  	p2 := util.BuildPod("c1", "p2", "n3", v1.PodPending, api.BuildResourceList("1.5", "0Gi"), "pg1", make(map[string]string), make(map[string]string))
   123  	p3 := util.BuildPod("c1", "p3", "", v1.PodPending, api.BuildResourceList("2", "10Gi"), "pg1", make(map[string]string), make(map[string]string))
   124  	addResource(p3.Spec.Containers[0].Resources.Requests, GPU, "2")
   125  	p4 := util.BuildPod("c1", "p4", "", v1.PodPending, api.BuildResourceList("3", "4Gi"), "pg1", make(map[string]string), make(map[string]string))
   126  	addResource(p4.Spec.Containers[0].Resources.Requests, FOO, "3")
   127  
   128  	n1 := util.BuildNode("n1", api.BuildResourceList("2", "4Gi", []api.ScalarResource{{Name: "pods", Value: "10"}}...), make(map[string]string))
   129  	n2 := util.BuildNode("n2", api.BuildResourceList("4", "16Gi", []api.ScalarResource{{Name: "pods", Value: "10"}}...), make(map[string]string))
   130  	addResource(n2.Status.Allocatable, GPU, "4")
   131  	n3 := util.BuildNode("n3", api.BuildResourceList("2", "4Gi", []api.ScalarResource{{Name: "pods", Value: "10"}}...), make(map[string]string))
   132  	addResource(n3.Status.Allocatable, FOO, "16")
   133  
   134  	pg1 := &schedulingv1.PodGroup{
   135  		ObjectMeta: metav1.ObjectMeta{
   136  			Name:      "pg1",
   137  			Namespace: "c1",
   138  		},
   139  		Spec: schedulingv1.PodGroupSpec{
   140  			Queue: "c1",
   141  		},
   142  	}
   143  	queue1 := &schedulingv1.Queue{
   144  		ObjectMeta: metav1.ObjectMeta{
   145  			Name: "c1",
   146  		},
   147  		Spec: schedulingv1.QueueSpec{
   148  			Weight: 1,
   149  		},
   150  	}
   151  
   152  	tests := []struct {
   153  		name      string
   154  		podGroups []*schedulingv1.PodGroup
   155  		pods      []*v1.Pod
   156  		nodes     []*v1.Node
   157  		queues    []*schedulingv1.Queue
   158  		arguments framework.Arguments
   159  		expected  map[string]map[string]float64
   160  	}{
   161  		{
   162  			name: "single job",
   163  			podGroups: []*schedulingv1.PodGroup{
   164  				pg1,
   165  			},
   166  			queues: []*schedulingv1.Queue{
   167  				queue1,
   168  			},
   169  			pods: []*v1.Pod{
   170  				p1, p2, p3, p4,
   171  			},
   172  			nodes: []*v1.Node{
   173  				n1, n2, n3,
   174  			},
   175  			arguments: framework.Arguments{
   176  				"binpack.weight":                    10,
   177  				"binpack.cpu":                       2,
   178  				"binpack.memory":                    3,
   179  				"binpack.resources":                 "nvidia.com/gpu, example.com/foo",
   180  				"binpack.resources.nvidia.com/gpu":  7,
   181  				"binpack.resources.example.com/foo": 8,
   182  			},
   183  			expected: map[string]map[string]float64{
   184  				"c1/p1": {
   185  					"n1": 700,
   186  					"n2": 137.5,
   187  					"n3": 150,
   188  				},
   189  				"c1/p2": {
   190  					"n1": 0,
   191  					"n2": 375,
   192  					"n3": 0,
   193  				},
   194  				"c1/p3": {
   195  					"n1": 0,
   196  					"n2": 531.25,
   197  					"n3": 0,
   198  				},
   199  				"c1/p4": {
   200  					"n1": 0,
   201  					"n2": 173.076923076,
   202  					"n3": 346.153846153,
   203  				},
   204  			},
   205  		},
   206  		{
   207  			name: "single job",
   208  			podGroups: []*schedulingv1.PodGroup{
   209  				pg1,
   210  			},
   211  			queues: []*schedulingv1.Queue{
   212  				queue1,
   213  			},
   214  			pods: []*v1.Pod{
   215  				p1, p2, p3, p4,
   216  			},
   217  			nodes: []*v1.Node{
   218  				n1, n2, n3,
   219  			},
   220  			arguments: framework.Arguments{
   221  				"binpack.weight":                   1,
   222  				"binpack.cpu":                      1,
   223  				"binpack.memory":                   1,
   224  				"binpack.resources":                "nvidia.com/gpu",
   225  				"binpack.resources.nvidia.com/gpu": 23,
   226  			},
   227  			expected: map[string]map[string]float64{
   228  				"c1/p1": {
   229  					"n1": 75,
   230  					"n2": 15.625,
   231  					"n3": 12.5,
   232  				},
   233  				"c1/p2": {
   234  					"n1": 0,
   235  					"n2": 37.5,
   236  					"n3": 0,
   237  				},
   238  				"c1/p3": {
   239  					"n1": 0,
   240  					"n2": 50.5,
   241  					"n3": 0,
   242  				},
   243  				"c1/p4": {
   244  					"n1": 0,
   245  					"n2": 50,
   246  					"n3": 50,
   247  				},
   248  			},
   249  		},
   250  	}
   251  
   252  	for i, test := range tests {
   253  		binder := &util.FakeBinder{
   254  			Binds:   map[string]string{},
   255  			Channel: make(chan string),
   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  
   265  			Recorder: record.NewFakeRecorder(100),
   266  		}
   267  		for _, node := range test.nodes {
   268  			schedulerCache.AddOrUpdateNode(node)
   269  		}
   270  		for _, pod := range test.pods {
   271  			schedulerCache.AddPod(pod)
   272  		}
   273  		for _, ss := range test.podGroups {
   274  			schedulerCache.AddPodGroupV1beta1(ss)
   275  		}
   276  		for _, q := range test.queues {
   277  			schedulerCache.AddQueueV1beta1(q)
   278  		}
   279  
   280  		trueValue := true
   281  		ssn := framework.OpenSession(schedulerCache, []conf.Tier{
   282  			{
   283  				Plugins: []conf.PluginOption{
   284  					{
   285  						Name:             PluginName,
   286  						EnabledNodeOrder: &trueValue,
   287  						Arguments:        test.arguments,
   288  					},
   289  				},
   290  			},
   291  		}, nil)
   292  		defer framework.CloseSession(ssn)
   293  
   294  		for _, job := range ssn.Jobs {
   295  			for _, task := range job.Tasks {
   296  				taskID := fmt.Sprintf("%s/%s", task.Namespace, task.Name)
   297  				for _, node := range ssn.Nodes {
   298  					score, err := ssn.NodeOrderFn(task, node)
   299  					if err != nil {
   300  						t.Errorf("case%d: task %s on node %s has err %v", i, taskID, node.Name, err)
   301  						continue
   302  					}
   303  					if expectScore := test.expected[taskID][node.Name]; math.Abs(expectScore-score) > eps {
   304  						t.Errorf("case%d: task %s on node %s expect have score %v, but get %v", i, taskID, node.Name, expectScore, score)
   305  					}
   306  				}
   307  			}
   308  		}
   309  	}
   310  }