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 }