volcano.sh/volcano@v1.9.0/pkg/scheduler/plugins/task-topology/topology_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 tasktopology 18 19 import ( 20 "fmt" 21 "reflect" 22 "testing" 23 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 26 "volcano.sh/apis/pkg/apis/scheduling" 27 "volcano.sh/volcano/pkg/scheduler/api" 28 ) 29 30 func Test_readTopologyFromPgAnnotations(t *testing.T) { 31 cases := []struct { 32 description string 33 job *api.JobInfo 34 topology *TaskTopology 35 err error 36 }{ 37 { 38 description: "correct annotation", 39 job: &api.JobInfo{ 40 Name: "job1", 41 Namespace: "default", 42 Tasks: map[api.TaskID]*api.TaskInfo{ 43 "0": { 44 Name: "job1-ps-0", 45 }, 46 "1": { 47 Name: "job1-ps-1", 48 }, 49 "2": { 50 Name: "job1-worker-0", 51 }, 52 "3": { 53 Name: "job1-worker-1", 54 }, 55 "4": { 56 Name: "job1-chief-0", 57 }, 58 "5": { 59 Name: "job1-evaluator-0", 60 }, 61 }, 62 PodGroup: &api.PodGroup{ 63 PodGroup: scheduling.PodGroup{ 64 ObjectMeta: metav1.ObjectMeta{ 65 Annotations: map[string]string{ 66 JobAffinityAnnotations: "ps,worker;ps,chief", 67 JobAntiAffinityAnnotations: "ps;worker,chief", 68 TaskOrderAnnotations: "ps,worker,chief,evaluator", 69 }, 70 }, 71 }, 72 }, 73 }, 74 topology: &TaskTopology{ 75 Affinity: [][]string{ 76 { 77 "ps", 78 "worker", 79 }, 80 { 81 "ps", 82 "chief", 83 }, 84 }, 85 AntiAffinity: [][]string{ 86 { 87 "ps", 88 }, 89 { 90 "worker", 91 "chief", 92 }, 93 }, 94 TaskOrder: []string{ 95 "ps", 96 "worker", 97 "chief", 98 "evaluator", 99 }, 100 }, 101 err: nil, 102 }, 103 { 104 description: "correct annotation with tasks whose names contain `-`", 105 job: &api.JobInfo{ 106 Name: "job1", 107 Namespace: "default", 108 Tasks: map[api.TaskID]*api.TaskInfo{ 109 "0": { 110 Name: "job1-ps-some-0", 111 }, 112 "1": { 113 Name: "job1-ps-some-1", 114 }, 115 "2": { 116 Name: "job1-worker-another-some-0", 117 }, 118 "3": { 119 Name: "job1-worker-another-some-1", 120 }, 121 "4": { 122 Name: "job1-chief-kk-0", 123 }, 124 "5": { 125 Name: "job1-evaluator-tt-0", 126 }, 127 }, 128 PodGroup: &api.PodGroup{ 129 PodGroup: scheduling.PodGroup{ 130 ObjectMeta: metav1.ObjectMeta{ 131 Annotations: map[string]string{ 132 JobAffinityAnnotations: "ps-some,worker-another-some;ps-some,chief-kk", 133 JobAntiAffinityAnnotations: "ps-some;worker-another-some,chief-kk", 134 TaskOrderAnnotations: "ps-some,worker-another-some,chief-kk,evaluator-tt", 135 }, 136 }, 137 }, 138 }, 139 }, 140 topology: &TaskTopology{ 141 Affinity: [][]string{ 142 { 143 "ps-some", 144 "worker-another-some", 145 }, 146 { 147 "ps-some", 148 "chief-kk", 149 }, 150 }, 151 AntiAffinity: [][]string{ 152 { 153 "ps-some", 154 }, 155 { 156 "worker-another-some", 157 "chief-kk", 158 }, 159 }, 160 TaskOrder: []string{ 161 "ps-some", 162 "worker-another-some", 163 "chief-kk", 164 "evaluator-tt", 165 }, 166 }, 167 err: nil, 168 }, 169 { 170 description: "nil annotation", 171 job: &api.JobInfo{ 172 PodGroup: &api.PodGroup{ 173 PodGroup: scheduling.PodGroup{ 174 ObjectMeta: metav1.ObjectMeta{ 175 Annotations: nil, 176 }, 177 }, 178 }, 179 }, 180 topology: nil, 181 err: nil, 182 }, 183 { 184 description: "invalid annotation", 185 job: &api.JobInfo{ 186 Name: "job1", 187 Namespace: "default", 188 Tasks: map[api.TaskID]*api.TaskInfo{ 189 "0": { 190 Name: "job1-ps-0", 191 }, 192 "1": { 193 Name: "job1-ps-1", 194 }, 195 "2": { 196 Name: "job1-worker-0", 197 }, 198 "3": { 199 Name: "job1-worker-1", 200 }, 201 "4": { 202 Name: "job1-chief-0", 203 }, 204 "5": { 205 Name: "job1-evaluator-0", 206 }, 207 }, 208 PodGroup: &api.PodGroup{ 209 PodGroup: scheduling.PodGroup{ 210 ObjectMeta: metav1.ObjectMeta{ 211 Annotations: map[string]string{ 212 JobAffinityAnnotations: "$%GF$^trtqwrg^", 213 JobAntiAffinityAnnotations: "ps;worker,chief;#@FGEW^vfa897bgs;;", 214 TaskOrderAnnotations: "ps,worker,chief,evaluator,,,", 215 }, 216 }, 217 }, 218 }, 219 }, 220 topology: nil, 221 err: fmt.Errorf("task %s do not exist in job <%s/%s>", "$%GF$^trtqwrg^", "default", "job1"), 222 }, 223 { 224 description: "invalid task name", 225 job: &api.JobInfo{ 226 Name: "job1", 227 Namespace: "default", 228 Tasks: map[api.TaskID]*api.TaskInfo{ 229 "0": { 230 Name: "job1-ps-0", 231 }, 232 "1": { 233 Name: "job1-ps-1", 234 }, 235 "2": { 236 Name: "job1-worker-0", 237 }, 238 "3": { 239 Name: "job1-worker-1", 240 }, 241 }, 242 PodGroup: &api.PodGroup{ 243 PodGroup: scheduling.PodGroup{ 244 ObjectMeta: metav1.ObjectMeta{ 245 Annotations: map[string]string{ 246 JobAffinityAnnotations: "ps,worker", 247 JobAntiAffinityAnnotations: "ps", 248 TaskOrderAnnotations: "ps,worker,chief", 249 }, 250 }, 251 }, 252 }, 253 }, 254 topology: nil, 255 err: fmt.Errorf("task %s do not exist in job <%s/%s>", "chief", "default", "job1"), 256 }, 257 { 258 description: "duplicated task name", 259 job: &api.JobInfo{ 260 Name: "job1", 261 Namespace: "default", 262 Tasks: map[api.TaskID]*api.TaskInfo{ 263 "0": { 264 Name: "job1-ps-0", 265 }, 266 "1": { 267 Name: "job1-ps-1", 268 }, 269 "2": { 270 Name: "job1-worker-0", 271 }, 272 "3": { 273 Name: "job1-worker-1", 274 }, 275 }, 276 PodGroup: &api.PodGroup{ 277 PodGroup: scheduling.PodGroup{ 278 ObjectMeta: metav1.ObjectMeta{ 279 Annotations: map[string]string{ 280 JobAffinityAnnotations: "ps,worker", 281 JobAntiAffinityAnnotations: "ps,ps", 282 TaskOrderAnnotations: "ps,worker", 283 }, 284 }, 285 }, 286 }, 287 }, 288 topology: nil, 289 err: fmt.Errorf("task %s is duplicated in job <%s/%s>", "ps", "default", "job1"), 290 }, 291 { 292 description: "redundant punctuations", 293 job: &api.JobInfo{ 294 Name: "job1", 295 Namespace: "default", 296 Tasks: map[api.TaskID]*api.TaskInfo{ 297 "0": { 298 Name: "job1-ps-0", 299 }, 300 "1": { 301 Name: "job1-ps-1", 302 }, 303 "2": { 304 Name: "job1-worker-0", 305 }, 306 "3": { 307 Name: "job1-worker-1", 308 }, 309 "4": { 310 Name: "job1-chief-0", 311 }, 312 "5": { 313 Name: "job1-evaluator-0", 314 }, 315 }, 316 PodGroup: &api.PodGroup{ 317 PodGroup: scheduling.PodGroup{ 318 ObjectMeta: metav1.ObjectMeta{ 319 Annotations: map[string]string{ 320 JobAffinityAnnotations: "ps,worker;ps,,chief,;", 321 JobAntiAffinityAnnotations: "ps;worker,chief;;;", 322 TaskOrderAnnotations: "ps,worker,chief,evaluator,,,", 323 }, 324 }, 325 }, 326 }, 327 }, 328 topology: &TaskTopology{ 329 Affinity: [][]string{ 330 { 331 "ps", 332 "worker", 333 }, 334 { 335 "ps", 336 "", 337 "chief", 338 "", 339 }, 340 { 341 "", 342 }, 343 }, 344 AntiAffinity: [][]string{ 345 { 346 "ps", 347 }, 348 { 349 "worker", 350 "chief", 351 }, 352 { 353 "", 354 }, 355 { 356 "", 357 }, 358 { 359 "", 360 }, 361 }, 362 TaskOrder: []string{ 363 "ps", 364 "worker", 365 "chief", 366 "evaluator", 367 "", 368 "", 369 "", 370 }, 371 }, 372 err: nil, 373 }, 374 } 375 376 for i, c := range cases { 377 t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { 378 t.Logf("case: %s", c.description) 379 topology, err := readTopologyFromPgAnnotations(c.job) 380 if !reflect.DeepEqual(err, c.err) { 381 t.Errorf("want %v ,got %v", c.err, err) 382 } 383 if !reflect.DeepEqual(topology, c.topology) { 384 t.Errorf("want %v ,got %v", c.topology, topology) 385 } 386 }) 387 } 388 }