github.com/hooklift/nomad@v0.5.7-0.20170407200202-db11e7dd7b55/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 plan := &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 // Apply the plan 86 future, err := s1.applyPlan(alloc.Job, plan, snap) 87 if err != nil { 88 t.Fatalf("err: %v", err) 89 } 90 91 // Verify our optimistic snapshot is updated 92 ws := memdb.NewWatchSet() 93 if out, err := snap.AllocByID(ws, alloc.ID); err != nil || out == nil { 94 t.Fatalf("bad: %v %v", out, err) 95 } 96 97 // Check plan does apply cleanly 98 index, err := planWaitFuture(future) 99 if err != nil { 100 t.Fatalf("err: %v", err) 101 } 102 if index == 0 { 103 t.Fatalf("bad: %d", index) 104 } 105 106 // Lookup the allocation 107 out, err := s1.fsm.State().AllocByID(ws, alloc.ID) 108 if err != nil { 109 t.Fatalf("err: %v", err) 110 } 111 if out == nil { 112 t.Fatalf("missing alloc") 113 } 114 115 // Evict alloc, Register alloc2 116 allocEvict := new(structs.Allocation) 117 *allocEvict = *alloc 118 allocEvict.DesiredStatus = structs.AllocDesiredStatusEvict 119 job := allocEvict.Job 120 allocEvict.Job = nil 121 alloc2 := mock.Alloc() 122 alloc2.Job = nil 123 s1.State().UpsertJobSummary(1500, mock.JobSummary(alloc2.JobID)) 124 plan = &structs.PlanResult{ 125 NodeUpdate: map[string][]*structs.Allocation{ 126 node.ID: []*structs.Allocation{allocEvict}, 127 }, 128 NodeAllocation: map[string][]*structs.Allocation{ 129 node.ID: []*structs.Allocation{alloc2}, 130 }, 131 } 132 133 // Snapshot the state 134 snap, err = s1.State().Snapshot() 135 if err != nil { 136 t.Fatalf("err: %v", err) 137 } 138 139 // Apply the plan 140 future, err = s1.applyPlan(job, plan, snap) 141 if err != nil { 142 t.Fatalf("err: %v", err) 143 } 144 145 // Check that our optimistic view is updated 146 if out, _ := snap.AllocByID(ws, allocEvict.ID); out.DesiredStatus != structs.AllocDesiredStatusEvict { 147 t.Fatalf("bad: %#v", out) 148 } 149 150 // Verify plan applies cleanly 151 index, err = planWaitFuture(future) 152 if err != nil { 153 t.Fatalf("err: %v", err) 154 } 155 if index == 0 { 156 t.Fatalf("bad: %d", index) 157 } 158 159 // Lookup the allocation 160 out, err = s1.fsm.State().AllocByID(ws, alloc.ID) 161 if err != nil { 162 t.Fatalf("err: %v", err) 163 } 164 if out.DesiredStatus != structs.AllocDesiredStatusEvict { 165 t.Fatalf("should be evicted alloc: %#v", out) 166 } 167 if out.Job == nil { 168 t.Fatalf("missing job") 169 } 170 171 // Lookup the allocation 172 out, err = s1.fsm.State().AllocByID(ws, alloc2.ID) 173 if err != nil { 174 t.Fatalf("err: %v", err) 175 } 176 if out == nil { 177 t.Fatalf("missing alloc") 178 } 179 if out.Job == nil { 180 t.Fatalf("missing job") 181 } 182 } 183 184 func TestPlanApply_EvalPlan_Simple(t *testing.T) { 185 state := testStateStore(t) 186 node := mock.Node() 187 state.UpsertNode(1000, node) 188 snap, _ := state.Snapshot() 189 190 alloc := mock.Alloc() 191 plan := &structs.Plan{ 192 NodeAllocation: map[string][]*structs.Allocation{ 193 node.ID: []*structs.Allocation{alloc}, 194 }, 195 } 196 197 pool := NewEvaluatePool(workerPoolSize, workerPoolBufferSize) 198 defer pool.Shutdown() 199 200 result, err := evaluatePlan(pool, snap, plan) 201 if err != nil { 202 t.Fatalf("err: %v", err) 203 } 204 if result == nil { 205 t.Fatalf("missing result") 206 } 207 if !reflect.DeepEqual(result.NodeAllocation, plan.NodeAllocation) { 208 t.Fatalf("incorrect node allocations") 209 } 210 } 211 212 func TestPlanApply_EvalPlan_Partial(t *testing.T) { 213 state := testStateStore(t) 214 node := mock.Node() 215 state.UpsertNode(1000, node) 216 node2 := mock.Node() 217 state.UpsertNode(1001, node2) 218 snap, _ := state.Snapshot() 219 220 alloc := mock.Alloc() 221 alloc2 := mock.Alloc() // Ensure alloc2 does not fit 222 alloc2.Resources = node2.Resources 223 plan := &structs.Plan{ 224 NodeAllocation: map[string][]*structs.Allocation{ 225 node.ID: []*structs.Allocation{alloc}, 226 node2.ID: []*structs.Allocation{alloc2}, 227 }, 228 } 229 230 pool := NewEvaluatePool(workerPoolSize, workerPoolBufferSize) 231 defer pool.Shutdown() 232 233 result, err := evaluatePlan(pool, snap, plan) 234 if err != nil { 235 t.Fatalf("err: %v", err) 236 } 237 if result == nil { 238 t.Fatalf("missing result") 239 } 240 241 if _, ok := result.NodeAllocation[node.ID]; !ok { 242 t.Fatalf("should allow alloc") 243 } 244 if _, ok := result.NodeAllocation[node2.ID]; ok { 245 t.Fatalf("should not allow alloc2") 246 } 247 if result.RefreshIndex != 1001 { 248 t.Fatalf("bad: %d", result.RefreshIndex) 249 } 250 } 251 252 func TestPlanApply_EvalPlan_Partial_AllAtOnce(t *testing.T) { 253 state := testStateStore(t) 254 node := mock.Node() 255 state.UpsertNode(1000, node) 256 node2 := mock.Node() 257 state.UpsertNode(1001, node2) 258 snap, _ := state.Snapshot() 259 260 alloc := mock.Alloc() 261 alloc2 := mock.Alloc() // Ensure alloc2 does not fit 262 alloc2.Resources = node2.Resources 263 plan := &structs.Plan{ 264 AllAtOnce: true, // Require all to make progress 265 NodeAllocation: map[string][]*structs.Allocation{ 266 node.ID: []*structs.Allocation{alloc}, 267 node2.ID: []*structs.Allocation{alloc2}, 268 }, 269 } 270 271 pool := NewEvaluatePool(workerPoolSize, workerPoolBufferSize) 272 defer pool.Shutdown() 273 274 result, err := evaluatePlan(pool, snap, plan) 275 if err != nil { 276 t.Fatalf("err: %v", err) 277 } 278 if result == nil { 279 t.Fatalf("missing result") 280 } 281 282 if len(result.NodeAllocation) != 0 { 283 t.Fatalf("should not alloc: %v", result.NodeAllocation) 284 } 285 if result.RefreshIndex != 1001 { 286 t.Fatalf("bad: %d", result.RefreshIndex) 287 } 288 } 289 290 func TestPlanApply_EvalNodePlan_Simple(t *testing.T) { 291 state := testStateStore(t) 292 node := mock.Node() 293 state.UpsertNode(1000, node) 294 snap, _ := state.Snapshot() 295 296 alloc := mock.Alloc() 297 plan := &structs.Plan{ 298 NodeAllocation: map[string][]*structs.Allocation{ 299 node.ID: []*structs.Allocation{alloc}, 300 }, 301 } 302 303 fit, err := evaluateNodePlan(snap, plan, node.ID) 304 if err != nil { 305 t.Fatalf("err: %v", err) 306 } 307 if !fit { 308 t.Fatalf("bad") 309 } 310 } 311 312 func TestPlanApply_EvalNodePlan_NodeNotReady(t *testing.T) { 313 state := testStateStore(t) 314 node := mock.Node() 315 node.Status = structs.NodeStatusInit 316 state.UpsertNode(1000, node) 317 snap, _ := state.Snapshot() 318 319 alloc := mock.Alloc() 320 plan := &structs.Plan{ 321 NodeAllocation: map[string][]*structs.Allocation{ 322 node.ID: []*structs.Allocation{alloc}, 323 }, 324 } 325 326 fit, err := evaluateNodePlan(snap, plan, node.ID) 327 if err != nil { 328 t.Fatalf("err: %v", err) 329 } 330 if fit { 331 t.Fatalf("bad") 332 } 333 } 334 335 func TestPlanApply_EvalNodePlan_NodeDrain(t *testing.T) { 336 state := testStateStore(t) 337 node := mock.Node() 338 node.Drain = true 339 state.UpsertNode(1000, node) 340 snap, _ := state.Snapshot() 341 342 alloc := mock.Alloc() 343 plan := &structs.Plan{ 344 NodeAllocation: map[string][]*structs.Allocation{ 345 node.ID: []*structs.Allocation{alloc}, 346 }, 347 } 348 349 fit, err := evaluateNodePlan(snap, plan, node.ID) 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_NodeNotExist(t *testing.T) { 359 state := testStateStore(t) 360 snap, _ := state.Snapshot() 361 362 nodeID := "12345678-abcd-efab-cdef-123456789abc" 363 alloc := mock.Alloc() 364 plan := &structs.Plan{ 365 NodeAllocation: map[string][]*structs.Allocation{ 366 nodeID: []*structs.Allocation{alloc}, 367 }, 368 } 369 370 fit, err := evaluateNodePlan(snap, plan, nodeID) 371 if err != nil { 372 t.Fatalf("err: %v", err) 373 } 374 if fit { 375 t.Fatalf("bad") 376 } 377 } 378 379 func TestPlanApply_EvalNodePlan_NodeFull(t *testing.T) { 380 alloc := mock.Alloc() 381 state := testStateStore(t) 382 node := mock.Node() 383 alloc.NodeID = node.ID 384 node.Resources = alloc.Resources 385 node.Reserved = nil 386 state.UpsertJobSummary(999, mock.JobSummary(alloc.JobID)) 387 state.UpsertNode(1000, node) 388 state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 389 390 alloc2 := mock.Alloc() 391 alloc2.NodeID = node.ID 392 state.UpsertJobSummary(1200, mock.JobSummary(alloc2.JobID)) 393 394 snap, _ := state.Snapshot() 395 plan := &structs.Plan{ 396 NodeAllocation: map[string][]*structs.Allocation{ 397 node.ID: []*structs.Allocation{alloc2}, 398 }, 399 } 400 401 fit, err := evaluateNodePlan(snap, plan, node.ID) 402 if err != nil { 403 t.Fatalf("err: %v", err) 404 } 405 if fit { 406 t.Fatalf("bad") 407 } 408 } 409 410 func TestPlanApply_EvalNodePlan_UpdateExisting(t *testing.T) { 411 alloc := mock.Alloc() 412 state := testStateStore(t) 413 node := mock.Node() 414 alloc.NodeID = node.ID 415 node.Resources = alloc.Resources 416 node.Reserved = nil 417 state.UpsertNode(1000, node) 418 state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 419 snap, _ := state.Snapshot() 420 421 plan := &structs.Plan{ 422 NodeAllocation: map[string][]*structs.Allocation{ 423 node.ID: []*structs.Allocation{alloc}, 424 }, 425 } 426 427 fit, err := evaluateNodePlan(snap, plan, node.ID) 428 if err != nil { 429 t.Fatalf("err: %v", err) 430 } 431 if !fit { 432 t.Fatalf("bad") 433 } 434 } 435 436 func TestPlanApply_EvalNodePlan_NodeFull_Evict(t *testing.T) { 437 alloc := mock.Alloc() 438 state := testStateStore(t) 439 node := mock.Node() 440 alloc.NodeID = node.ID 441 node.Resources = alloc.Resources 442 node.Reserved = nil 443 state.UpsertNode(1000, node) 444 state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 445 snap, _ := state.Snapshot() 446 447 allocEvict := new(structs.Allocation) 448 *allocEvict = *alloc 449 allocEvict.DesiredStatus = structs.AllocDesiredStatusEvict 450 alloc2 := mock.Alloc() 451 plan := &structs.Plan{ 452 NodeUpdate: map[string][]*structs.Allocation{ 453 node.ID: []*structs.Allocation{allocEvict}, 454 }, 455 NodeAllocation: map[string][]*structs.Allocation{ 456 node.ID: []*structs.Allocation{alloc2}, 457 }, 458 } 459 460 fit, err := evaluateNodePlan(snap, plan, node.ID) 461 if err != nil { 462 t.Fatalf("err: %v", err) 463 } 464 if !fit { 465 t.Fatalf("bad") 466 } 467 } 468 469 func TestPlanApply_EvalNodePlan_NodeFull_AllocEvict(t *testing.T) { 470 alloc := mock.Alloc() 471 state := testStateStore(t) 472 node := mock.Node() 473 alloc.NodeID = node.ID 474 alloc.DesiredStatus = structs.AllocDesiredStatusEvict 475 node.Resources = alloc.Resources 476 node.Reserved = nil 477 state.UpsertNode(1000, node) 478 state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 479 snap, _ := state.Snapshot() 480 481 alloc2 := mock.Alloc() 482 plan := &structs.Plan{ 483 NodeAllocation: map[string][]*structs.Allocation{ 484 node.ID: []*structs.Allocation{alloc2}, 485 }, 486 } 487 488 fit, err := evaluateNodePlan(snap, plan, node.ID) 489 if err != nil { 490 t.Fatalf("err: %v", err) 491 } 492 if !fit { 493 t.Fatalf("bad") 494 } 495 } 496 497 func TestPlanApply_EvalNodePlan_NodeDown_EvictOnly(t *testing.T) { 498 alloc := mock.Alloc() 499 state := testStateStore(t) 500 node := mock.Node() 501 alloc.NodeID = node.ID 502 node.Resources = alloc.Resources 503 node.Reserved = nil 504 node.Status = structs.NodeStatusDown 505 state.UpsertNode(1000, node) 506 state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 507 snap, _ := state.Snapshot() 508 509 allocEvict := new(structs.Allocation) 510 *allocEvict = *alloc 511 allocEvict.DesiredStatus = structs.AllocDesiredStatusEvict 512 plan := &structs.Plan{ 513 NodeUpdate: map[string][]*structs.Allocation{ 514 node.ID: []*structs.Allocation{allocEvict}, 515 }, 516 } 517 518 fit, err := evaluateNodePlan(snap, plan, node.ID) 519 if err != nil { 520 t.Fatalf("err: %v", err) 521 } 522 if !fit { 523 t.Fatalf("bad") 524 } 525 }