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