github.com/anuvu/nomad@v0.8.7-atom1/scheduler/rank_test.go (about) 1 package scheduler 2 3 import ( 4 "testing" 5 6 "github.com/hashicorp/nomad/helper/uuid" 7 "github.com/hashicorp/nomad/nomad/mock" 8 "github.com/hashicorp/nomad/nomad/structs" 9 require "github.com/stretchr/testify/require" 10 ) 11 12 func TestFeasibleRankIterator(t *testing.T) { 13 _, ctx := testContext(t) 14 var nodes []*structs.Node 15 for i := 0; i < 10; i++ { 16 nodes = append(nodes, mock.Node()) 17 } 18 static := NewStaticIterator(ctx, nodes) 19 20 feasible := NewFeasibleRankIterator(ctx, static) 21 22 out := collectRanked(feasible) 23 if len(out) != len(nodes) { 24 t.Fatalf("bad: %v", out) 25 } 26 } 27 28 func TestBinPackIterator_NoExistingAlloc(t *testing.T) { 29 _, ctx := testContext(t) 30 nodes := []*RankedNode{ 31 { 32 Node: &structs.Node{ 33 // Perfect fit 34 Resources: &structs.Resources{ 35 CPU: 2048, 36 MemoryMB: 2048, 37 }, 38 Reserved: &structs.Resources{ 39 CPU: 1024, 40 MemoryMB: 1024, 41 }, 42 }, 43 }, 44 { 45 Node: &structs.Node{ 46 // Overloaded 47 Resources: &structs.Resources{ 48 CPU: 1024, 49 MemoryMB: 1024, 50 }, 51 Reserved: &structs.Resources{ 52 CPU: 512, 53 MemoryMB: 512, 54 }, 55 }, 56 }, 57 { 58 Node: &structs.Node{ 59 // 50% fit 60 Resources: &structs.Resources{ 61 CPU: 4096, 62 MemoryMB: 4096, 63 }, 64 Reserved: &structs.Resources{ 65 CPU: 1024, 66 MemoryMB: 1024, 67 }, 68 }, 69 }, 70 } 71 static := NewStaticRankIterator(ctx, nodes) 72 73 taskGroup := &structs.TaskGroup{ 74 EphemeralDisk: &structs.EphemeralDisk{}, 75 Tasks: []*structs.Task{ 76 { 77 Name: "web", 78 Resources: &structs.Resources{ 79 CPU: 1024, 80 MemoryMB: 1024, 81 }, 82 }, 83 }, 84 } 85 binp := NewBinPackIterator(ctx, static, false, 0) 86 binp.SetTaskGroup(taskGroup) 87 88 out := collectRanked(binp) 89 if len(out) != 2 { 90 t.Fatalf("Bad: %v", out) 91 } 92 if out[0] != nodes[0] || out[1] != nodes[2] { 93 t.Fatalf("Bad: %v", out) 94 } 95 96 if out[0].Score != 18 { 97 t.Fatalf("Bad: %v", out[0]) 98 } 99 if out[1].Score < 10 || out[1].Score > 16 { 100 t.Fatalf("Bad: %v", out[1]) 101 } 102 } 103 104 func TestBinPackIterator_PlannedAlloc(t *testing.T) { 105 _, ctx := testContext(t) 106 nodes := []*RankedNode{ 107 { 108 Node: &structs.Node{ 109 // Perfect fit 110 ID: uuid.Generate(), 111 Resources: &structs.Resources{ 112 CPU: 2048, 113 MemoryMB: 2048, 114 }, 115 }, 116 }, 117 { 118 Node: &structs.Node{ 119 // Perfect fit 120 ID: uuid.Generate(), 121 Resources: &structs.Resources{ 122 CPU: 2048, 123 MemoryMB: 2048, 124 }, 125 }, 126 }, 127 } 128 static := NewStaticRankIterator(ctx, nodes) 129 130 // Add a planned alloc to node1 that fills it 131 plan := ctx.Plan() 132 plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{ 133 { 134 Resources: &structs.Resources{ 135 CPU: 2048, 136 MemoryMB: 2048, 137 }, 138 }, 139 } 140 141 // Add a planned alloc to node2 that half fills it 142 plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{ 143 { 144 Resources: &structs.Resources{ 145 CPU: 1024, 146 MemoryMB: 1024, 147 }, 148 }, 149 } 150 151 taskGroup := &structs.TaskGroup{ 152 EphemeralDisk: &structs.EphemeralDisk{}, 153 Tasks: []*structs.Task{ 154 { 155 Name: "web", 156 Resources: &structs.Resources{ 157 CPU: 1024, 158 MemoryMB: 1024, 159 }, 160 }, 161 }, 162 } 163 164 binp := NewBinPackIterator(ctx, static, false, 0) 165 binp.SetTaskGroup(taskGroup) 166 167 out := collectRanked(binp) 168 if len(out) != 1 { 169 t.Fatalf("Bad: %#v", out) 170 } 171 if out[0] != nodes[1] { 172 t.Fatalf("Bad: %v", out) 173 } 174 175 if out[0].Score != 18 { 176 t.Fatalf("Bad: %v", out[0]) 177 } 178 } 179 180 func TestBinPackIterator_ExistingAlloc(t *testing.T) { 181 state, ctx := testContext(t) 182 nodes := []*RankedNode{ 183 { 184 Node: &structs.Node{ 185 // Perfect fit 186 ID: uuid.Generate(), 187 Resources: &structs.Resources{ 188 CPU: 2048, 189 MemoryMB: 2048, 190 }, 191 }, 192 }, 193 { 194 Node: &structs.Node{ 195 // Perfect fit 196 ID: uuid.Generate(), 197 Resources: &structs.Resources{ 198 CPU: 2048, 199 MemoryMB: 2048, 200 }, 201 }, 202 }, 203 } 204 static := NewStaticRankIterator(ctx, nodes) 205 206 // Add existing allocations 207 j1, j2 := mock.Job(), mock.Job() 208 alloc1 := &structs.Allocation{ 209 Namespace: structs.DefaultNamespace, 210 ID: uuid.Generate(), 211 EvalID: uuid.Generate(), 212 NodeID: nodes[0].Node.ID, 213 JobID: j1.ID, 214 Job: j1, 215 Resources: &structs.Resources{ 216 CPU: 2048, 217 MemoryMB: 2048, 218 }, 219 DesiredStatus: structs.AllocDesiredStatusRun, 220 ClientStatus: structs.AllocClientStatusPending, 221 TaskGroup: "web", 222 } 223 alloc2 := &structs.Allocation{ 224 Namespace: structs.DefaultNamespace, 225 ID: uuid.Generate(), 226 EvalID: uuid.Generate(), 227 NodeID: nodes[1].Node.ID, 228 JobID: j2.ID, 229 Job: j2, 230 Resources: &structs.Resources{ 231 CPU: 1024, 232 MemoryMB: 1024, 233 }, 234 DesiredStatus: structs.AllocDesiredStatusRun, 235 ClientStatus: structs.AllocClientStatusPending, 236 TaskGroup: "web", 237 } 238 noErr(t, state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID))) 239 noErr(t, state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))) 240 noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2})) 241 242 taskGroup := &structs.TaskGroup{ 243 EphemeralDisk: &structs.EphemeralDisk{}, 244 Tasks: []*structs.Task{ 245 { 246 Name: "web", 247 Resources: &structs.Resources{ 248 CPU: 1024, 249 MemoryMB: 1024, 250 }, 251 }, 252 }, 253 } 254 binp := NewBinPackIterator(ctx, static, false, 0) 255 binp.SetTaskGroup(taskGroup) 256 257 out := collectRanked(binp) 258 if len(out) != 1 { 259 t.Fatalf("Bad: %#v", out) 260 } 261 if out[0] != nodes[1] { 262 t.Fatalf("Bad: %v", out) 263 } 264 if out[0].Score != 18 { 265 t.Fatalf("Bad: %v", out[0]) 266 } 267 } 268 269 func TestBinPackIterator_ExistingAlloc_PlannedEvict(t *testing.T) { 270 state, ctx := testContext(t) 271 nodes := []*RankedNode{ 272 { 273 Node: &structs.Node{ 274 // Perfect fit 275 ID: uuid.Generate(), 276 Resources: &structs.Resources{ 277 CPU: 2048, 278 MemoryMB: 2048, 279 }, 280 }, 281 }, 282 { 283 Node: &structs.Node{ 284 // Perfect fit 285 ID: uuid.Generate(), 286 Resources: &structs.Resources{ 287 CPU: 2048, 288 MemoryMB: 2048, 289 }, 290 }, 291 }, 292 } 293 static := NewStaticRankIterator(ctx, nodes) 294 295 // Add existing allocations 296 j1, j2 := mock.Job(), mock.Job() 297 alloc1 := &structs.Allocation{ 298 Namespace: structs.DefaultNamespace, 299 ID: uuid.Generate(), 300 EvalID: uuid.Generate(), 301 NodeID: nodes[0].Node.ID, 302 JobID: j1.ID, 303 Job: j1, 304 Resources: &structs.Resources{ 305 CPU: 2048, 306 MemoryMB: 2048, 307 }, 308 DesiredStatus: structs.AllocDesiredStatusRun, 309 ClientStatus: structs.AllocClientStatusPending, 310 TaskGroup: "web", 311 } 312 alloc2 := &structs.Allocation{ 313 Namespace: structs.DefaultNamespace, 314 ID: uuid.Generate(), 315 EvalID: uuid.Generate(), 316 NodeID: nodes[1].Node.ID, 317 JobID: j2.ID, 318 Job: j2, 319 Resources: &structs.Resources{ 320 CPU: 1024, 321 MemoryMB: 1024, 322 }, 323 DesiredStatus: structs.AllocDesiredStatusRun, 324 ClientStatus: structs.AllocClientStatusPending, 325 TaskGroup: "web", 326 } 327 noErr(t, state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID))) 328 noErr(t, state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))) 329 noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2})) 330 331 // Add a planned eviction to alloc1 332 plan := ctx.Plan() 333 plan.NodeUpdate[nodes[0].Node.ID] = []*structs.Allocation{alloc1} 334 335 taskGroup := &structs.TaskGroup{ 336 EphemeralDisk: &structs.EphemeralDisk{}, 337 Tasks: []*structs.Task{ 338 { 339 Name: "web", 340 Resources: &structs.Resources{ 341 CPU: 1024, 342 MemoryMB: 1024, 343 }, 344 }, 345 }, 346 } 347 348 binp := NewBinPackIterator(ctx, static, false, 0) 349 binp.SetTaskGroup(taskGroup) 350 351 out := collectRanked(binp) 352 if len(out) != 2 { 353 t.Fatalf("Bad: %#v", out) 354 } 355 if out[0] != nodes[0] || out[1] != nodes[1] { 356 t.Fatalf("Bad: %v", out) 357 } 358 if out[0].Score < 10 || out[0].Score > 16 { 359 t.Fatalf("Bad: %v", out[0]) 360 } 361 if out[1].Score != 18 { 362 t.Fatalf("Bad: %v", out[1]) 363 } 364 } 365 366 func TestJobAntiAffinity_PlannedAlloc(t *testing.T) { 367 _, ctx := testContext(t) 368 nodes := []*RankedNode{ 369 { 370 Node: &structs.Node{ 371 ID: uuid.Generate(), 372 }, 373 }, 374 { 375 Node: &structs.Node{ 376 ID: uuid.Generate(), 377 }, 378 }, 379 } 380 static := NewStaticRankIterator(ctx, nodes) 381 382 // Add a planned alloc to node1 that fills it 383 plan := ctx.Plan() 384 plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{ 385 { 386 ID: uuid.Generate(), 387 JobID: "foo", 388 }, 389 { 390 ID: uuid.Generate(), 391 JobID: "foo", 392 }, 393 } 394 395 // Add a planned alloc to node2 that half fills it 396 plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{ 397 { 398 JobID: "bar", 399 }, 400 } 401 402 binp := NewJobAntiAffinityIterator(ctx, static, 5.0, "foo") 403 404 out := collectRanked(binp) 405 if len(out) != 2 { 406 t.Fatalf("Bad: %#v", out) 407 } 408 if out[0] != nodes[0] { 409 t.Fatalf("Bad: %v", out) 410 } 411 if out[0].Score != -10.0 { 412 t.Fatalf("Bad: %#v", out[0]) 413 } 414 415 if out[1] != nodes[1] { 416 t.Fatalf("Bad: %v", out) 417 } 418 if out[1].Score != 0.0 { 419 t.Fatalf("Bad: %v", out[1]) 420 } 421 } 422 423 func collectRanked(iter RankIterator) (out []*RankedNode) { 424 for { 425 next := iter.Next() 426 if next == nil { 427 break 428 } 429 out = append(out, next) 430 } 431 return 432 } 433 434 func TestNodeAntiAffinity_PenaltyNodes(t *testing.T) { 435 _, ctx := testContext(t) 436 node1 := &structs.Node{ 437 ID: uuid.Generate(), 438 } 439 node2 := &structs.Node{ 440 ID: uuid.Generate(), 441 } 442 443 nodes := []*RankedNode{ 444 { 445 Node: node1, 446 }, 447 { 448 Node: node2, 449 }, 450 } 451 static := NewStaticRankIterator(ctx, nodes) 452 453 nodeAntiAffIter := NewNodeAntiAffinityIterator(ctx, static, 50.0) 454 nodeAntiAffIter.SetPenaltyNodes(map[string]struct{}{node1.ID: {}}) 455 456 out := collectRanked(nodeAntiAffIter) 457 458 require := require.New(t) 459 require.Equal(2, len(out)) 460 require.Equal(node1.ID, out[0].Node.ID) 461 require.Equal(-50.0, out[0].Score) 462 463 require.Equal(node2.ID, out[1].Node.ID) 464 require.Equal(0.0, out[1].Score) 465 466 }