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  }