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