github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/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 } 207 alloc2 := &structs.Allocation{ 208 ID: structs.GenerateUUID(), 209 EvalID: structs.GenerateUUID(), 210 NodeID: nodes[1].Node.ID, 211 JobID: structs.GenerateUUID(), 212 Resources: &structs.Resources{ 213 CPU: 1024, 214 MemoryMB: 1024, 215 }, 216 DesiredStatus: structs.AllocDesiredStatusRun, 217 } 218 noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2})) 219 220 task := &structs.Task{ 221 Name: "web", 222 Resources: &structs.Resources{ 223 CPU: 1024, 224 MemoryMB: 1024, 225 }, 226 } 227 228 binp := NewBinPackIterator(ctx, static, false, 0) 229 binp.SetTasks([]*structs.Task{task}) 230 231 out := collectRanked(binp) 232 if len(out) != 1 { 233 t.Fatalf("Bad: %#v", out) 234 } 235 if out[0] != nodes[1] { 236 t.Fatalf("Bad: %v", out) 237 } 238 if out[0].Score != 18 { 239 t.Fatalf("Bad: %v", out[0]) 240 } 241 } 242 243 func TestBinPackIterator_ExistingAlloc_PlannedEvict(t *testing.T) { 244 state, ctx := testContext(t) 245 nodes := []*RankedNode{ 246 &RankedNode{ 247 Node: &structs.Node{ 248 // Perfect fit 249 ID: structs.GenerateUUID(), 250 Resources: &structs.Resources{ 251 CPU: 2048, 252 MemoryMB: 2048, 253 }, 254 }, 255 }, 256 &RankedNode{ 257 Node: &structs.Node{ 258 // Perfect fit 259 ID: structs.GenerateUUID(), 260 Resources: &structs.Resources{ 261 CPU: 2048, 262 MemoryMB: 2048, 263 }, 264 }, 265 }, 266 } 267 static := NewStaticRankIterator(ctx, nodes) 268 269 // Add existing allocations 270 alloc1 := &structs.Allocation{ 271 ID: structs.GenerateUUID(), 272 EvalID: structs.GenerateUUID(), 273 NodeID: nodes[0].Node.ID, 274 JobID: structs.GenerateUUID(), 275 Resources: &structs.Resources{ 276 CPU: 2048, 277 MemoryMB: 2048, 278 }, 279 DesiredStatus: structs.AllocDesiredStatusRun, 280 } 281 alloc2 := &structs.Allocation{ 282 ID: structs.GenerateUUID(), 283 EvalID: structs.GenerateUUID(), 284 NodeID: nodes[1].Node.ID, 285 JobID: structs.GenerateUUID(), 286 Resources: &structs.Resources{ 287 CPU: 1024, 288 MemoryMB: 1024, 289 }, 290 DesiredStatus: structs.AllocDesiredStatusRun, 291 } 292 noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2})) 293 294 // Add a planned eviction to alloc1 295 plan := ctx.Plan() 296 plan.NodeUpdate[nodes[0].Node.ID] = []*structs.Allocation{alloc1} 297 298 task := &structs.Task{ 299 Name: "web", 300 Resources: &structs.Resources{ 301 CPU: 1024, 302 MemoryMB: 1024, 303 }, 304 } 305 306 binp := NewBinPackIterator(ctx, static, false, 0) 307 binp.SetTasks([]*structs.Task{task}) 308 309 out := collectRanked(binp) 310 if len(out) != 2 { 311 t.Fatalf("Bad: %#v", out) 312 } 313 if out[0] != nodes[0] || out[1] != nodes[1] { 314 t.Fatalf("Bad: %v", out) 315 } 316 if out[0].Score < 10 || out[0].Score > 16 { 317 t.Fatalf("Bad: %v", out[0]) 318 } 319 if out[1].Score != 18 { 320 t.Fatalf("Bad: %v", out[0]) 321 } 322 } 323 324 func TestJobAntiAffinity_PlannedAlloc(t *testing.T) { 325 _, ctx := testContext(t) 326 nodes := []*RankedNode{ 327 &RankedNode{ 328 Node: &structs.Node{ 329 ID: structs.GenerateUUID(), 330 }, 331 }, 332 &RankedNode{ 333 Node: &structs.Node{ 334 ID: structs.GenerateUUID(), 335 }, 336 }, 337 } 338 static := NewStaticRankIterator(ctx, nodes) 339 340 // Add a planned alloc to node1 that fills it 341 plan := ctx.Plan() 342 plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{ 343 &structs.Allocation{ 344 JobID: "foo", 345 }, 346 &structs.Allocation{ 347 JobID: "foo", 348 }, 349 } 350 351 // Add a planned alloc to node2 that half fills it 352 plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{ 353 &structs.Allocation{ 354 JobID: "bar", 355 }, 356 } 357 358 binp := NewJobAntiAffinityIterator(ctx, static, 5.0, "foo") 359 360 out := collectRanked(binp) 361 if len(out) != 2 { 362 t.Fatalf("Bad: %#v", out) 363 } 364 if out[0] != nodes[0] { 365 t.Fatalf("Bad: %v", out) 366 } 367 if out[0].Score != -10.0 { 368 t.Fatalf("Bad: %v", out[0]) 369 } 370 371 if out[1] != nodes[1] { 372 t.Fatalf("Bad: %v", out) 373 } 374 if out[1].Score != 0.0 { 375 t.Fatalf("Bad: %v", out[1]) 376 } 377 } 378 379 func collectRanked(iter RankIterator) (out []*RankedNode) { 380 for { 381 next := iter.Next() 382 if next == nil { 383 break 384 } 385 out = append(out, next) 386 } 387 return 388 }