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