github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/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 task := &structs.Task{ 72 Name: "web", 73 Resources: &structs.Resources{ 74 CPU: 1024, 75 MemoryMB: 1024, 76 }, 77 } 78 79 binp := NewBinPackIterator(ctx, static, false, 0) 80 binp.SetTasks([]*structs.Task{task}) 81 82 out := collectRanked(binp) 83 if len(out) != 2 { 84 t.Fatalf("Bad: %v", out) 85 } 86 if out[0] != nodes[0] || out[1] != nodes[2] { 87 t.Fatalf("Bad: %v", out) 88 } 89 90 if out[0].Score != 18 { 91 t.Fatalf("Bad: %v", out[0]) 92 } 93 if out[1].Score < 10 || out[1].Score > 16 { 94 t.Fatalf("Bad: %v", out[1]) 95 } 96 } 97 98 func TestBinPackIterator_PlannedAlloc(t *testing.T) { 99 _, ctx := testContext(t) 100 nodes := []*RankedNode{ 101 &RankedNode{ 102 Node: &structs.Node{ 103 // Perfect fit 104 ID: structs.GenerateUUID(), 105 Resources: &structs.Resources{ 106 CPU: 2048, 107 MemoryMB: 2048, 108 }, 109 }, 110 }, 111 &RankedNode{ 112 Node: &structs.Node{ 113 // Perfect fit 114 ID: structs.GenerateUUID(), 115 Resources: &structs.Resources{ 116 CPU: 2048, 117 MemoryMB: 2048, 118 }, 119 }, 120 }, 121 } 122 static := NewStaticRankIterator(ctx, nodes) 123 124 // Add a planned alloc to node1 that fills it 125 plan := ctx.Plan() 126 plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{ 127 &structs.Allocation{ 128 Resources: &structs.Resources{ 129 CPU: 2048, 130 MemoryMB: 2048, 131 }, 132 }, 133 } 134 135 // Add a planned alloc to node2 that half fills it 136 plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{ 137 &structs.Allocation{ 138 Resources: &structs.Resources{ 139 CPU: 1024, 140 MemoryMB: 1024, 141 }, 142 }, 143 } 144 145 task := &structs.Task{ 146 Name: "web", 147 Resources: &structs.Resources{ 148 CPU: 1024, 149 MemoryMB: 1024, 150 }, 151 } 152 153 binp := NewBinPackIterator(ctx, static, false, 0) 154 binp.SetTasks([]*structs.Task{task}) 155 156 out := collectRanked(binp) 157 if len(out) != 1 { 158 t.Fatalf("Bad: %#v", out) 159 } 160 if out[0] != nodes[1] { 161 t.Fatalf("Bad: %v", out) 162 } 163 164 if out[0].Score != 18 { 165 t.Fatalf("Bad: %v", out[0]) 166 } 167 } 168 169 func TestBinPackIterator_ExistingAlloc(t *testing.T) { 170 state, ctx := testContext(t) 171 nodes := []*RankedNode{ 172 &RankedNode{ 173 Node: &structs.Node{ 174 // Perfect fit 175 ID: structs.GenerateUUID(), 176 Resources: &structs.Resources{ 177 CPU: 2048, 178 MemoryMB: 2048, 179 }, 180 }, 181 }, 182 &RankedNode{ 183 Node: &structs.Node{ 184 // Perfect fit 185 ID: structs.GenerateUUID(), 186 Resources: &structs.Resources{ 187 CPU: 2048, 188 MemoryMB: 2048, 189 }, 190 }, 191 }, 192 } 193 static := NewStaticRankIterator(ctx, nodes) 194 195 // Add existing allocations 196 alloc1 := &structs.Allocation{ 197 ID: structs.GenerateUUID(), 198 EvalID: structs.GenerateUUID(), 199 NodeID: nodes[0].Node.ID, 200 JobID: structs.GenerateUUID(), 201 Resources: &structs.Resources{ 202 CPU: 2048, 203 MemoryMB: 2048, 204 }, 205 DesiredStatus: structs.AllocDesiredStatusRun, 206 ClientStatus: structs.AllocClientStatusPending, 207 } 208 alloc2 := &structs.Allocation{ 209 ID: structs.GenerateUUID(), 210 EvalID: structs.GenerateUUID(), 211 NodeID: nodes[1].Node.ID, 212 JobID: structs.GenerateUUID(), 213 Resources: &structs.Resources{ 214 CPU: 1024, 215 MemoryMB: 1024, 216 }, 217 DesiredStatus: structs.AllocDesiredStatusRun, 218 ClientStatus: structs.AllocClientStatusPending, 219 } 220 noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2})) 221 222 task := &structs.Task{ 223 Name: "web", 224 Resources: &structs.Resources{ 225 CPU: 1024, 226 MemoryMB: 1024, 227 }, 228 } 229 230 binp := NewBinPackIterator(ctx, static, false, 0) 231 binp.SetTasks([]*structs.Task{task}) 232 233 out := collectRanked(binp) 234 if len(out) != 1 { 235 t.Fatalf("Bad: %#v", out) 236 } 237 if out[0] != nodes[1] { 238 t.Fatalf("Bad: %v", out) 239 } 240 if out[0].Score != 18 { 241 t.Fatalf("Bad: %v", out[0]) 242 } 243 } 244 245 func TestBinPackIterator_ExistingAlloc_PlannedEvict(t *testing.T) { 246 state, ctx := testContext(t) 247 nodes := []*RankedNode{ 248 &RankedNode{ 249 Node: &structs.Node{ 250 // Perfect fit 251 ID: structs.GenerateUUID(), 252 Resources: &structs.Resources{ 253 CPU: 2048, 254 MemoryMB: 2048, 255 }, 256 }, 257 }, 258 &RankedNode{ 259 Node: &structs.Node{ 260 // Perfect fit 261 ID: structs.GenerateUUID(), 262 Resources: &structs.Resources{ 263 CPU: 2048, 264 MemoryMB: 2048, 265 }, 266 }, 267 }, 268 } 269 static := NewStaticRankIterator(ctx, nodes) 270 271 // Add existing allocations 272 alloc1 := &structs.Allocation{ 273 ID: structs.GenerateUUID(), 274 EvalID: structs.GenerateUUID(), 275 NodeID: nodes[0].Node.ID, 276 JobID: structs.GenerateUUID(), 277 Resources: &structs.Resources{ 278 CPU: 2048, 279 MemoryMB: 2048, 280 }, 281 DesiredStatus: structs.AllocDesiredStatusRun, 282 ClientStatus: structs.AllocClientStatusPending, 283 } 284 alloc2 := &structs.Allocation{ 285 ID: structs.GenerateUUID(), 286 EvalID: structs.GenerateUUID(), 287 NodeID: nodes[1].Node.ID, 288 JobID: structs.GenerateUUID(), 289 Resources: &structs.Resources{ 290 CPU: 1024, 291 MemoryMB: 1024, 292 }, 293 DesiredStatus: structs.AllocDesiredStatusRun, 294 ClientStatus: structs.AllocClientStatusPending, 295 } 296 noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2})) 297 298 // Add a planned eviction to alloc1 299 plan := ctx.Plan() 300 plan.NodeUpdate[nodes[0].Node.ID] = []*structs.Allocation{alloc1} 301 302 task := &structs.Task{ 303 Name: "web", 304 Resources: &structs.Resources{ 305 CPU: 1024, 306 MemoryMB: 1024, 307 }, 308 } 309 310 binp := NewBinPackIterator(ctx, static, false, 0) 311 binp.SetTasks([]*structs.Task{task}) 312 313 out := collectRanked(binp) 314 if len(out) != 2 { 315 t.Fatalf("Bad: %#v", out) 316 } 317 if out[0] != nodes[0] || out[1] != nodes[1] { 318 t.Fatalf("Bad: %v", out) 319 } 320 if out[0].Score < 10 || out[0].Score > 16 { 321 t.Fatalf("Bad: %v", out[0]) 322 } 323 if out[1].Score != 18 { 324 t.Fatalf("Bad: %v", out[1]) 325 } 326 } 327 328 func TestJobAntiAffinity_PlannedAlloc(t *testing.T) { 329 _, ctx := testContext(t) 330 nodes := []*RankedNode{ 331 &RankedNode{ 332 Node: &structs.Node{ 333 ID: structs.GenerateUUID(), 334 }, 335 }, 336 &RankedNode{ 337 Node: &structs.Node{ 338 ID: structs.GenerateUUID(), 339 }, 340 }, 341 } 342 static := NewStaticRankIterator(ctx, nodes) 343 344 // Add a planned alloc to node1 that fills it 345 plan := ctx.Plan() 346 plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{ 347 &structs.Allocation{ 348 ID: structs.GenerateUUID(), 349 JobID: "foo", 350 }, 351 &structs.Allocation{ 352 ID: structs.GenerateUUID(), 353 JobID: "foo", 354 }, 355 } 356 357 // Add a planned alloc to node2 that half fills it 358 plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{ 359 &structs.Allocation{ 360 JobID: "bar", 361 }, 362 } 363 364 binp := NewJobAntiAffinityIterator(ctx, static, 5.0, "foo") 365 366 out := collectRanked(binp) 367 if len(out) != 2 { 368 t.Fatalf("Bad: %#v", out) 369 } 370 if out[0] != nodes[0] { 371 t.Fatalf("Bad: %v", out) 372 } 373 if out[0].Score != -10.0 { 374 t.Fatalf("Bad: %#v", out[0]) 375 } 376 377 if out[1] != nodes[1] { 378 t.Fatalf("Bad: %v", out) 379 } 380 if out[1].Score != 0.0 { 381 t.Fatalf("Bad: %v", out[1]) 382 } 383 } 384 385 func collectRanked(iter RankIterator) (out []*RankedNode) { 386 for { 387 next := iter.Next() 388 if next == nil { 389 break 390 } 391 out = append(out, next) 392 } 393 return 394 }