open-nomad/nomad/plan_apply_test.go

548 lines
12 KiB
Go
Raw Normal View History

2015-07-28 22:15:42 +00:00
package nomad
2015-08-05 01:10:57 +00:00
import (
"reflect"
2015-08-05 01:10:57 +00:00
"testing"
2015-07-28 22:15:42 +00:00
2017-02-08 05:22:48 +00:00
memdb "github.com/hashicorp/go-memdb"
"github.com/hashicorp/nomad/nomad/mock"
2015-08-05 01:10:57 +00:00
"github.com/hashicorp/nomad/nomad/structs"
2015-08-05 01:18:44 +00:00
"github.com/hashicorp/nomad/testutil"
2015-10-11 21:57:36 +00:00
"github.com/hashicorp/raft"
2015-08-05 01:10:57 +00:00
)
const (
// workerPoolSize is the size of the worker pool
workerPoolSize = 2
)
2015-10-11 21:57:36 +00:00
// planWaitFuture is used to wait for the Raft future to complete
func planWaitFuture(future raft.ApplyFuture) (uint64, error) {
if err := future.Error(); err != nil {
return 0, err
}
return future.Index(), nil
}
2015-08-05 01:18:44 +00:00
func testRegisterNode(t *testing.T, s *Server, n *structs.Node) {
// Create the register request
req := &structs.NodeRegisterRequest{
Node: n,
WriteRequest: structs.WriteRequest{Region: "global"},
2015-08-05 01:18:44 +00:00
}
// Fetch the response
2015-08-07 00:02:37 +00:00
var resp structs.NodeUpdateResponse
if err := s.RPC("Node.Register", req, &resp); err != nil {
2015-08-05 01:18:44 +00:00
t.Fatalf("err: %v", err)
}
if resp.Index == 0 {
t.Fatalf("bad index: %d", resp.Index)
}
}
2016-07-25 21:11:32 +00:00
func testRegisterJob(t *testing.T, s *Server, j *structs.Job) {
// Create the register request
req := &structs.JobRegisterRequest{
Job: j,
WriteRequest: structs.WriteRequest{Region: "global"},
}
// Fetch the response
var resp structs.JobRegisterResponse
if err := s.RPC("Job.Register", req, &resp); err != nil {
t.Fatalf("err: %v", err)
}
if resp.Index == 0 {
t.Fatalf("bad index: %d", resp.Index)
}
}
2015-08-05 01:18:44 +00:00
func TestPlanApply_applyPlan(t *testing.T) {
s1 := testServer(t, nil)
defer s1.Shutdown()
testutil.WaitForLeader(t, s1.RPC)
// Register ndoe
node := mock.Node()
2015-08-05 01:18:44 +00:00
testRegisterNode(t, s1, node)
// Register alloc
alloc := mock.Alloc()
2016-07-25 21:11:32 +00:00
s1.State().UpsertJobSummary(1000, mock.JobSummary(alloc.JobID))
planRes := &structs.PlanResult{
2015-08-05 01:18:44 +00:00
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc},
},
}
2015-10-11 22:46:46 +00:00
// Snapshot the state
snap, err := s1.State().Snapshot()
if err != nil {
t.Fatalf("err: %v", err)
}
// Create the plan with a deployment
plan := &structs.Plan{
Job: alloc.Job,
CreatedDeployment: mock.Deployment(),
}
2015-08-05 01:18:44 +00:00
// Apply the plan
future, err := s1.applyPlan(plan, planRes, snap)
2015-10-11 21:48:18 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
2015-10-11 22:46:46 +00:00
// Verify our optimistic snapshot is updated
2017-02-08 05:22:48 +00:00
ws := memdb.NewWatchSet()
if out, err := snap.AllocByID(ws, alloc.ID); err != nil || out == nil {
2015-10-11 22:46:46 +00:00
t.Fatalf("bad: %v %v", out, err)
}
if out, err := snap.DeploymentByID(ws, plan.CreatedDeployment.ID); err != nil || out == nil {
t.Fatalf("bad: %v %v", out, err)
}
2015-10-11 22:46:46 +00:00
// Check plan does apply cleanly
2015-10-11 21:57:36 +00:00
index, err := planWaitFuture(future)
2015-08-05 01:18:44 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
if index == 0 {
t.Fatalf("bad: %d", index)
}
// Lookup the allocation
fsmState := s1.fsm.State()
out, err := fsmState.AllocByID(ws, alloc.ID)
2015-08-05 01:18:44 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
if out == nil {
t.Fatalf("missing alloc")
}
// Lookup the deployment
dout, err := fsmState.DeploymentByID(ws, plan.CreatedDeployment.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if dout == nil {
t.Fatalf("missing deployment")
}
2015-08-05 01:18:44 +00:00
// Evict alloc, Register alloc2
allocEvict := new(structs.Allocation)
*allocEvict = *alloc
allocEvict.DesiredStatus = structs.AllocDesiredStatusEvict
2016-02-21 19:42:54 +00:00
job := allocEvict.Job
allocEvict.Job = nil
alloc2 := mock.Alloc()
2016-07-26 22:11:48 +00:00
s1.State().UpsertJobSummary(1500, mock.JobSummary(alloc2.JobID))
planRes = &structs.PlanResult{
NodeUpdate: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{allocEvict},
2015-08-05 01:18:44 +00:00
},
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc2},
},
}
2015-10-11 22:46:46 +00:00
// Snapshot the state
snap, err = s1.State().Snapshot()
if err != nil {
t.Fatalf("err: %v", err)
}
2015-08-05 01:18:44 +00:00
// Apply the plan
plan = &structs.Plan{
Job: job,
}
future, err = s1.applyPlan(plan, planRes, snap)
2015-10-11 21:48:18 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
2015-10-11 22:46:46 +00:00
// Check that our optimistic view is updated
2017-02-08 05:22:48 +00:00
if out, _ := snap.AllocByID(ws, allocEvict.ID); out.DesiredStatus != structs.AllocDesiredStatusEvict {
2015-10-11 22:46:46 +00:00
t.Fatalf("bad: %#v", out)
}
// Verify plan applies cleanly
2015-10-11 21:57:36 +00:00
index, err = planWaitFuture(future)
2015-08-05 01:18:44 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
if index == 0 {
t.Fatalf("bad: %d", index)
}
// Lookup the allocation
2017-02-08 05:22:48 +00:00
out, err = s1.fsm.State().AllocByID(ws, alloc.ID)
2015-08-05 01:18:44 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
if out.DesiredStatus != structs.AllocDesiredStatusEvict {
t.Fatalf("should be evicted alloc: %#v", out)
2015-08-05 01:18:44 +00:00
}
2016-02-21 19:42:54 +00:00
if out.Job == nil {
t.Fatalf("missing job")
}
2015-08-05 01:18:44 +00:00
// Lookup the allocation
2017-02-08 05:22:48 +00:00
out, err = s1.fsm.State().AllocByID(ws, alloc2.ID)
2015-08-05 01:18:44 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
if out == nil {
t.Fatalf("missing alloc")
}
2016-02-21 19:42:54 +00:00
if out.Job == nil {
t.Fatalf("missing job")
}
2015-08-05 01:18:44 +00:00
}
2015-08-05 01:30:05 +00:00
func TestPlanApply_EvalPlan_Simple(t *testing.T) {
state := testStateStore(t)
node := mock.Node()
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
2015-08-05 01:30:05 +00:00
snap, _ := state.Snapshot()
alloc := mock.Alloc()
2015-08-05 01:30:05 +00:00
plan := &structs.Plan{
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc},
},
}
pool := NewEvaluatePool(workerPoolSize, workerPoolBufferSize)
defer pool.Shutdown()
result, err := evaluatePlan(pool, snap, plan)
2015-08-05 01:30:05 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
if result == nil {
t.Fatalf("missing result")
}
if !reflect.DeepEqual(result.NodeAllocation, plan.NodeAllocation) {
t.Fatalf("incorrect node allocations")
}
2015-08-05 01:30:05 +00:00
}
func TestPlanApply_EvalPlan_Partial(t *testing.T) {
state := testStateStore(t)
node := mock.Node()
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
node2 := mock.Node()
2015-09-07 03:47:42 +00:00
state.UpsertNode(1001, node2)
2015-08-05 01:30:05 +00:00
snap, _ := state.Snapshot()
alloc := mock.Alloc()
alloc2 := mock.Alloc() // Ensure alloc2 does not fit
2015-08-05 01:30:05 +00:00
alloc2.Resources = node2.Resources
plan := &structs.Plan{
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc},
node2.ID: []*structs.Allocation{alloc2},
},
}
pool := NewEvaluatePool(workerPoolSize, workerPoolBufferSize)
defer pool.Shutdown()
result, err := evaluatePlan(pool, snap, plan)
2015-08-05 01:30:05 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
if result == nil {
t.Fatalf("missing result")
}
if _, ok := result.NodeAllocation[node.ID]; !ok {
t.Fatalf("should allow alloc")
}
if _, ok := result.NodeAllocation[node2.ID]; ok {
t.Fatalf("should not allow alloc2")
}
if result.RefreshIndex != 1001 {
t.Fatalf("bad: %d", result.RefreshIndex)
}
2015-08-05 01:30:05 +00:00
}
func TestPlanApply_EvalPlan_Partial_AllAtOnce(t *testing.T) {
state := testStateStore(t)
node := mock.Node()
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
node2 := mock.Node()
2015-09-07 03:47:42 +00:00
state.UpsertNode(1001, node2)
2015-08-05 01:30:05 +00:00
snap, _ := state.Snapshot()
alloc := mock.Alloc()
alloc2 := mock.Alloc() // Ensure alloc2 does not fit
2015-08-05 01:30:05 +00:00
alloc2.Resources = node2.Resources
plan := &structs.Plan{
AllAtOnce: true, // Require all to make progress
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc},
node2.ID: []*structs.Allocation{alloc2},
},
}
pool := NewEvaluatePool(workerPoolSize, workerPoolBufferSize)
defer pool.Shutdown()
result, err := evaluatePlan(pool, snap, plan)
2015-08-05 01:30:05 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
if result == nil {
t.Fatalf("missing result")
}
if len(result.NodeAllocation) != 0 {
t.Fatalf("should not alloc: %v", result.NodeAllocation)
}
if result.RefreshIndex != 1001 {
t.Fatalf("bad: %d", result.RefreshIndex)
}
2015-08-05 01:30:05 +00:00
}
2015-08-05 01:10:57 +00:00
func TestPlanApply_EvalNodePlan_Simple(t *testing.T) {
state := testStateStore(t)
node := mock.Node()
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
2015-08-05 01:10:57 +00:00
snap, _ := state.Snapshot()
alloc := mock.Alloc()
2015-08-05 01:10:57 +00:00
plan := &structs.Plan{
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc},
},
}
fit, err := evaluateNodePlan(snap, plan, node.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if !fit {
t.Fatalf("bad")
}
}
func TestPlanApply_EvalNodePlan_NodeNotReady(t *testing.T) {
state := testStateStore(t)
node := mock.Node()
2015-08-05 01:10:57 +00:00
node.Status = structs.NodeStatusInit
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
2015-08-05 01:10:57 +00:00
snap, _ := state.Snapshot()
alloc := mock.Alloc()
2015-08-05 01:10:57 +00:00
plan := &structs.Plan{
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc},
},
}
fit, err := evaluateNodePlan(snap, plan, node.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if fit {
t.Fatalf("bad")
}
}
2015-09-07 02:47:02 +00:00
func TestPlanApply_EvalNodePlan_NodeDrain(t *testing.T) {
state := testStateStore(t)
node := mock.Node()
node.Drain = true
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
2015-09-07 02:47:02 +00:00
snap, _ := state.Snapshot()
alloc := mock.Alloc()
plan := &structs.Plan{
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc},
},
}
fit, err := evaluateNodePlan(snap, plan, node.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if fit {
t.Fatalf("bad")
}
}
2015-08-05 01:10:57 +00:00
func TestPlanApply_EvalNodePlan_NodeNotExist(t *testing.T) {
state := testStateStore(t)
snap, _ := state.Snapshot()
nodeID := "12345678-abcd-efab-cdef-123456789abc"
alloc := mock.Alloc()
2015-08-05 01:10:57 +00:00
plan := &structs.Plan{
NodeAllocation: map[string][]*structs.Allocation{
nodeID: []*structs.Allocation{alloc},
},
}
fit, err := evaluateNodePlan(snap, plan, nodeID)
if err != nil {
t.Fatalf("err: %v", err)
}
if fit {
t.Fatalf("bad")
}
}
func TestPlanApply_EvalNodePlan_NodeFull(t *testing.T) {
alloc := mock.Alloc()
2015-08-05 01:10:57 +00:00
state := testStateStore(t)
node := mock.Node()
2015-08-05 01:10:57 +00:00
alloc.NodeID = node.ID
node.Resources = alloc.Resources
node.Reserved = nil
2016-07-25 21:11:32 +00:00
state.UpsertJobSummary(999, mock.JobSummary(alloc.JobID))
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
state.UpsertAllocs(1001, []*structs.Allocation{alloc})
2015-08-05 01:10:57 +00:00
alloc2 := mock.Alloc()
alloc2.NodeID = node.ID
2016-07-25 21:11:32 +00:00
state.UpsertJobSummary(1200, mock.JobSummary(alloc2.JobID))
snap, _ := state.Snapshot()
2015-08-05 01:10:57 +00:00
plan := &structs.Plan{
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc2},
2015-08-05 01:10:57 +00:00
},
}
fit, err := evaluateNodePlan(snap, plan, node.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if fit {
t.Fatalf("bad")
}
}
func TestPlanApply_EvalNodePlan_UpdateExisting(t *testing.T) {
alloc := mock.Alloc()
state := testStateStore(t)
node := mock.Node()
alloc.NodeID = node.ID
node.Resources = alloc.Resources
node.Reserved = nil
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
state.UpsertAllocs(1001, []*structs.Allocation{alloc})
snap, _ := state.Snapshot()
plan := &structs.Plan{
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc},
},
}
fit, err := evaluateNodePlan(snap, plan, node.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if !fit {
t.Fatalf("bad")
}
}
2015-08-05 01:10:57 +00:00
func TestPlanApply_EvalNodePlan_NodeFull_Evict(t *testing.T) {
alloc := mock.Alloc()
2015-08-05 01:10:57 +00:00
state := testStateStore(t)
node := mock.Node()
2015-08-05 01:10:57 +00:00
alloc.NodeID = node.ID
node.Resources = alloc.Resources
node.Reserved = nil
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
state.UpsertAllocs(1001, []*structs.Allocation{alloc})
2015-08-05 01:10:57 +00:00
snap, _ := state.Snapshot()
allocEvict := new(structs.Allocation)
*allocEvict = *alloc
allocEvict.DesiredStatus = structs.AllocDesiredStatusEvict
alloc2 := mock.Alloc()
2015-08-05 01:10:57 +00:00
plan := &structs.Plan{
NodeUpdate: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{allocEvict},
2015-08-05 01:10:57 +00:00
},
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc2},
},
}
fit, err := evaluateNodePlan(snap, plan, node.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if !fit {
t.Fatalf("bad")
}
2015-07-28 22:15:42 +00:00
}
2015-08-23 01:30:49 +00:00
func TestPlanApply_EvalNodePlan_NodeFull_AllocEvict(t *testing.T) {
alloc := mock.Alloc()
state := testStateStore(t)
node := mock.Node()
alloc.NodeID = node.ID
alloc.DesiredStatus = structs.AllocDesiredStatusEvict
2015-08-23 01:30:49 +00:00
node.Resources = alloc.Resources
node.Reserved = nil
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
state.UpsertAllocs(1001, []*structs.Allocation{alloc})
2015-08-23 01:30:49 +00:00
snap, _ := state.Snapshot()
alloc2 := mock.Alloc()
plan := &structs.Plan{
NodeAllocation: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{alloc2},
},
}
fit, err := evaluateNodePlan(snap, plan, node.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if !fit {
t.Fatalf("bad")
}
}
2015-09-07 02:47:02 +00:00
func TestPlanApply_EvalNodePlan_NodeDown_EvictOnly(t *testing.T) {
alloc := mock.Alloc()
state := testStateStore(t)
node := mock.Node()
alloc.NodeID = node.ID
node.Resources = alloc.Resources
node.Reserved = nil
2015-09-07 02:47:02 +00:00
node.Status = structs.NodeStatusDown
2015-09-07 03:47:42 +00:00
state.UpsertNode(1000, node)
state.UpsertAllocs(1001, []*structs.Allocation{alloc})
snap, _ := state.Snapshot()
allocEvict := new(structs.Allocation)
*allocEvict = *alloc
allocEvict.DesiredStatus = structs.AllocDesiredStatusEvict
plan := &structs.Plan{
NodeUpdate: map[string][]*structs.Allocation{
node.ID: []*structs.Allocation{allocEvict},
},
}
fit, err := evaluateNodePlan(snap, plan, node.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if !fit {
t.Fatalf("bad")
}
}