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