github.com/hooklift/nomad@v0.5.7-0.20170407200202-db11e7dd7b55/nomad/plan_apply_test.go (about)

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