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