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