github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/nomad/plan_apply_test.go (about) 1 package nomad 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/hashicorp/nomad/nomad/mock" 8 "github.com/hashicorp/nomad/nomad/structs" 9 "github.com/hashicorp/nomad/testutil" 10 "github.com/hashicorp/raft" 11 ) 12 13 const ( 14 // workerPoolSize is the size of the worker pool 15 workerPoolSize = 2 16 ) 17 18 // planWaitFuture is used to wait for the Raft future to complete 19 func planWaitFuture(future raft.ApplyFuture) (uint64, error) { 20 if err := future.Error(); err != nil { 21 return 0, err 22 } 23 return future.Index(), nil 24 } 25 26 func testRegisterNode(t *testing.T, s *Server, n *structs.Node) { 27 // Create the register request 28 req := &structs.NodeRegisterRequest{ 29 Node: n, 30 WriteRequest: structs.WriteRequest{Region: "global"}, 31 } 32 33 // Fetch the response 34 var resp structs.NodeUpdateResponse 35 if err := s.RPC("Node.Register", req, &resp); err != nil { 36 t.Fatalf("err: %v", err) 37 } 38 if resp.Index == 0 { 39 t.Fatalf("bad index: %d", resp.Index) 40 } 41 } 42 43 func TestPlanApply_applyPlan(t *testing.T) { 44 s1 := testServer(t, nil) 45 defer s1.Shutdown() 46 testutil.WaitForLeader(t, s1.RPC) 47 48 // Register ndoe 49 node := mock.Node() 50 testRegisterNode(t, s1, node) 51 52 // Register alloc 53 alloc := mock.Alloc() 54 plan := &structs.PlanResult{ 55 NodeAllocation: map[string][]*structs.Allocation{ 56 node.ID: []*structs.Allocation{alloc}, 57 }, 58 } 59 60 // Snapshot the state 61 snap, err := s1.State().Snapshot() 62 if err != nil { 63 t.Fatalf("err: %v", err) 64 } 65 66 // Apply the plan 67 future, err := s1.applyPlan(alloc.Job, plan, snap) 68 if err != nil { 69 t.Fatalf("err: %v", err) 70 } 71 72 // Verify our optimistic snapshot is updated 73 if out, err := snap.AllocByID(alloc.ID); err != nil || out == nil { 74 t.Fatalf("bad: %v %v", out, err) 75 } 76 77 // Check plan does apply cleanly 78 index, err := planWaitFuture(future) 79 if err != nil { 80 t.Fatalf("err: %v", err) 81 } 82 if index == 0 { 83 t.Fatalf("bad: %d", index) 84 } 85 86 // Lookup the allocation 87 out, err := s1.fsm.State().AllocByID(alloc.ID) 88 if err != nil { 89 t.Fatalf("err: %v", err) 90 } 91 if out == nil { 92 t.Fatalf("missing alloc") 93 } 94 95 // Evict alloc, Register alloc2 96 allocEvict := new(structs.Allocation) 97 *allocEvict = *alloc 98 allocEvict.DesiredStatus = structs.AllocDesiredStatusEvict 99 job := allocEvict.Job 100 allocEvict.Job = nil 101 alloc2 := mock.Alloc() 102 alloc2.Job = nil 103 plan = &structs.PlanResult{ 104 NodeUpdate: map[string][]*structs.Allocation{ 105 node.ID: []*structs.Allocation{allocEvict}, 106 }, 107 NodeAllocation: map[string][]*structs.Allocation{ 108 node.ID: []*structs.Allocation{alloc2}, 109 }, 110 } 111 112 // Snapshot the state 113 snap, err = s1.State().Snapshot() 114 if err != nil { 115 t.Fatalf("err: %v", err) 116 } 117 118 // Apply the plan 119 future, err = s1.applyPlan(job, plan, snap) 120 if err != nil { 121 t.Fatalf("err: %v", err) 122 } 123 124 // Check that our optimistic view is updated 125 if out, _ := snap.AllocByID(allocEvict.ID); out.DesiredStatus != structs.AllocDesiredStatusEvict { 126 t.Fatalf("bad: %#v", out) 127 } 128 129 // Verify plan applies cleanly 130 index, err = planWaitFuture(future) 131 if err != nil { 132 t.Fatalf("err: %v", err) 133 } 134 if index == 0 { 135 t.Fatalf("bad: %d", index) 136 } 137 138 // Lookup the allocation 139 out, err = s1.fsm.State().AllocByID(alloc.ID) 140 if err != nil { 141 t.Fatalf("err: %v", err) 142 } 143 if out.DesiredStatus != structs.AllocDesiredStatusEvict { 144 t.Fatalf("should be evicted alloc: %#v", out) 145 } 146 if out.Job == nil { 147 t.Fatalf("missing job") 148 } 149 150 // Lookup the allocation 151 out, err = s1.fsm.State().AllocByID(alloc2.ID) 152 if err != nil { 153 t.Fatalf("err: %v", err) 154 } 155 if out == nil { 156 t.Fatalf("missing alloc") 157 } 158 if out.Job == nil { 159 t.Fatalf("missing job") 160 } 161 } 162 163 func TestPlanApply_EvalPlan_Simple(t *testing.T) { 164 state := testStateStore(t) 165 node := mock.Node() 166 state.UpsertNode(1000, node) 167 snap, _ := state.Snapshot() 168 169 alloc := mock.Alloc() 170 plan := &structs.Plan{ 171 NodeAllocation: map[string][]*structs.Allocation{ 172 node.ID: []*structs.Allocation{alloc}, 173 }, 174 } 175 176 pool := NewEvaluatePool(workerPoolSize, workerPoolBufferSize) 177 defer pool.Shutdown() 178 179 result, err := evaluatePlan(pool, snap, plan) 180 if err != nil { 181 t.Fatalf("err: %v", err) 182 } 183 if result == nil { 184 t.Fatalf("missing result") 185 } 186 if !reflect.DeepEqual(result.NodeAllocation, plan.NodeAllocation) { 187 t.Fatalf("incorrect node allocations") 188 } 189 } 190 191 func TestPlanApply_EvalPlan_Partial(t *testing.T) { 192 state := testStateStore(t) 193 node := mock.Node() 194 state.UpsertNode(1000, node) 195 node2 := mock.Node() 196 state.UpsertNode(1001, node2) 197 snap, _ := state.Snapshot() 198 199 alloc := mock.Alloc() 200 alloc2 := mock.Alloc() // Ensure alloc2 does not fit 201 alloc2.Resources = node2.Resources 202 plan := &structs.Plan{ 203 NodeAllocation: map[string][]*structs.Allocation{ 204 node.ID: []*structs.Allocation{alloc}, 205 node2.ID: []*structs.Allocation{alloc2}, 206 }, 207 } 208 209 pool := NewEvaluatePool(workerPoolSize, workerPoolBufferSize) 210 defer pool.Shutdown() 211 212 result, err := evaluatePlan(pool, snap, plan) 213 if err != nil { 214 t.Fatalf("err: %v", err) 215 } 216 if result == nil { 217 t.Fatalf("missing result") 218 } 219 220 if _, ok := result.NodeAllocation[node.ID]; !ok { 221 t.Fatalf("should allow alloc") 222 } 223 if _, ok := result.NodeAllocation[node2.ID]; ok { 224 t.Fatalf("should not allow alloc2") 225 } 226 if result.RefreshIndex != 1001 { 227 t.Fatalf("bad: %d", result.RefreshIndex) 228 } 229 } 230 231 func TestPlanApply_EvalPlan_Partial_AllAtOnce(t *testing.T) { 232 state := testStateStore(t) 233 node := mock.Node() 234 state.UpsertNode(1000, node) 235 node2 := mock.Node() 236 state.UpsertNode(1001, node2) 237 snap, _ := state.Snapshot() 238 239 alloc := mock.Alloc() 240 alloc2 := mock.Alloc() // Ensure alloc2 does not fit 241 alloc2.Resources = node2.Resources 242 plan := &structs.Plan{ 243 AllAtOnce: true, // Require all to make progress 244 NodeAllocation: map[string][]*structs.Allocation{ 245 node.ID: []*structs.Allocation{alloc}, 246 node2.ID: []*structs.Allocation{alloc2}, 247 }, 248 } 249 250 pool := NewEvaluatePool(workerPoolSize, workerPoolBufferSize) 251 defer pool.Shutdown() 252 253 result, err := evaluatePlan(pool, snap, plan) 254 if err != nil { 255 t.Fatalf("err: %v", err) 256 } 257 if result == nil { 258 t.Fatalf("missing result") 259 } 260 261 if len(result.NodeAllocation) != 0 { 262 t.Fatalf("should not alloc: %v", result.NodeAllocation) 263 } 264 if result.RefreshIndex != 1001 { 265 t.Fatalf("bad: %d", result.RefreshIndex) 266 } 267 } 268 269 func TestPlanApply_EvalNodePlan_Simple(t *testing.T) { 270 state := testStateStore(t) 271 node := mock.Node() 272 state.UpsertNode(1000, node) 273 snap, _ := state.Snapshot() 274 275 alloc := mock.Alloc() 276 plan := &structs.Plan{ 277 NodeAllocation: map[string][]*structs.Allocation{ 278 node.ID: []*structs.Allocation{alloc}, 279 }, 280 } 281 282 fit, err := evaluateNodePlan(snap, plan, node.ID) 283 if err != nil { 284 t.Fatalf("err: %v", err) 285 } 286 if !fit { 287 t.Fatalf("bad") 288 } 289 } 290 291 func TestPlanApply_EvalNodePlan_NodeNotReady(t *testing.T) { 292 state := testStateStore(t) 293 node := mock.Node() 294 node.Status = structs.NodeStatusInit 295 state.UpsertNode(1000, node) 296 snap, _ := state.Snapshot() 297 298 alloc := mock.Alloc() 299 plan := &structs.Plan{ 300 NodeAllocation: map[string][]*structs.Allocation{ 301 node.ID: []*structs.Allocation{alloc}, 302 }, 303 } 304 305 fit, err := evaluateNodePlan(snap, plan, node.ID) 306 if err != nil { 307 t.Fatalf("err: %v", err) 308 } 309 if fit { 310 t.Fatalf("bad") 311 } 312 } 313 314 func TestPlanApply_EvalNodePlan_NodeDrain(t *testing.T) { 315 state := testStateStore(t) 316 node := mock.Node() 317 node.Drain = true 318 state.UpsertNode(1000, node) 319 snap, _ := state.Snapshot() 320 321 alloc := mock.Alloc() 322 plan := &structs.Plan{ 323 NodeAllocation: map[string][]*structs.Allocation{ 324 node.ID: []*structs.Allocation{alloc}, 325 }, 326 } 327 328 fit, err := evaluateNodePlan(snap, plan, node.ID) 329 if err != nil { 330 t.Fatalf("err: %v", err) 331 } 332 if fit { 333 t.Fatalf("bad") 334 } 335 } 336 337 func TestPlanApply_EvalNodePlan_NodeNotExist(t *testing.T) { 338 state := testStateStore(t) 339 snap, _ := state.Snapshot() 340 341 nodeID := "12345678-abcd-efab-cdef-123456789abc" 342 alloc := mock.Alloc() 343 plan := &structs.Plan{ 344 NodeAllocation: map[string][]*structs.Allocation{ 345 nodeID: []*structs.Allocation{alloc}, 346 }, 347 } 348 349 fit, err := evaluateNodePlan(snap, plan, nodeID) 350 if err != nil { 351 t.Fatalf("err: %v", err) 352 } 353 if fit { 354 t.Fatalf("bad") 355 } 356 } 357 358 func TestPlanApply_EvalNodePlan_NodeFull(t *testing.T) { 359 alloc := mock.Alloc() 360 state := testStateStore(t) 361 node := mock.Node() 362 alloc.NodeID = node.ID 363 node.Resources = alloc.Resources 364 node.Reserved = nil 365 state.UpsertNode(1000, node) 366 state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 367 snap, _ := state.Snapshot() 368 369 alloc2 := mock.Alloc() 370 alloc2.NodeID = node.ID 371 plan := &structs.Plan{ 372 NodeAllocation: map[string][]*structs.Allocation{ 373 node.ID: []*structs.Allocation{alloc2}, 374 }, 375 } 376 377 fit, err := evaluateNodePlan(snap, plan, node.ID) 378 if err != nil { 379 t.Fatalf("err: %v", err) 380 } 381 if fit { 382 t.Fatalf("bad") 383 } 384 } 385 386 func TestPlanApply_EvalNodePlan_UpdateExisting(t *testing.T) { 387 alloc := mock.Alloc() 388 state := testStateStore(t) 389 node := mock.Node() 390 alloc.NodeID = node.ID 391 node.Resources = alloc.Resources 392 node.Reserved = nil 393 state.UpsertNode(1000, node) 394 state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 395 snap, _ := state.Snapshot() 396 397 plan := &structs.Plan{ 398 NodeAllocation: map[string][]*structs.Allocation{ 399 node.ID: []*structs.Allocation{alloc}, 400 }, 401 } 402 403 fit, err := evaluateNodePlan(snap, plan, node.ID) 404 if err != nil { 405 t.Fatalf("err: %v", err) 406 } 407 if !fit { 408 t.Fatalf("bad") 409 } 410 } 411 412 func TestPlanApply_EvalNodePlan_NodeFull_Evict(t *testing.T) { 413 alloc := mock.Alloc() 414 state := testStateStore(t) 415 node := mock.Node() 416 alloc.NodeID = node.ID 417 node.Resources = alloc.Resources 418 node.Reserved = nil 419 state.UpsertNode(1000, node) 420 state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 421 snap, _ := state.Snapshot() 422 423 allocEvict := new(structs.Allocation) 424 *allocEvict = *alloc 425 allocEvict.DesiredStatus = structs.AllocDesiredStatusEvict 426 alloc2 := mock.Alloc() 427 plan := &structs.Plan{ 428 NodeUpdate: map[string][]*structs.Allocation{ 429 node.ID: []*structs.Allocation{allocEvict}, 430 }, 431 NodeAllocation: map[string][]*structs.Allocation{ 432 node.ID: []*structs.Allocation{alloc2}, 433 }, 434 } 435 436 fit, err := evaluateNodePlan(snap, plan, node.ID) 437 if err != nil { 438 t.Fatalf("err: %v", err) 439 } 440 if !fit { 441 t.Fatalf("bad") 442 } 443 } 444 445 func TestPlanApply_EvalNodePlan_NodeFull_AllocEvict(t *testing.T) { 446 alloc := mock.Alloc() 447 state := testStateStore(t) 448 node := mock.Node() 449 alloc.NodeID = node.ID 450 alloc.DesiredStatus = structs.AllocDesiredStatusEvict 451 node.Resources = alloc.Resources 452 node.Reserved = nil 453 state.UpsertNode(1000, node) 454 state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 455 snap, _ := state.Snapshot() 456 457 alloc2 := mock.Alloc() 458 plan := &structs.Plan{ 459 NodeAllocation: map[string][]*structs.Allocation{ 460 node.ID: []*structs.Allocation{alloc2}, 461 }, 462 } 463 464 fit, err := evaluateNodePlan(snap, plan, node.ID) 465 if err != nil { 466 t.Fatalf("err: %v", err) 467 } 468 if !fit { 469 t.Fatalf("bad") 470 } 471 } 472 473 func TestPlanApply_EvalNodePlan_NodeDown_EvictOnly(t *testing.T) { 474 alloc := mock.Alloc() 475 state := testStateStore(t) 476 node := mock.Node() 477 alloc.NodeID = node.ID 478 node.Resources = alloc.Resources 479 node.Reserved = nil 480 node.Status = structs.NodeStatusDown 481 state.UpsertNode(1000, node) 482 state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 483 snap, _ := state.Snapshot() 484 485 allocEvict := new(structs.Allocation) 486 *allocEvict = *alloc 487 allocEvict.DesiredStatus = structs.AllocDesiredStatusEvict 488 plan := &structs.Plan{ 489 NodeUpdate: map[string][]*structs.Allocation{ 490 node.ID: []*structs.Allocation{allocEvict}, 491 }, 492 } 493 494 fit, err := evaluateNodePlan(snap, plan, node.ID) 495 if err != nil { 496 t.Fatalf("err: %v", err) 497 } 498 if !fit { 499 t.Fatalf("bad") 500 } 501 }