volcano.sh/volcano@v1.9.0/pkg/scheduler/plugins/nodegroup/nodegroup_test.go (about) 1 package nodegroup 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 8 "github.com/agiledragon/gomonkey/v2" 9 v1 "k8s.io/api/core/v1" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/client-go/tools/record" 12 13 batch "volcano.sh/apis/pkg/apis/batch/v1alpha1" 14 schedulingv1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1" 15 "volcano.sh/volcano/pkg/scheduler/api" 16 "volcano.sh/volcano/pkg/scheduler/cache" 17 "volcano.sh/volcano/pkg/scheduler/conf" 18 "volcano.sh/volcano/pkg/scheduler/framework" 19 "volcano.sh/volcano/pkg/scheduler/util" 20 ) 21 22 func TestNodeGroup(t *testing.T) { 23 var tmp *cache.SchedulerCache 24 patchUpdateQueueStatus := gomonkey.ApplyMethod(reflect.TypeOf(tmp), "UpdateQueueStatus", func(scCache *cache.SchedulerCache, queue *api.QueueInfo) error { 25 return nil 26 }) 27 defer patchUpdateQueueStatus.Reset() 28 29 framework.RegisterPluginBuilder(PluginName, New) 30 defer framework.CleanupPluginBuilders() 31 32 p1 := util.BuildPod("c1", "p1", "", v1.PodPending, api.BuildResourceList("2", "4Gi"), "pg1", map[string]string{ 33 batch.QueueNameKey: "q1", 34 }, make(map[string]string)) 35 36 p2 := util.BuildPod("c1", "p2", "", v1.PodPending, api.BuildResourceList("2", "4Gi"), "pg2", map[string]string{ 37 batch.QueueNameKey: "q2", 38 }, make(map[string]string)) 39 40 n1 := util.BuildNode("n1", api.BuildResourceList("2", "4Gi"), map[string]string{ 41 NodeGroupNameKey: "group1", 42 }) 43 n2 := util.BuildNode("n2", api.BuildResourceList("4", "16Gi"), map[string]string{ 44 NodeGroupNameKey: "group2", 45 }) 46 n3 := util.BuildNode("n3", api.BuildResourceList("4", "16Gi"), map[string]string{ 47 NodeGroupNameKey: "group3", 48 }) 49 n4 := util.BuildNode("n4", api.BuildResourceList("4", "16Gi"), map[string]string{ 50 NodeGroupNameKey: "group4", 51 }) 52 n5 := util.BuildNode("n5", api.BuildResourceList("4", "16Gi"), make(map[string]string)) 53 54 pg1 := &schedulingv1.PodGroup{ 55 ObjectMeta: metav1.ObjectMeta{ 56 Name: "pg1", 57 Namespace: "c1", 58 }, 59 Spec: schedulingv1.PodGroupSpec{ 60 Queue: "q1", 61 }, 62 } 63 64 pg2 := &schedulingv1.PodGroup{ 65 ObjectMeta: metav1.ObjectMeta{ 66 Name: "pg2", 67 Namespace: "c1", 68 }, 69 Spec: schedulingv1.PodGroupSpec{ 70 Queue: "q2", 71 }, 72 } 73 74 queue1 := &schedulingv1.Queue{ 75 ObjectMeta: metav1.ObjectMeta{ 76 Name: "q1", 77 }, 78 Spec: schedulingv1.QueueSpec{ 79 Weight: 1, 80 Affinity: &schedulingv1.Affinity{ 81 NodeGroupAffinity: &schedulingv1.NodeGroupAffinity{ 82 RequiredDuringSchedulingIgnoredDuringExecution: []string{"group1", "group3"}, 83 PreferredDuringSchedulingIgnoredDuringExecution: []string{"group3"}, 84 }, 85 NodeGroupAntiAffinity: &schedulingv1.NodeGroupAntiAffinity{ 86 RequiredDuringSchedulingIgnoredDuringExecution: []string{"group2", "group4"}, 87 PreferredDuringSchedulingIgnoredDuringExecution: []string{"group4"}, 88 }, 89 }, 90 }, 91 } 92 93 queue2 := &schedulingv1.Queue{ 94 ObjectMeta: metav1.ObjectMeta{ 95 Name: "q2", 96 }, 97 Spec: schedulingv1.QueueSpec{ 98 Weight: 1, 99 Affinity: &schedulingv1.Affinity{ 100 NodeGroupAffinity: &schedulingv1.NodeGroupAffinity{ 101 RequiredDuringSchedulingIgnoredDuringExecution: []string{"group1"}, 102 PreferredDuringSchedulingIgnoredDuringExecution: []string{"group3"}, 103 }, 104 NodeGroupAntiAffinity: &schedulingv1.NodeGroupAntiAffinity{ 105 RequiredDuringSchedulingIgnoredDuringExecution: []string{"group2"}, 106 PreferredDuringSchedulingIgnoredDuringExecution: []string{"group4"}, 107 }, 108 }, 109 }, 110 } 111 112 tests := []struct { 113 name string 114 podGroups []*schedulingv1.PodGroup 115 pods []*v1.Pod 116 nodes []*v1.Node 117 queues []*schedulingv1.Queue 118 arguments framework.Arguments 119 expected map[string]map[string]float64 120 expectedStatus map[string]map[string]int 121 }{ 122 { 123 name: "case: soft constraints is subset of hard constraints", 124 podGroups: []*schedulingv1.PodGroup{ 125 pg1, 126 }, 127 queues: []*schedulingv1.Queue{ 128 queue1, 129 }, 130 pods: []*v1.Pod{ 131 p1, 132 }, 133 nodes: []*v1.Node{ 134 n1, n2, n3, n4, n5, 135 }, 136 arguments: framework.Arguments{}, 137 expected: map[string]map[string]float64{ 138 "c1/p1": { 139 "n1": 100, 140 "n2": 0.0, 141 "n3": 150, 142 "n4": -1, 143 "n5": 0.0, 144 }, 145 }, 146 expectedStatus: map[string]map[string]int{ 147 "c1/p1": { 148 "n1": api.Success, 149 "n2": api.UnschedulableAndUnresolvable, 150 "n3": api.Success, 151 "n4": api.Success, 152 "n5": api.UnschedulableAndUnresolvable, 153 }, 154 }, 155 }, 156 { 157 // test unnormal case 158 name: "case: soft constraints is not subset of hard constraints", 159 podGroups: []*schedulingv1.PodGroup{ 160 pg2, 161 }, 162 queues: []*schedulingv1.Queue{ 163 queue2, 164 }, 165 pods: []*v1.Pod{ 166 p2, 167 }, 168 nodes: []*v1.Node{ 169 n1, n2, n3, n4, n5, 170 }, 171 arguments: framework.Arguments{}, 172 expected: map[string]map[string]float64{ 173 "c1/p2": { 174 "n1": 100, 175 "n2": 0.0, 176 "n3": 50, 177 "n4": -1, 178 "n5": 0.0, 179 }, 180 }, 181 expectedStatus: map[string]map[string]int{ 182 "c1/p2": { 183 "n1": api.Success, 184 "n2": api.UnschedulableAndUnresolvable, 185 "n3": api.Success, 186 "n4": api.Success, 187 "n5": api.UnschedulableAndUnresolvable, 188 }, 189 }, 190 }, 191 } 192 193 for i, test := range tests { 194 t.Run(fmt.Sprintf("case %v %v", i, test.name), func(t *testing.T) { 195 binder := &util.FakeBinder{ 196 Binds: map[string]string{}, 197 Channel: make(chan string), 198 } 199 schedulerCache := &cache.SchedulerCache{ 200 Nodes: make(map[string]*api.NodeInfo), 201 Jobs: make(map[api.JobID]*api.JobInfo), 202 Queues: make(map[api.QueueID]*api.QueueInfo), 203 Binder: binder, 204 StatusUpdater: &util.FakeStatusUpdater{}, 205 VolumeBinder: &util.FakeVolumeBinder{}, 206 207 Recorder: record.NewFakeRecorder(100), 208 } 209 210 for _, node := range test.nodes { 211 schedulerCache.AddOrUpdateNode(node) 212 } 213 for _, pod := range test.pods { 214 schedulerCache.AddPod(pod) 215 } 216 for _, ss := range test.podGroups { 217 schedulerCache.AddPodGroupV1beta1(ss) 218 } 219 for _, q := range test.queues { 220 schedulerCache.AddQueueV1beta1(q) 221 } 222 223 trueValue := true 224 ssn := framework.OpenSession(schedulerCache, []conf.Tier{ 225 { 226 Plugins: []conf.PluginOption{ 227 { 228 Name: PluginName, 229 EnabledNodeOrder: &trueValue, 230 EnabledPredicate: &trueValue, 231 Arguments: test.arguments, 232 }, 233 }, 234 }, 235 }, nil) 236 defer framework.CloseSession(ssn) 237 238 for _, job := range ssn.Jobs { 239 for _, task := range job.Tasks { 240 taskID := fmt.Sprintf("%s/%s", task.Namespace, task.Name) 241 242 for _, node := range ssn.Nodes { 243 score, err := ssn.NodeOrderFn(task, node) 244 if err != nil { 245 t.Errorf("case%d: task %s on node %s has err %v", i, taskID, node.Name, err) 246 continue 247 } 248 if expectScore := test.expected[taskID][node.Name]; expectScore != score { 249 t.Errorf("case%d: task %s on node %s expect have score %v, but get %v", i, taskID, node.Name, expectScore, score) 250 } 251 252 status, _ := ssn.PredicateFn(task, node) 253 if expectStatus := test.expectedStatus[taskID][node.Name]; expectStatus != status[0].Code { 254 t.Errorf("case%d: task %s on node %s expect have status code %v, but get %v", i, taskID, node.Name, expectStatus, status[0].Code) 255 } 256 257 } 258 } 259 } 260 t.Logf("nodegroup unit test finished ") 261 }) 262 } 263 264 }