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