github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/scheduler/generic_sched_test.go (about)

     1  package scheduler
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sort"
     7  	"testing"
     8  	"time"
     9  
    10  	memdb "github.com/hashicorp/go-memdb"
    11  	"github.com/ncodes/nomad/nomad/mock"
    12  	"github.com/ncodes/nomad/nomad/structs"
    13  )
    14  
    15  func TestServiceSched_JobRegister(t *testing.T) {
    16  	h := NewHarness(t)
    17  
    18  	// Create some nodes
    19  	for i := 0; i < 10; i++ {
    20  		node := mock.Node()
    21  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
    22  	}
    23  
    24  	// Create a job
    25  	job := mock.Job()
    26  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
    27  
    28  	// Create a mock evaluation to register the job
    29  	eval := &structs.Evaluation{
    30  		ID:          structs.GenerateUUID(),
    31  		Priority:    job.Priority,
    32  		TriggeredBy: structs.EvalTriggerJobRegister,
    33  		JobID:       job.ID,
    34  	}
    35  
    36  	// Process the evaluation
    37  	err := h.Process(NewServiceScheduler, eval)
    38  	if err != nil {
    39  		t.Fatalf("err: %v", err)
    40  	}
    41  
    42  	// Ensure a single plan
    43  	if len(h.Plans) != 1 {
    44  		t.Fatalf("bad: %#v", h.Plans)
    45  	}
    46  	plan := h.Plans[0]
    47  
    48  	// Ensure the plan doesn't have annotations.
    49  	if plan.Annotations != nil {
    50  		t.Fatalf("expected no annotations")
    51  	}
    52  
    53  	// Ensure the eval has no spawned blocked eval
    54  	if len(h.CreateEvals) != 0 {
    55  		t.Fatalf("bad: %#v", h.CreateEvals)
    56  		if h.Evals[0].BlockedEval != "" {
    57  			t.Fatalf("bad: %#v", h.Evals[0])
    58  		}
    59  	}
    60  
    61  	// Ensure the plan allocated
    62  	var planned []*structs.Allocation
    63  	for _, allocList := range plan.NodeAllocation {
    64  		planned = append(planned, allocList...)
    65  	}
    66  	if len(planned) != 10 {
    67  		t.Fatalf("bad: %#v", plan)
    68  	}
    69  
    70  	// Lookup the allocations by JobID
    71  	ws := memdb.NewWatchSet()
    72  	out, err := h.State.AllocsByJob(ws, job.ID, false)
    73  	noErr(t, err)
    74  
    75  	// Ensure all allocations placed
    76  	if len(out) != 10 {
    77  		t.Fatalf("bad: %#v", out)
    78  	}
    79  
    80  	// Ensure different ports were used.
    81  	used := make(map[int]struct{})
    82  	for _, alloc := range out {
    83  		for _, resource := range alloc.TaskResources {
    84  			for _, port := range resource.Networks[0].DynamicPorts {
    85  				if _, ok := used[port.Value]; ok {
    86  					t.Fatalf("Port collision %v", port.Value)
    87  				}
    88  				used[port.Value] = struct{}{}
    89  			}
    90  		}
    91  	}
    92  
    93  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
    94  }
    95  
    96  func TestServiceSched_JobRegister_StickyAllocs(t *testing.T) {
    97  	h := NewHarness(t)
    98  
    99  	// Create some nodes
   100  	for i := 0; i < 10; i++ {
   101  		node := mock.Node()
   102  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   103  	}
   104  
   105  	// Create a job
   106  	job := mock.Job()
   107  	job.TaskGroups[0].EphemeralDisk.Sticky = true
   108  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   109  
   110  	// Create a mock evaluation to register the job
   111  	eval := &structs.Evaluation{
   112  		ID:          structs.GenerateUUID(),
   113  		Priority:    job.Priority,
   114  		TriggeredBy: structs.EvalTriggerJobRegister,
   115  		JobID:       job.ID,
   116  	}
   117  
   118  	// Process the evaluation
   119  	if err := h.Process(NewServiceScheduler, eval); err != nil {
   120  		t.Fatalf("err: %v", err)
   121  	}
   122  
   123  	// Ensure the plan allocated
   124  	plan := h.Plans[0]
   125  	var planned []*structs.Allocation
   126  	for _, allocList := range plan.NodeAllocation {
   127  		planned = append(planned, allocList...)
   128  	}
   129  	if len(planned) != 10 {
   130  		t.Fatalf("bad: %#v", plan)
   131  	}
   132  
   133  	// Get an allocation and mark it as failed
   134  	alloc := planned[4].Copy()
   135  	alloc.ClientStatus = structs.AllocClientStatusFailed
   136  	noErr(t, h.State.UpdateAllocsFromClient(h.NextIndex(), []*structs.Allocation{alloc}))
   137  
   138  	// Create a mock evaluation to handle the update
   139  	eval = &structs.Evaluation{
   140  		ID:          structs.GenerateUUID(),
   141  		Priority:    job.Priority,
   142  		TriggeredBy: structs.EvalTriggerNodeUpdate,
   143  		JobID:       job.ID,
   144  	}
   145  	h1 := NewHarnessWithState(t, h.State)
   146  	if err := h1.Process(NewServiceScheduler, eval); err != nil {
   147  		t.Fatalf("err: %v", err)
   148  	}
   149  
   150  	// Ensure we have created only one new allocation
   151  	plan = h1.Plans[0]
   152  	var newPlanned []*structs.Allocation
   153  	for _, allocList := range plan.NodeAllocation {
   154  		newPlanned = append(newPlanned, allocList...)
   155  	}
   156  	if len(newPlanned) != 1 {
   157  		t.Fatalf("bad plan: %#v", plan)
   158  	}
   159  	// Ensure that the new allocation was placed on the same node as the older
   160  	// one
   161  	if newPlanned[0].NodeID != alloc.NodeID || newPlanned[0].PreviousAllocation != alloc.ID {
   162  		t.Fatalf("expected: %#v, actual: %#v", alloc, newPlanned[0])
   163  	}
   164  }
   165  
   166  func TestServiceSched_JobRegister_DiskConstraints(t *testing.T) {
   167  	h := NewHarness(t)
   168  
   169  	// Create a node
   170  	node := mock.Node()
   171  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   172  
   173  	// Create a job with count 2 and disk as 60GB so that only one allocation
   174  	// can fit
   175  	job := mock.Job()
   176  	job.TaskGroups[0].Count = 2
   177  	job.TaskGroups[0].EphemeralDisk.SizeMB = 88 * 1024
   178  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   179  
   180  	// Create a mock evaluation to register the job
   181  	eval := &structs.Evaluation{
   182  		ID:          structs.GenerateUUID(),
   183  		Priority:    job.Priority,
   184  		TriggeredBy: structs.EvalTriggerJobRegister,
   185  		JobID:       job.ID,
   186  	}
   187  
   188  	// Process the evaluation
   189  	err := h.Process(NewServiceScheduler, eval)
   190  	if err != nil {
   191  		t.Fatalf("err: %v", err)
   192  	}
   193  
   194  	// Ensure a single plan
   195  	if len(h.Plans) != 1 {
   196  		t.Fatalf("bad: %#v", h.Plans)
   197  	}
   198  	plan := h.Plans[0]
   199  
   200  	// Ensure the plan doesn't have annotations.
   201  	if plan.Annotations != nil {
   202  		t.Fatalf("expected no annotations")
   203  	}
   204  
   205  	// Ensure the eval has a blocked eval
   206  	if len(h.CreateEvals) != 1 {
   207  		t.Fatalf("bad: %#v", h.CreateEvals)
   208  	}
   209  
   210  	// Ensure the plan allocated only one allocation
   211  	var planned []*structs.Allocation
   212  	for _, allocList := range plan.NodeAllocation {
   213  		planned = append(planned, allocList...)
   214  	}
   215  	if len(planned) != 1 {
   216  		t.Fatalf("bad: %#v", plan)
   217  	}
   218  
   219  	// Lookup the allocations by JobID
   220  	ws := memdb.NewWatchSet()
   221  	out, err := h.State.AllocsByJob(ws, job.ID, false)
   222  	noErr(t, err)
   223  
   224  	// Ensure only one allocation was placed
   225  	if len(out) != 1 {
   226  		t.Fatalf("bad: %#v", out)
   227  	}
   228  
   229  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   230  }
   231  
   232  func TestServiceSched_JobRegister_DistinctHosts(t *testing.T) {
   233  	h := NewHarness(t)
   234  
   235  	// Create some nodes
   236  	for i := 0; i < 10; i++ {
   237  		node := mock.Node()
   238  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   239  	}
   240  
   241  	// Create a job that uses distinct host and has count 1 higher than what is
   242  	// possible.
   243  	job := mock.Job()
   244  	job.TaskGroups[0].Count = 11
   245  	job.Constraints = append(job.Constraints, &structs.Constraint{Operand: structs.ConstraintDistinctHosts})
   246  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   247  
   248  	// Create a mock evaluation to register the job
   249  	eval := &structs.Evaluation{
   250  		ID:          structs.GenerateUUID(),
   251  		Priority:    job.Priority,
   252  		TriggeredBy: structs.EvalTriggerJobRegister,
   253  		JobID:       job.ID,
   254  	}
   255  
   256  	// Process the evaluation
   257  	err := h.Process(NewServiceScheduler, eval)
   258  	if err != nil {
   259  		t.Fatalf("err: %v", err)
   260  	}
   261  
   262  	// Ensure a single plan
   263  	if len(h.Plans) != 1 {
   264  		t.Fatalf("bad: %#v", h.Plans)
   265  	}
   266  	plan := h.Plans[0]
   267  
   268  	// Ensure the eval has spawned blocked eval
   269  	if len(h.CreateEvals) != 1 {
   270  		t.Fatalf("bad: %#v", h.CreateEvals)
   271  	}
   272  
   273  	// Ensure the plan failed to alloc
   274  	outEval := h.Evals[0]
   275  	if len(outEval.FailedTGAllocs) != 1 {
   276  		t.Fatalf("bad: %+v", outEval)
   277  	}
   278  
   279  	// Ensure the plan allocated
   280  	var planned []*structs.Allocation
   281  	for _, allocList := range plan.NodeAllocation {
   282  		planned = append(planned, allocList...)
   283  	}
   284  	if len(planned) != 10 {
   285  		t.Fatalf("bad: %#v", plan)
   286  	}
   287  
   288  	// Lookup the allocations by JobID
   289  	ws := memdb.NewWatchSet()
   290  	out, err := h.State.AllocsByJob(ws, job.ID, false)
   291  	noErr(t, err)
   292  
   293  	// Ensure all allocations placed
   294  	if len(out) != 10 {
   295  		t.Fatalf("bad: %#v", out)
   296  	}
   297  
   298  	// Ensure different node was used per.
   299  	used := make(map[string]struct{})
   300  	for _, alloc := range out {
   301  		if _, ok := used[alloc.NodeID]; ok {
   302  			t.Fatalf("Node collision %v", alloc.NodeID)
   303  		}
   304  		used[alloc.NodeID] = struct{}{}
   305  	}
   306  
   307  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   308  }
   309  
   310  func TestServiceSched_JobRegister_DistinctProperty(t *testing.T) {
   311  	h := NewHarness(t)
   312  
   313  	// Create some nodes
   314  	for i := 0; i < 10; i++ {
   315  		node := mock.Node()
   316  		rack := "rack2"
   317  		if i < 5 {
   318  			rack = "rack1"
   319  		}
   320  		node.Meta["rack"] = rack
   321  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   322  	}
   323  
   324  	// Create a job that uses distinct property and has count higher than what is
   325  	// possible.
   326  	job := mock.Job()
   327  	job.TaskGroups[0].Count = 4
   328  	job.Constraints = append(job.Constraints,
   329  		&structs.Constraint{
   330  			Operand: structs.ConstraintDistinctProperty,
   331  			LTarget: "${meta.rack}",
   332  		})
   333  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   334  
   335  	// Create a mock evaluation to register the job
   336  	eval := &structs.Evaluation{
   337  		ID:          structs.GenerateUUID(),
   338  		Priority:    job.Priority,
   339  		TriggeredBy: structs.EvalTriggerJobRegister,
   340  		JobID:       job.ID,
   341  	}
   342  
   343  	// Process the evaluation
   344  	err := h.Process(NewServiceScheduler, eval)
   345  	if err != nil {
   346  		t.Fatalf("err: %v", err)
   347  	}
   348  
   349  	// Ensure a single plan
   350  	if len(h.Plans) != 1 {
   351  		t.Fatalf("bad: %#v", h.Plans)
   352  	}
   353  	plan := h.Plans[0]
   354  
   355  	// Ensure the plan doesn't have annotations.
   356  	if plan.Annotations != nil {
   357  		t.Fatalf("expected no annotations")
   358  	}
   359  
   360  	// Ensure the eval has spawned blocked eval
   361  	if len(h.CreateEvals) != 1 {
   362  		t.Fatalf("bad: %#v", h.CreateEvals)
   363  	}
   364  
   365  	// Ensure the plan failed to alloc
   366  	outEval := h.Evals[0]
   367  	if len(outEval.FailedTGAllocs) != 1 {
   368  		t.Fatalf("bad: %+v", outEval)
   369  	}
   370  
   371  	// Ensure the plan allocated
   372  	var planned []*structs.Allocation
   373  	for _, allocList := range plan.NodeAllocation {
   374  		planned = append(planned, allocList...)
   375  	}
   376  	if len(planned) != 2 {
   377  		t.Fatalf("bad: %#v", plan)
   378  	}
   379  
   380  	// Lookup the allocations by JobID
   381  	ws := memdb.NewWatchSet()
   382  	out, err := h.State.AllocsByJob(ws, job.ID, false)
   383  	noErr(t, err)
   384  
   385  	// Ensure all allocations placed
   386  	if len(out) != 2 {
   387  		t.Fatalf("bad: %#v", out)
   388  	}
   389  
   390  	// Ensure different node was used per.
   391  	used := make(map[string]struct{})
   392  	for _, alloc := range out {
   393  		if _, ok := used[alloc.NodeID]; ok {
   394  			t.Fatalf("Node collision %v", alloc.NodeID)
   395  		}
   396  		used[alloc.NodeID] = struct{}{}
   397  	}
   398  
   399  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   400  }
   401  
   402  func TestServiceSched_JobRegister_DistinctProperty_TaskGroup(t *testing.T) {
   403  	h := NewHarness(t)
   404  
   405  	// Create some nodes
   406  	for i := 0; i < 2; i++ {
   407  		node := mock.Node()
   408  		node.Meta["ssd"] = "true"
   409  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   410  	}
   411  
   412  	// Create a job that uses distinct property and has count higher than what is
   413  	// possible.
   414  	job := mock.Job()
   415  	job.TaskGroups = append(job.TaskGroups, job.TaskGroups[0].Copy())
   416  	job.TaskGroups[0].Count = 1
   417  	job.TaskGroups[0].Constraints = append(job.TaskGroups[0].Constraints,
   418  		&structs.Constraint{
   419  			Operand: structs.ConstraintDistinctProperty,
   420  			LTarget: "${meta.ssd}",
   421  		})
   422  
   423  	job.TaskGroups[1].Name = "tg2"
   424  	job.TaskGroups[1].Count = 1
   425  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   426  
   427  	// Create a mock evaluation to register the job
   428  	eval := &structs.Evaluation{
   429  		ID:          structs.GenerateUUID(),
   430  		Priority:    job.Priority,
   431  		TriggeredBy: structs.EvalTriggerJobRegister,
   432  		JobID:       job.ID,
   433  	}
   434  
   435  	// Process the evaluation
   436  	err := h.Process(NewServiceScheduler, eval)
   437  	if err != nil {
   438  		t.Fatalf("err: %v", err)
   439  	}
   440  
   441  	// Ensure a single plan
   442  	if len(h.Plans) != 1 {
   443  		t.Fatalf("bad: %#v", h.Plans)
   444  	}
   445  	plan := h.Plans[0]
   446  
   447  	// Ensure the plan doesn't have annotations.
   448  	if plan.Annotations != nil {
   449  		t.Fatalf("expected no annotations")
   450  	}
   451  
   452  	// Ensure the eval hasn't spawned blocked eval
   453  	if len(h.CreateEvals) != 0 {
   454  		t.Fatalf("bad: %#v", h.CreateEvals[0])
   455  	}
   456  
   457  	// Ensure the plan allocated
   458  	var planned []*structs.Allocation
   459  	for _, allocList := range plan.NodeAllocation {
   460  		planned = append(planned, allocList...)
   461  	}
   462  	if len(planned) != 2 {
   463  		t.Fatalf("bad: %#v", plan)
   464  	}
   465  
   466  	// Lookup the allocations by JobID
   467  	ws := memdb.NewWatchSet()
   468  	out, err := h.State.AllocsByJob(ws, job.ID, false)
   469  	noErr(t, err)
   470  
   471  	// Ensure all allocations placed
   472  	if len(out) != 2 {
   473  		t.Fatalf("bad: %#v", out)
   474  	}
   475  
   476  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   477  }
   478  
   479  func TestServiceSched_JobRegister_Annotate(t *testing.T) {
   480  	h := NewHarness(t)
   481  
   482  	// Create some nodes
   483  	for i := 0; i < 10; i++ {
   484  		node := mock.Node()
   485  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   486  	}
   487  
   488  	// Create a job
   489  	job := mock.Job()
   490  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   491  
   492  	// Create a mock evaluation to register the job
   493  	eval := &structs.Evaluation{
   494  		ID:           structs.GenerateUUID(),
   495  		Priority:     job.Priority,
   496  		TriggeredBy:  structs.EvalTriggerJobRegister,
   497  		JobID:        job.ID,
   498  		AnnotatePlan: true,
   499  	}
   500  
   501  	// Process the evaluation
   502  	err := h.Process(NewServiceScheduler, eval)
   503  	if err != nil {
   504  		t.Fatalf("err: %v", err)
   505  	}
   506  
   507  	// Ensure a single plan
   508  	if len(h.Plans) != 1 {
   509  		t.Fatalf("bad: %#v", h.Plans)
   510  	}
   511  	plan := h.Plans[0]
   512  
   513  	// Ensure the plan allocated
   514  	var planned []*structs.Allocation
   515  	for _, allocList := range plan.NodeAllocation {
   516  		planned = append(planned, allocList...)
   517  	}
   518  	if len(planned) != 10 {
   519  		t.Fatalf("bad: %#v", plan)
   520  	}
   521  
   522  	// Lookup the allocations by JobID
   523  	ws := memdb.NewWatchSet()
   524  	out, err := h.State.AllocsByJob(ws, job.ID, false)
   525  	noErr(t, err)
   526  
   527  	// Ensure all allocations placed
   528  	if len(out) != 10 {
   529  		t.Fatalf("bad: %#v", out)
   530  	}
   531  
   532  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   533  
   534  	// Ensure the plan had annotations.
   535  	if plan.Annotations == nil {
   536  		t.Fatalf("expected annotations")
   537  	}
   538  
   539  	desiredTGs := plan.Annotations.DesiredTGUpdates
   540  	if l := len(desiredTGs); l != 1 {
   541  		t.Fatalf("incorrect number of task groups; got %v; want %v", l, 1)
   542  	}
   543  
   544  	desiredChanges, ok := desiredTGs["web"]
   545  	if !ok {
   546  		t.Fatalf("expected task group web to have desired changes")
   547  	}
   548  
   549  	expected := &structs.DesiredUpdates{Place: 10}
   550  	if !reflect.DeepEqual(desiredChanges, expected) {
   551  		t.Fatalf("Unexpected desired updates; got %#v; want %#v", desiredChanges, expected)
   552  	}
   553  }
   554  
   555  func TestServiceSched_JobRegister_CountZero(t *testing.T) {
   556  	h := NewHarness(t)
   557  
   558  	// Create some nodes
   559  	for i := 0; i < 10; i++ {
   560  		node := mock.Node()
   561  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   562  	}
   563  
   564  	// Create a job and set the task group count to zero.
   565  	job := mock.Job()
   566  	job.TaskGroups[0].Count = 0
   567  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   568  
   569  	// Create a mock evaluation to register the job
   570  	eval := &structs.Evaluation{
   571  		ID:          structs.GenerateUUID(),
   572  		Priority:    job.Priority,
   573  		TriggeredBy: structs.EvalTriggerJobRegister,
   574  		JobID:       job.ID,
   575  	}
   576  
   577  	// Process the evaluation
   578  	err := h.Process(NewServiceScheduler, eval)
   579  	if err != nil {
   580  		t.Fatalf("err: %v", err)
   581  	}
   582  
   583  	// Ensure there was no plan
   584  	if len(h.Plans) != 0 {
   585  		t.Fatalf("bad: %#v", h.Plans)
   586  	}
   587  
   588  	// Lookup the allocations by JobID
   589  	ws := memdb.NewWatchSet()
   590  	out, err := h.State.AllocsByJob(ws, job.ID, false)
   591  	noErr(t, err)
   592  
   593  	// Ensure no allocations placed
   594  	if len(out) != 0 {
   595  		t.Fatalf("bad: %#v", out)
   596  	}
   597  
   598  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   599  }
   600  
   601  func TestServiceSched_JobRegister_AllocFail(t *testing.T) {
   602  	h := NewHarness(t)
   603  
   604  	// Create NO nodes
   605  	// Create a job
   606  	job := mock.Job()
   607  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   608  
   609  	// Create a mock evaluation to register the job
   610  	eval := &structs.Evaluation{
   611  		ID:          structs.GenerateUUID(),
   612  		Priority:    job.Priority,
   613  		TriggeredBy: structs.EvalTriggerJobRegister,
   614  		JobID:       job.ID,
   615  	}
   616  
   617  	// Process the evaluation
   618  	err := h.Process(NewServiceScheduler, eval)
   619  	if err != nil {
   620  		t.Fatalf("err: %v", err)
   621  	}
   622  
   623  	// Ensure no plan
   624  	if len(h.Plans) != 0 {
   625  		t.Fatalf("bad: %#v", h.Plans)
   626  	}
   627  
   628  	// Ensure there is a follow up eval.
   629  	if len(h.CreateEvals) != 1 || h.CreateEvals[0].Status != structs.EvalStatusBlocked {
   630  		t.Fatalf("bad: %#v", h.CreateEvals)
   631  	}
   632  
   633  	if len(h.Evals) != 1 {
   634  		t.Fatalf("incorrect number of updated eval: %#v", h.Evals)
   635  	}
   636  	outEval := h.Evals[0]
   637  
   638  	// Ensure the eval has its spawned blocked eval
   639  	if outEval.BlockedEval != h.CreateEvals[0].ID {
   640  		t.Fatalf("bad: %#v", outEval)
   641  	}
   642  
   643  	// Ensure the plan failed to alloc
   644  	if outEval == nil || len(outEval.FailedTGAllocs) != 1 {
   645  		t.Fatalf("bad: %#v", outEval)
   646  	}
   647  
   648  	metrics, ok := outEval.FailedTGAllocs[job.TaskGroups[0].Name]
   649  	if !ok {
   650  		t.Fatalf("no failed metrics: %#v", outEval.FailedTGAllocs)
   651  	}
   652  
   653  	// Check the coalesced failures
   654  	if metrics.CoalescedFailures != 9 {
   655  		t.Fatalf("bad: %#v", metrics)
   656  	}
   657  
   658  	// Check the available nodes
   659  	if count, ok := metrics.NodesAvailable["dc1"]; !ok || count != 0 {
   660  		t.Fatalf("bad: %#v", metrics)
   661  	}
   662  
   663  	// Check queued allocations
   664  	queued := outEval.QueuedAllocations["web"]
   665  	if queued != 10 {
   666  		t.Fatalf("expected queued: %v, actual: %v", 10, queued)
   667  	}
   668  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   669  }
   670  
   671  func TestServiceSched_JobRegister_CreateBlockedEval(t *testing.T) {
   672  	h := NewHarness(t)
   673  
   674  	// Create a full node
   675  	node := mock.Node()
   676  	node.Reserved = node.Resources
   677  	node.ComputeClass()
   678  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   679  
   680  	// Create an ineligible node
   681  	node2 := mock.Node()
   682  	node2.Attributes["kernel.name"] = "windows"
   683  	node2.ComputeClass()
   684  	noErr(t, h.State.UpsertNode(h.NextIndex(), node2))
   685  
   686  	// Create a jobs
   687  	job := mock.Job()
   688  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   689  
   690  	// Create a mock evaluation to register the job
   691  	eval := &structs.Evaluation{
   692  		ID:          structs.GenerateUUID(),
   693  		Priority:    job.Priority,
   694  		TriggeredBy: structs.EvalTriggerJobRegister,
   695  		JobID:       job.ID,
   696  	}
   697  
   698  	// Process the evaluation
   699  	err := h.Process(NewServiceScheduler, eval)
   700  	if err != nil {
   701  		t.Fatalf("err: %v", err)
   702  	}
   703  
   704  	// Ensure no plan
   705  	if len(h.Plans) != 0 {
   706  		t.Fatalf("bad: %#v", h.Plans)
   707  	}
   708  
   709  	// Ensure the plan has created a follow up eval.
   710  	if len(h.CreateEvals) != 1 {
   711  		t.Fatalf("bad: %#v", h.CreateEvals)
   712  	}
   713  
   714  	created := h.CreateEvals[0]
   715  	if created.Status != structs.EvalStatusBlocked {
   716  		t.Fatalf("bad: %#v", created)
   717  	}
   718  
   719  	classes := created.ClassEligibility
   720  	if len(classes) != 2 || !classes[node.ComputedClass] || classes[node2.ComputedClass] {
   721  		t.Fatalf("bad: %#v", classes)
   722  	}
   723  
   724  	if created.EscapedComputedClass {
   725  		t.Fatalf("bad: %#v", created)
   726  	}
   727  
   728  	// Ensure there is a follow up eval.
   729  	if len(h.CreateEvals) != 1 || h.CreateEvals[0].Status != structs.EvalStatusBlocked {
   730  		t.Fatalf("bad: %#v", h.CreateEvals)
   731  	}
   732  
   733  	if len(h.Evals) != 1 {
   734  		t.Fatalf("incorrect number of updated eval: %#v", h.Evals)
   735  	}
   736  	outEval := h.Evals[0]
   737  
   738  	// Ensure the plan failed to alloc
   739  	if outEval == nil || len(outEval.FailedTGAllocs) != 1 {
   740  		t.Fatalf("bad: %#v", outEval)
   741  	}
   742  
   743  	metrics, ok := outEval.FailedTGAllocs[job.TaskGroups[0].Name]
   744  	if !ok {
   745  		t.Fatalf("no failed metrics: %#v", outEval.FailedTGAllocs)
   746  	}
   747  
   748  	// Check the coalesced failures
   749  	if metrics.CoalescedFailures != 9 {
   750  		t.Fatalf("bad: %#v", metrics)
   751  	}
   752  
   753  	// Check the available nodes
   754  	if count, ok := metrics.NodesAvailable["dc1"]; !ok || count != 2 {
   755  		t.Fatalf("bad: %#v", metrics)
   756  	}
   757  
   758  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   759  }
   760  
   761  func TestServiceSched_JobRegister_FeasibleAndInfeasibleTG(t *testing.T) {
   762  	h := NewHarness(t)
   763  
   764  	// Create one node
   765  	node := mock.Node()
   766  	node.NodeClass = "class_0"
   767  	noErr(t, node.ComputeClass())
   768  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   769  
   770  	// Create a job that constrains on a node class
   771  	job := mock.Job()
   772  	job.TaskGroups[0].Count = 2
   773  	job.TaskGroups[0].Constraints = append(job.Constraints,
   774  		&structs.Constraint{
   775  			LTarget: "${node.class}",
   776  			RTarget: "class_0",
   777  			Operand: "=",
   778  		},
   779  	)
   780  	tg2 := job.TaskGroups[0].Copy()
   781  	tg2.Name = "web2"
   782  	tg2.Constraints[1].RTarget = "class_1"
   783  	job.TaskGroups = append(job.TaskGroups, tg2)
   784  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   785  
   786  	// Create a mock evaluation to register the job
   787  	eval := &structs.Evaluation{
   788  		ID:          structs.GenerateUUID(),
   789  		Priority:    job.Priority,
   790  		TriggeredBy: structs.EvalTriggerJobRegister,
   791  		JobID:       job.ID,
   792  	}
   793  
   794  	// Process the evaluation
   795  	err := h.Process(NewServiceScheduler, eval)
   796  	if err != nil {
   797  		t.Fatalf("err: %v", err)
   798  	}
   799  
   800  	// Ensure a single plan
   801  	if len(h.Plans) != 1 {
   802  		t.Fatalf("bad: %#v", h.Plans)
   803  	}
   804  	plan := h.Plans[0]
   805  
   806  	// Ensure the plan allocated
   807  	var planned []*structs.Allocation
   808  	for _, allocList := range plan.NodeAllocation {
   809  		planned = append(planned, allocList...)
   810  	}
   811  	if len(planned) != 2 {
   812  		t.Fatalf("bad: %#v", plan)
   813  	}
   814  
   815  	// Ensure two allocations placed
   816  	ws := memdb.NewWatchSet()
   817  	out, err := h.State.AllocsByJob(ws, job.ID, false)
   818  	noErr(t, err)
   819  	if len(out) != 2 {
   820  		t.Fatalf("bad: %#v", out)
   821  	}
   822  
   823  	if len(h.Evals) != 1 {
   824  		t.Fatalf("incorrect number of updated eval: %#v", h.Evals)
   825  	}
   826  	outEval := h.Evals[0]
   827  
   828  	// Ensure the eval has its spawned blocked eval
   829  	if outEval.BlockedEval != h.CreateEvals[0].ID {
   830  		t.Fatalf("bad: %#v", outEval)
   831  	}
   832  
   833  	// Ensure the plan failed to alloc one tg
   834  	if outEval == nil || len(outEval.FailedTGAllocs) != 1 {
   835  		t.Fatalf("bad: %#v", outEval)
   836  	}
   837  
   838  	metrics, ok := outEval.FailedTGAllocs[tg2.Name]
   839  	if !ok {
   840  		t.Fatalf("no failed metrics: %#v", outEval.FailedTGAllocs)
   841  	}
   842  
   843  	// Check the coalesced failures
   844  	if metrics.CoalescedFailures != tg2.Count-1 {
   845  		t.Fatalf("bad: %#v", metrics)
   846  	}
   847  
   848  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   849  }
   850  
   851  // This test just ensures the scheduler handles the eval type to avoid
   852  // regressions.
   853  func TestServiceSched_EvaluateMaxPlanEval(t *testing.T) {
   854  	h := NewHarness(t)
   855  
   856  	// Create a job and set the task group count to zero.
   857  	job := mock.Job()
   858  	job.TaskGroups[0].Count = 0
   859  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   860  
   861  	// Create a mock blocked evaluation
   862  	eval := &structs.Evaluation{
   863  		ID:          structs.GenerateUUID(),
   864  		Status:      structs.EvalStatusBlocked,
   865  		Priority:    job.Priority,
   866  		TriggeredBy: structs.EvalTriggerMaxPlans,
   867  		JobID:       job.ID,
   868  	}
   869  
   870  	// Insert it into the state store
   871  	noErr(t, h.State.UpsertEvals(h.NextIndex(), []*structs.Evaluation{eval}))
   872  
   873  	// Process the evaluation
   874  	err := h.Process(NewServiceScheduler, eval)
   875  	if err != nil {
   876  		t.Fatalf("err: %v", err)
   877  	}
   878  
   879  	// Ensure there was no plan
   880  	if len(h.Plans) != 0 {
   881  		t.Fatalf("bad: %#v", h.Plans)
   882  	}
   883  
   884  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   885  }
   886  
   887  func TestServiceSched_Plan_Partial_Progress(t *testing.T) {
   888  	h := NewHarness(t)
   889  
   890  	// Create a node
   891  	node := mock.Node()
   892  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   893  
   894  	// Create a job with a high resource ask so that all the allocations can't
   895  	// be placed on a single node.
   896  	job := mock.Job()
   897  	job.TaskGroups[0].Count = 3
   898  	job.TaskGroups[0].Tasks[0].Resources.CPU = 3600
   899  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   900  
   901  	// Create a mock evaluation to register the job
   902  	eval := &structs.Evaluation{
   903  		ID:          structs.GenerateUUID(),
   904  		Priority:    job.Priority,
   905  		TriggeredBy: structs.EvalTriggerJobRegister,
   906  		JobID:       job.ID,
   907  	}
   908  
   909  	// Process the evaluation
   910  	err := h.Process(NewServiceScheduler, eval)
   911  	if err != nil {
   912  		t.Fatalf("err: %v", err)
   913  	}
   914  
   915  	// Ensure a single plan
   916  	if len(h.Plans) != 1 {
   917  		t.Fatalf("bad: %#v", h.Plans)
   918  	}
   919  	plan := h.Plans[0]
   920  
   921  	// Ensure the plan doesn't have annotations.
   922  	if plan.Annotations != nil {
   923  		t.Fatalf("expected no annotations")
   924  	}
   925  
   926  	// Ensure the plan allocated
   927  	var planned []*structs.Allocation
   928  	for _, allocList := range plan.NodeAllocation {
   929  		planned = append(planned, allocList...)
   930  	}
   931  	if len(planned) != 1 {
   932  		t.Fatalf("bad: %#v", plan)
   933  	}
   934  
   935  	// Lookup the allocations by JobID
   936  	ws := memdb.NewWatchSet()
   937  	out, err := h.State.AllocsByJob(ws, job.ID, false)
   938  	noErr(t, err)
   939  
   940  	// Ensure only one allocations placed
   941  	if len(out) != 1 {
   942  		t.Fatalf("bad: %#v", out)
   943  	}
   944  
   945  	queued := h.Evals[0].QueuedAllocations["web"]
   946  	if queued != 2 {
   947  		t.Fatalf("expected: %v, actual: %v", 2, queued)
   948  	}
   949  
   950  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   951  }
   952  
   953  func TestServiceSched_EvaluateBlockedEval(t *testing.T) {
   954  	h := NewHarness(t)
   955  
   956  	// Create a job
   957  	job := mock.Job()
   958  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   959  
   960  	// Create a mock blocked evaluation
   961  	eval := &structs.Evaluation{
   962  		ID:          structs.GenerateUUID(),
   963  		Status:      structs.EvalStatusBlocked,
   964  		Priority:    job.Priority,
   965  		TriggeredBy: structs.EvalTriggerJobRegister,
   966  		JobID:       job.ID,
   967  	}
   968  
   969  	// Insert it into the state store
   970  	noErr(t, h.State.UpsertEvals(h.NextIndex(), []*structs.Evaluation{eval}))
   971  
   972  	// Process the evaluation
   973  	err := h.Process(NewServiceScheduler, eval)
   974  	if err != nil {
   975  		t.Fatalf("err: %v", err)
   976  	}
   977  
   978  	// Ensure there was no plan
   979  	if len(h.Plans) != 0 {
   980  		t.Fatalf("bad: %#v", h.Plans)
   981  	}
   982  
   983  	// Ensure that the eval was reblocked
   984  	if len(h.ReblockEvals) != 1 {
   985  		t.Fatalf("bad: %#v", h.ReblockEvals)
   986  	}
   987  	if h.ReblockEvals[0].ID != eval.ID {
   988  		t.Fatalf("expect same eval to be reblocked; got %q; want %q", h.ReblockEvals[0].ID, eval.ID)
   989  	}
   990  
   991  	// Ensure the eval status was not updated
   992  	if len(h.Evals) != 0 {
   993  		t.Fatalf("Existing eval should not have status set")
   994  	}
   995  }
   996  
   997  func TestServiceSched_EvaluateBlockedEval_Finished(t *testing.T) {
   998  	h := NewHarness(t)
   999  
  1000  	// Create some nodes
  1001  	for i := 0; i < 10; i++ {
  1002  		node := mock.Node()
  1003  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1004  	}
  1005  
  1006  	// Create a job and set the task group count to zero.
  1007  	job := mock.Job()
  1008  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1009  
  1010  	// Create a mock blocked evaluation
  1011  	eval := &structs.Evaluation{
  1012  		ID:          structs.GenerateUUID(),
  1013  		Status:      structs.EvalStatusBlocked,
  1014  		Priority:    job.Priority,
  1015  		TriggeredBy: structs.EvalTriggerJobRegister,
  1016  		JobID:       job.ID,
  1017  	}
  1018  
  1019  	// Insert it into the state store
  1020  	noErr(t, h.State.UpsertEvals(h.NextIndex(), []*structs.Evaluation{eval}))
  1021  
  1022  	// Process the evaluation
  1023  	err := h.Process(NewServiceScheduler, eval)
  1024  	if err != nil {
  1025  		t.Fatalf("err: %v", err)
  1026  	}
  1027  
  1028  	// Ensure a single plan
  1029  	if len(h.Plans) != 1 {
  1030  		t.Fatalf("bad: %#v", h.Plans)
  1031  	}
  1032  	plan := h.Plans[0]
  1033  
  1034  	// Ensure the plan doesn't have annotations.
  1035  	if plan.Annotations != nil {
  1036  		t.Fatalf("expected no annotations")
  1037  	}
  1038  
  1039  	// Ensure the eval has no spawned blocked eval
  1040  	if len(h.Evals) != 1 {
  1041  		t.Fatalf("bad: %#v", h.Evals)
  1042  		if h.Evals[0].BlockedEval != "" {
  1043  			t.Fatalf("bad: %#v", h.Evals[0])
  1044  		}
  1045  	}
  1046  
  1047  	// Ensure the plan allocated
  1048  	var planned []*structs.Allocation
  1049  	for _, allocList := range plan.NodeAllocation {
  1050  		planned = append(planned, allocList...)
  1051  	}
  1052  	if len(planned) != 10 {
  1053  		t.Fatalf("bad: %#v", plan)
  1054  	}
  1055  
  1056  	// Lookup the allocations by JobID
  1057  	ws := memdb.NewWatchSet()
  1058  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  1059  	noErr(t, err)
  1060  
  1061  	// Ensure all allocations placed
  1062  	if len(out) != 10 {
  1063  		t.Fatalf("bad: %#v", out)
  1064  	}
  1065  
  1066  	// Ensure the eval was not reblocked
  1067  	if len(h.ReblockEvals) != 0 {
  1068  		t.Fatalf("Existing eval should not have been reblocked as it placed all allocations")
  1069  	}
  1070  
  1071  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1072  
  1073  	// Ensure queued allocations is zero
  1074  	queued := h.Evals[0].QueuedAllocations["web"]
  1075  	if queued != 0 {
  1076  		t.Fatalf("expected queued: %v, actual: %v", 0, queued)
  1077  	}
  1078  }
  1079  
  1080  func TestServiceSched_JobModify(t *testing.T) {
  1081  	h := NewHarness(t)
  1082  
  1083  	// Create some nodes
  1084  	var nodes []*structs.Node
  1085  	for i := 0; i < 10; i++ {
  1086  		node := mock.Node()
  1087  		nodes = append(nodes, node)
  1088  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1089  	}
  1090  
  1091  	// Generate a fake job with allocations
  1092  	job := mock.Job()
  1093  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1094  
  1095  	var allocs []*structs.Allocation
  1096  	for i := 0; i < 10; i++ {
  1097  		alloc := mock.Alloc()
  1098  		alloc.Job = job
  1099  		alloc.JobID = job.ID
  1100  		alloc.NodeID = nodes[i].ID
  1101  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1102  		allocs = append(allocs, alloc)
  1103  	}
  1104  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1105  
  1106  	// Add a few terminal status allocations, these should be ignored
  1107  	var terminal []*structs.Allocation
  1108  	for i := 0; i < 5; i++ {
  1109  		alloc := mock.Alloc()
  1110  		alloc.Job = job
  1111  		alloc.JobID = job.ID
  1112  		alloc.NodeID = nodes[i].ID
  1113  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1114  		alloc.DesiredStatus = structs.AllocDesiredStatusStop
  1115  		terminal = append(terminal, alloc)
  1116  	}
  1117  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), terminal))
  1118  
  1119  	// Update the job
  1120  	job2 := mock.Job()
  1121  	job2.ID = job.ID
  1122  
  1123  	// Update the task, such that it cannot be done in-place
  1124  	job2.TaskGroups[0].Tasks[0].Config["command"] = "/bin/other"
  1125  	noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
  1126  
  1127  	// Create a mock evaluation to deal with drain
  1128  	eval := &structs.Evaluation{
  1129  		ID:          structs.GenerateUUID(),
  1130  		Priority:    50,
  1131  		TriggeredBy: structs.EvalTriggerJobRegister,
  1132  		JobID:       job.ID,
  1133  	}
  1134  
  1135  	// Process the evaluation
  1136  	err := h.Process(NewServiceScheduler, eval)
  1137  	if err != nil {
  1138  		t.Fatalf("err: %v", err)
  1139  	}
  1140  
  1141  	// Ensure a single plan
  1142  	if len(h.Plans) != 1 {
  1143  		t.Fatalf("bad: %#v", h.Plans)
  1144  	}
  1145  	plan := h.Plans[0]
  1146  
  1147  	// Ensure the plan evicted all allocs
  1148  	var update []*structs.Allocation
  1149  	for _, updateList := range plan.NodeUpdate {
  1150  		update = append(update, updateList...)
  1151  	}
  1152  	if len(update) != len(allocs) {
  1153  		t.Fatalf("bad: %#v", plan)
  1154  	}
  1155  
  1156  	// Ensure the plan allocated
  1157  	var planned []*structs.Allocation
  1158  	for _, allocList := range plan.NodeAllocation {
  1159  		planned = append(planned, allocList...)
  1160  	}
  1161  	if len(planned) != 10 {
  1162  		t.Fatalf("bad: %#v", plan)
  1163  	}
  1164  
  1165  	// Lookup the allocations by JobID
  1166  	ws := memdb.NewWatchSet()
  1167  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  1168  	noErr(t, err)
  1169  
  1170  	// Ensure all allocations placed
  1171  	out, _ = structs.FilterTerminalAllocs(out)
  1172  	if len(out) != 10 {
  1173  		t.Fatalf("bad: %#v", out)
  1174  	}
  1175  
  1176  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1177  }
  1178  
  1179  // Have a single node and submit a job. Increment the count such that all fit
  1180  // on the node but the node doesn't have enough resources to fit the new count +
  1181  // 1. This tests that we properly discount the resources of existing allocs.
  1182  func TestServiceSched_JobModify_IncrCount_NodeLimit(t *testing.T) {
  1183  	h := NewHarness(t)
  1184  
  1185  	// Create one node
  1186  	node := mock.Node()
  1187  	node.Resources.CPU = 1000
  1188  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1189  
  1190  	// Generate a fake job with one allocation
  1191  	job := mock.Job()
  1192  	job.TaskGroups[0].Tasks[0].Resources.CPU = 256
  1193  	job2 := job.Copy()
  1194  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1195  
  1196  	var allocs []*structs.Allocation
  1197  	alloc := mock.Alloc()
  1198  	alloc.Job = job
  1199  	alloc.JobID = job.ID
  1200  	alloc.NodeID = node.ID
  1201  	alloc.Name = "my-job.web[0]"
  1202  	alloc.Resources.CPU = 256
  1203  	allocs = append(allocs, alloc)
  1204  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1205  
  1206  	// Update the job to count 3
  1207  	job2.TaskGroups[0].Count = 3
  1208  	noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
  1209  
  1210  	// Create a mock evaluation to deal with drain
  1211  	eval := &structs.Evaluation{
  1212  		ID:          structs.GenerateUUID(),
  1213  		Priority:    50,
  1214  		TriggeredBy: structs.EvalTriggerJobRegister,
  1215  		JobID:       job.ID,
  1216  	}
  1217  
  1218  	// Process the evaluation
  1219  	err := h.Process(NewServiceScheduler, eval)
  1220  	if err != nil {
  1221  		t.Fatalf("err: %v", err)
  1222  	}
  1223  
  1224  	// Ensure a single plan
  1225  	if len(h.Plans) != 1 {
  1226  		t.Fatalf("bad: %#v", h.Plans)
  1227  	}
  1228  	plan := h.Plans[0]
  1229  
  1230  	// Ensure the plan didn't evicted the alloc
  1231  	var update []*structs.Allocation
  1232  	for _, updateList := range plan.NodeUpdate {
  1233  		update = append(update, updateList...)
  1234  	}
  1235  	if len(update) != 0 {
  1236  		t.Fatalf("bad: %#v", plan)
  1237  	}
  1238  
  1239  	// Ensure the plan allocated
  1240  	var planned []*structs.Allocation
  1241  	for _, allocList := range plan.NodeAllocation {
  1242  		planned = append(planned, allocList...)
  1243  	}
  1244  	if len(planned) != 3 {
  1245  		t.Fatalf("bad: %#v", plan)
  1246  	}
  1247  
  1248  	// Ensure the plan had no failures
  1249  	if len(h.Evals) != 1 {
  1250  		t.Fatalf("incorrect number of updated eval: %#v", h.Evals)
  1251  	}
  1252  	outEval := h.Evals[0]
  1253  	if outEval == nil || len(outEval.FailedTGAllocs) != 0 {
  1254  		t.Fatalf("bad: %#v", outEval)
  1255  	}
  1256  
  1257  	// Lookup the allocations by JobID
  1258  	ws := memdb.NewWatchSet()
  1259  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  1260  	noErr(t, err)
  1261  
  1262  	// Ensure all allocations placed
  1263  	out, _ = structs.FilterTerminalAllocs(out)
  1264  	if len(out) != 3 {
  1265  		t.Fatalf("bad: %#v", out)
  1266  	}
  1267  
  1268  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1269  }
  1270  
  1271  func TestServiceSched_JobModify_CountZero(t *testing.T) {
  1272  	h := NewHarness(t)
  1273  
  1274  	// Create some nodes
  1275  	var nodes []*structs.Node
  1276  	for i := 0; i < 10; i++ {
  1277  		node := mock.Node()
  1278  		nodes = append(nodes, node)
  1279  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1280  	}
  1281  
  1282  	// Generate a fake job with allocations
  1283  	job := mock.Job()
  1284  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1285  
  1286  	var allocs []*structs.Allocation
  1287  	for i := 0; i < 10; i++ {
  1288  		alloc := mock.Alloc()
  1289  		alloc.Job = job
  1290  		alloc.JobID = job.ID
  1291  		alloc.NodeID = nodes[i].ID
  1292  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1293  		allocs = append(allocs, alloc)
  1294  	}
  1295  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1296  
  1297  	// Add a few terminal status allocations, these should be ignored
  1298  	var terminal []*structs.Allocation
  1299  	for i := 0; i < 5; i++ {
  1300  		alloc := mock.Alloc()
  1301  		alloc.Job = job
  1302  		alloc.JobID = job.ID
  1303  		alloc.NodeID = nodes[i].ID
  1304  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1305  		alloc.DesiredStatus = structs.AllocDesiredStatusStop
  1306  		terminal = append(terminal, alloc)
  1307  	}
  1308  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), terminal))
  1309  
  1310  	// Update the job to be count zero
  1311  	job2 := mock.Job()
  1312  	job2.ID = job.ID
  1313  	job2.TaskGroups[0].Count = 0
  1314  	noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
  1315  
  1316  	// Create a mock evaluation to deal with drain
  1317  	eval := &structs.Evaluation{
  1318  		ID:          structs.GenerateUUID(),
  1319  		Priority:    50,
  1320  		TriggeredBy: structs.EvalTriggerJobRegister,
  1321  		JobID:       job.ID,
  1322  	}
  1323  
  1324  	// Process the evaluation
  1325  	err := h.Process(NewServiceScheduler, eval)
  1326  	if err != nil {
  1327  		t.Fatalf("err: %v", err)
  1328  	}
  1329  
  1330  	// Ensure a single plan
  1331  	if len(h.Plans) != 1 {
  1332  		t.Fatalf("bad: %#v", h.Plans)
  1333  	}
  1334  	plan := h.Plans[0]
  1335  
  1336  	// Ensure the plan evicted all allocs
  1337  	var update []*structs.Allocation
  1338  	for _, updateList := range plan.NodeUpdate {
  1339  		update = append(update, updateList...)
  1340  	}
  1341  	if len(update) != len(allocs) {
  1342  		t.Fatalf("bad: %#v", plan)
  1343  	}
  1344  
  1345  	// Ensure the plan didn't allocated
  1346  	var planned []*structs.Allocation
  1347  	for _, allocList := range plan.NodeAllocation {
  1348  		planned = append(planned, allocList...)
  1349  	}
  1350  	if len(planned) != 0 {
  1351  		t.Fatalf("bad: %#v", plan)
  1352  	}
  1353  
  1354  	// Lookup the allocations by JobID
  1355  	ws := memdb.NewWatchSet()
  1356  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  1357  	noErr(t, err)
  1358  
  1359  	// Ensure all allocations placed
  1360  	out, _ = structs.FilterTerminalAllocs(out)
  1361  	if len(out) != 0 {
  1362  		t.Fatalf("bad: %#v", out)
  1363  	}
  1364  
  1365  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1366  }
  1367  
  1368  func TestServiceSched_JobModify_Rolling(t *testing.T) {
  1369  	h := NewHarness(t)
  1370  
  1371  	// Create some nodes
  1372  	var nodes []*structs.Node
  1373  	for i := 0; i < 10; i++ {
  1374  		node := mock.Node()
  1375  		nodes = append(nodes, node)
  1376  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1377  	}
  1378  
  1379  	// Generate a fake job with allocations
  1380  	job := mock.Job()
  1381  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1382  
  1383  	var allocs []*structs.Allocation
  1384  	for i := 0; i < 10; i++ {
  1385  		alloc := mock.Alloc()
  1386  		alloc.Job = job
  1387  		alloc.JobID = job.ID
  1388  		alloc.NodeID = nodes[i].ID
  1389  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1390  		allocs = append(allocs, alloc)
  1391  	}
  1392  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1393  
  1394  	// Update the job
  1395  	job2 := mock.Job()
  1396  	job2.ID = job.ID
  1397  	job2.Update = structs.UpdateStrategy{
  1398  		Stagger:     30 * time.Second,
  1399  		MaxParallel: 5,
  1400  	}
  1401  
  1402  	// Update the task, such that it cannot be done in-place
  1403  	job2.TaskGroups[0].Tasks[0].Config["command"] = "/bin/other"
  1404  	noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
  1405  
  1406  	// Create a mock evaluation to deal with drain
  1407  	eval := &structs.Evaluation{
  1408  		ID:          structs.GenerateUUID(),
  1409  		Priority:    50,
  1410  		TriggeredBy: structs.EvalTriggerJobRegister,
  1411  		JobID:       job.ID,
  1412  	}
  1413  
  1414  	// Process the evaluation
  1415  	err := h.Process(NewServiceScheduler, eval)
  1416  	if err != nil {
  1417  		t.Fatalf("err: %v", err)
  1418  	}
  1419  
  1420  	// Ensure a single plan
  1421  	if len(h.Plans) != 1 {
  1422  		t.Fatalf("bad: %#v", h.Plans)
  1423  	}
  1424  	plan := h.Plans[0]
  1425  
  1426  	// Ensure the plan evicted only MaxParallel
  1427  	var update []*structs.Allocation
  1428  	for _, updateList := range plan.NodeUpdate {
  1429  		update = append(update, updateList...)
  1430  	}
  1431  	if len(update) != job2.Update.MaxParallel {
  1432  		t.Fatalf("bad: %#v", plan)
  1433  	}
  1434  
  1435  	// Ensure the plan allocated
  1436  	var planned []*structs.Allocation
  1437  	for _, allocList := range plan.NodeAllocation {
  1438  		planned = append(planned, allocList...)
  1439  	}
  1440  	if len(planned) != job2.Update.MaxParallel {
  1441  		t.Fatalf("bad: %#v", plan)
  1442  	}
  1443  
  1444  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1445  
  1446  	// Ensure a follow up eval was created
  1447  	eval = h.Evals[0]
  1448  	if eval.NextEval == "" {
  1449  		t.Fatalf("missing next eval")
  1450  	}
  1451  
  1452  	// Check for create
  1453  	if len(h.CreateEvals) == 0 {
  1454  		t.Fatalf("missing created eval")
  1455  	}
  1456  	create := h.CreateEvals[0]
  1457  	if eval.NextEval != create.ID {
  1458  		t.Fatalf("ID mismatch")
  1459  	}
  1460  	if create.PreviousEval != eval.ID {
  1461  		t.Fatalf("missing previous eval")
  1462  	}
  1463  
  1464  	if create.TriggeredBy != structs.EvalTriggerRollingUpdate {
  1465  		t.Fatalf("bad: %#v", create)
  1466  	}
  1467  }
  1468  
  1469  func TestServiceSched_JobModify_InPlace(t *testing.T) {
  1470  	h := NewHarness(t)
  1471  
  1472  	// Create some nodes
  1473  	var nodes []*structs.Node
  1474  	for i := 0; i < 10; i++ {
  1475  		node := mock.Node()
  1476  		nodes = append(nodes, node)
  1477  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1478  	}
  1479  
  1480  	// Generate a fake job with allocations
  1481  	job := mock.Job()
  1482  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1483  
  1484  	var allocs []*structs.Allocation
  1485  	for i := 0; i < 10; i++ {
  1486  		alloc := mock.Alloc()
  1487  		alloc.Job = job
  1488  		alloc.JobID = job.ID
  1489  		alloc.NodeID = nodes[i].ID
  1490  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1491  		allocs = append(allocs, alloc)
  1492  	}
  1493  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1494  
  1495  	// Update the job
  1496  	job2 := mock.Job()
  1497  	job2.ID = job.ID
  1498  	noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
  1499  
  1500  	// Create a mock evaluation to deal with drain
  1501  	eval := &structs.Evaluation{
  1502  		ID:          structs.GenerateUUID(),
  1503  		Priority:    50,
  1504  		TriggeredBy: structs.EvalTriggerJobRegister,
  1505  		JobID:       job.ID,
  1506  	}
  1507  
  1508  	// Process the evaluation
  1509  	err := h.Process(NewServiceScheduler, eval)
  1510  	if err != nil {
  1511  		t.Fatalf("err: %v", err)
  1512  	}
  1513  
  1514  	// Ensure a single plan
  1515  	if len(h.Plans) != 1 {
  1516  		t.Fatalf("bad: %#v", h.Plans)
  1517  	}
  1518  	plan := h.Plans[0]
  1519  
  1520  	// Ensure the plan did not evict any allocs
  1521  	var update []*structs.Allocation
  1522  	for _, updateList := range plan.NodeUpdate {
  1523  		update = append(update, updateList...)
  1524  	}
  1525  	if len(update) != 0 {
  1526  		t.Fatalf("bad: %#v", plan)
  1527  	}
  1528  
  1529  	// Ensure the plan updated the existing allocs
  1530  	var planned []*structs.Allocation
  1531  	for _, allocList := range plan.NodeAllocation {
  1532  		planned = append(planned, allocList...)
  1533  	}
  1534  	if len(planned) != 10 {
  1535  		t.Fatalf("bad: %#v", plan)
  1536  	}
  1537  	for _, p := range planned {
  1538  		if p.Job != job2 {
  1539  			t.Fatalf("should update job")
  1540  		}
  1541  	}
  1542  
  1543  	// Lookup the allocations by JobID
  1544  	ws := memdb.NewWatchSet()
  1545  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  1546  	noErr(t, err)
  1547  
  1548  	// Ensure all allocations placed
  1549  	if len(out) != 10 {
  1550  		for _, alloc := range out {
  1551  			t.Logf("%#v", alloc)
  1552  		}
  1553  		t.Fatalf("bad: %#v", out)
  1554  	}
  1555  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1556  
  1557  	// Verify the network did not change
  1558  	rp := structs.Port{Label: "main", Value: 5000}
  1559  	for _, alloc := range out {
  1560  		for _, resources := range alloc.TaskResources {
  1561  			if resources.Networks[0].ReservedPorts[0] != rp {
  1562  				t.Fatalf("bad: %#v", alloc)
  1563  			}
  1564  		}
  1565  	}
  1566  }
  1567  
  1568  func TestServiceSched_JobModify_DistinctProperty(t *testing.T) {
  1569  	h := NewHarness(t)
  1570  
  1571  	// Create some nodes
  1572  	var nodes []*structs.Node
  1573  	for i := 0; i < 10; i++ {
  1574  		node := mock.Node()
  1575  		node.Meta["rack"] = fmt.Sprintf("rack%d", i)
  1576  		nodes = append(nodes, node)
  1577  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1578  	}
  1579  
  1580  	// Create a job that uses distinct property and has count higher than what is
  1581  	// possible.
  1582  	job := mock.Job()
  1583  	job.TaskGroups[0].Count = 11
  1584  	job.Constraints = append(job.Constraints,
  1585  		&structs.Constraint{
  1586  			Operand: structs.ConstraintDistinctProperty,
  1587  			LTarget: "${meta.rack}",
  1588  		})
  1589  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1590  
  1591  	oldJob := job.Copy()
  1592  	oldJob.JobModifyIndex -= 1
  1593  	oldJob.TaskGroups[0].Count = 4
  1594  
  1595  	// Place 4 of 10
  1596  	var allocs []*structs.Allocation
  1597  	for i := 0; i < 4; i++ {
  1598  		alloc := mock.Alloc()
  1599  		alloc.Job = oldJob
  1600  		alloc.JobID = job.ID
  1601  		alloc.NodeID = nodes[i].ID
  1602  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1603  		allocs = append(allocs, alloc)
  1604  	}
  1605  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1606  
  1607  	// Create a mock evaluation to register the job
  1608  	eval := &structs.Evaluation{
  1609  		ID:          structs.GenerateUUID(),
  1610  		Priority:    job.Priority,
  1611  		TriggeredBy: structs.EvalTriggerJobRegister,
  1612  		JobID:       job.ID,
  1613  	}
  1614  
  1615  	// Process the evaluation
  1616  	err := h.Process(NewServiceScheduler, eval)
  1617  	if err != nil {
  1618  		t.Fatalf("err: %v", err)
  1619  	}
  1620  
  1621  	// Ensure a single plan
  1622  	if len(h.Plans) != 1 {
  1623  		t.Fatalf("bad: %#v", h.Plans)
  1624  	}
  1625  	plan := h.Plans[0]
  1626  
  1627  	// Ensure the plan doesn't have annotations.
  1628  	if plan.Annotations != nil {
  1629  		t.Fatalf("expected no annotations")
  1630  	}
  1631  
  1632  	// Ensure the eval hasn't spawned blocked eval
  1633  	if len(h.CreateEvals) != 1 {
  1634  		t.Fatalf("bad: %#v", h.CreateEvals)
  1635  	}
  1636  
  1637  	// Ensure the plan failed to alloc
  1638  	outEval := h.Evals[0]
  1639  	if len(outEval.FailedTGAllocs) != 1 {
  1640  		t.Fatalf("bad: %+v", outEval)
  1641  	}
  1642  
  1643  	// Ensure the plan allocated
  1644  	var planned []*structs.Allocation
  1645  	for _, allocList := range plan.NodeAllocation {
  1646  		planned = append(planned, allocList...)
  1647  	}
  1648  	if len(planned) != 10 {
  1649  		t.Fatalf("bad: %#v", planned)
  1650  	}
  1651  
  1652  	// Lookup the allocations by JobID
  1653  	ws := memdb.NewWatchSet()
  1654  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  1655  	noErr(t, err)
  1656  
  1657  	// Ensure all allocations placed
  1658  	if len(out) != 10 {
  1659  		t.Fatalf("bad: %#v", out)
  1660  	}
  1661  
  1662  	// Ensure different node was used per.
  1663  	used := make(map[string]struct{})
  1664  	for _, alloc := range out {
  1665  		if _, ok := used[alloc.NodeID]; ok {
  1666  			t.Fatalf("Node collision %v", alloc.NodeID)
  1667  		}
  1668  		used[alloc.NodeID] = struct{}{}
  1669  	}
  1670  
  1671  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1672  }
  1673  
  1674  func TestServiceSched_JobDeregister(t *testing.T) {
  1675  	h := NewHarness(t)
  1676  
  1677  	// Generate a fake job with allocations
  1678  	job := mock.Job()
  1679  
  1680  	var allocs []*structs.Allocation
  1681  	for i := 0; i < 10; i++ {
  1682  		alloc := mock.Alloc()
  1683  		alloc.Job = job
  1684  		alloc.JobID = job.ID
  1685  		allocs = append(allocs, alloc)
  1686  	}
  1687  	for _, alloc := range allocs {
  1688  		h.State.UpsertJobSummary(h.NextIndex(), mock.JobSummary(alloc.JobID))
  1689  	}
  1690  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1691  
  1692  	// Create a mock evaluation to deregister the job
  1693  	eval := &structs.Evaluation{
  1694  		ID:          structs.GenerateUUID(),
  1695  		Priority:    50,
  1696  		TriggeredBy: structs.EvalTriggerJobDeregister,
  1697  		JobID:       job.ID,
  1698  	}
  1699  
  1700  	// Process the evaluation
  1701  	err := h.Process(NewServiceScheduler, eval)
  1702  	if err != nil {
  1703  		t.Fatalf("err: %v", err)
  1704  	}
  1705  
  1706  	// Ensure a single plan
  1707  	if len(h.Plans) != 1 {
  1708  		t.Fatalf("bad: %#v", h.Plans)
  1709  	}
  1710  	plan := h.Plans[0]
  1711  
  1712  	// Ensure the plan evicted all nodes
  1713  	if len(plan.NodeUpdate["12345678-abcd-efab-cdef-123456789abc"]) != len(allocs) {
  1714  		t.Fatalf("bad: %#v", plan)
  1715  	}
  1716  
  1717  	// Lookup the allocations by JobID
  1718  	ws := memdb.NewWatchSet()
  1719  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  1720  	noErr(t, err)
  1721  
  1722  	// Ensure that the job field on the allocation is still populated
  1723  	for _, alloc := range out {
  1724  		if alloc.Job == nil {
  1725  			t.Fatalf("bad: %#v", alloc)
  1726  		}
  1727  	}
  1728  
  1729  	// Ensure no remaining allocations
  1730  	out, _ = structs.FilterTerminalAllocs(out)
  1731  	if len(out) != 0 {
  1732  		t.Fatalf("bad: %#v", out)
  1733  	}
  1734  
  1735  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1736  }
  1737  
  1738  func TestServiceSched_NodeDown(t *testing.T) {
  1739  	h := NewHarness(t)
  1740  
  1741  	// Register a node
  1742  	node := mock.Node()
  1743  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1744  
  1745  	// Generate a fake job with allocations and an update policy.
  1746  	job := mock.Job()
  1747  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1748  
  1749  	var allocs []*structs.Allocation
  1750  	for i := 0; i < 10; i++ {
  1751  		alloc := mock.Alloc()
  1752  		alloc.Job = job
  1753  		alloc.JobID = job.ID
  1754  		alloc.NodeID = node.ID
  1755  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1756  		allocs = append(allocs, alloc)
  1757  	}
  1758  
  1759  	// Cover each terminal case and ensure it doesn't change to lost
  1760  	allocs[7].DesiredStatus = structs.AllocDesiredStatusRun
  1761  	allocs[7].ClientStatus = structs.AllocClientStatusLost
  1762  	allocs[8].DesiredStatus = structs.AllocDesiredStatusRun
  1763  	allocs[8].ClientStatus = structs.AllocClientStatusFailed
  1764  	allocs[9].DesiredStatus = structs.AllocDesiredStatusRun
  1765  	allocs[9].ClientStatus = structs.AllocClientStatusComplete
  1766  
  1767  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1768  
  1769  	// Mark some allocs as running
  1770  	ws := memdb.NewWatchSet()
  1771  	for i := 0; i < 4; i++ {
  1772  		out, _ := h.State.AllocByID(ws, allocs[i].ID)
  1773  		out.ClientStatus = structs.AllocClientStatusRunning
  1774  		noErr(t, h.State.UpdateAllocsFromClient(h.NextIndex(), []*structs.Allocation{out}))
  1775  	}
  1776  
  1777  	// Mark the node as down
  1778  	noErr(t, h.State.UpdateNodeStatus(h.NextIndex(), node.ID, structs.NodeStatusDown))
  1779  
  1780  	// Create a mock evaluation to deal with drain
  1781  	eval := &structs.Evaluation{
  1782  		ID:          structs.GenerateUUID(),
  1783  		Priority:    50,
  1784  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  1785  		JobID:       job.ID,
  1786  		NodeID:      node.ID,
  1787  	}
  1788  
  1789  	// Process the evaluation
  1790  	err := h.Process(NewServiceScheduler, eval)
  1791  	if err != nil {
  1792  		t.Fatalf("err: %v", err)
  1793  	}
  1794  
  1795  	// Ensure a single plan
  1796  	if len(h.Plans) != 1 {
  1797  		t.Fatalf("bad: %#v", h.Plans)
  1798  	}
  1799  	plan := h.Plans[0]
  1800  
  1801  	// Test the scheduler marked all non-terminal allocations as lost
  1802  	if len(plan.NodeUpdate[node.ID]) != 7 {
  1803  		t.Fatalf("bad: %#v", plan)
  1804  	}
  1805  
  1806  	for _, out := range plan.NodeUpdate[node.ID] {
  1807  		if out.ClientStatus != structs.AllocClientStatusLost && out.DesiredStatus != structs.AllocDesiredStatusStop {
  1808  			t.Fatalf("bad alloc: %#v", out)
  1809  		}
  1810  	}
  1811  
  1812  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1813  }
  1814  
  1815  func TestServiceSched_NodeUpdate(t *testing.T) {
  1816  	h := NewHarness(t)
  1817  
  1818  	// Register a node
  1819  	node := mock.Node()
  1820  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1821  
  1822  	// Generate a fake job with allocations and an update policy.
  1823  	job := mock.Job()
  1824  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1825  
  1826  	var allocs []*structs.Allocation
  1827  	for i := 0; i < 10; i++ {
  1828  		alloc := mock.Alloc()
  1829  		alloc.Job = job
  1830  		alloc.JobID = job.ID
  1831  		alloc.NodeID = node.ID
  1832  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1833  		allocs = append(allocs, alloc)
  1834  	}
  1835  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1836  
  1837  	// Mark some allocs as running
  1838  	ws := memdb.NewWatchSet()
  1839  	for i := 0; i < 4; i++ {
  1840  		out, _ := h.State.AllocByID(ws, allocs[i].ID)
  1841  		out.ClientStatus = structs.AllocClientStatusRunning
  1842  		noErr(t, h.State.UpdateAllocsFromClient(h.NextIndex(), []*structs.Allocation{out}))
  1843  	}
  1844  
  1845  	// Create a mock evaluation which won't trigger any new placements
  1846  	eval := &structs.Evaluation{
  1847  		ID:          structs.GenerateUUID(),
  1848  		Priority:    50,
  1849  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  1850  		JobID:       job.ID,
  1851  		NodeID:      node.ID,
  1852  	}
  1853  
  1854  	// Process the evaluation
  1855  	err := h.Process(NewServiceScheduler, eval)
  1856  	if err != nil {
  1857  		t.Fatalf("err: %v", err)
  1858  	}
  1859  	if val, ok := h.Evals[0].QueuedAllocations["web"]; !ok || val != 0 {
  1860  		t.Fatalf("bad queued allocations: %v", h.Evals[0].QueuedAllocations)
  1861  	}
  1862  
  1863  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1864  }
  1865  
  1866  func TestServiceSched_NodeDrain(t *testing.T) {
  1867  	h := NewHarness(t)
  1868  
  1869  	// Register a draining node
  1870  	node := mock.Node()
  1871  	node.Drain = true
  1872  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1873  
  1874  	// Create some nodes
  1875  	for i := 0; i < 10; i++ {
  1876  		node := mock.Node()
  1877  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1878  	}
  1879  
  1880  	// Generate a fake job with allocations and an update policy.
  1881  	job := mock.Job()
  1882  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1883  
  1884  	var allocs []*structs.Allocation
  1885  	for i := 0; i < 10; i++ {
  1886  		alloc := mock.Alloc()
  1887  		alloc.Job = job
  1888  		alloc.JobID = job.ID
  1889  		alloc.NodeID = node.ID
  1890  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1891  		allocs = append(allocs, alloc)
  1892  	}
  1893  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1894  
  1895  	// Create a mock evaluation to deal with drain
  1896  	eval := &structs.Evaluation{
  1897  		ID:          structs.GenerateUUID(),
  1898  		Priority:    50,
  1899  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  1900  		JobID:       job.ID,
  1901  		NodeID:      node.ID,
  1902  	}
  1903  
  1904  	// Process the evaluation
  1905  	err := h.Process(NewServiceScheduler, eval)
  1906  	if err != nil {
  1907  		t.Fatalf("err: %v", err)
  1908  	}
  1909  
  1910  	// Ensure a single plan
  1911  	if len(h.Plans) != 1 {
  1912  		t.Fatalf("bad: %#v", h.Plans)
  1913  	}
  1914  	plan := h.Plans[0]
  1915  
  1916  	// Ensure the plan evicted all allocs
  1917  	if len(plan.NodeUpdate[node.ID]) != len(allocs) {
  1918  		t.Fatalf("bad: %#v", plan)
  1919  	}
  1920  
  1921  	// Ensure the plan allocated
  1922  	var planned []*structs.Allocation
  1923  	for _, allocList := range plan.NodeAllocation {
  1924  		planned = append(planned, allocList...)
  1925  	}
  1926  	if len(planned) != 10 {
  1927  		t.Fatalf("bad: %#v", plan)
  1928  	}
  1929  
  1930  	// Lookup the allocations by JobID
  1931  	ws := memdb.NewWatchSet()
  1932  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  1933  	noErr(t, err)
  1934  
  1935  	// Ensure all allocations placed
  1936  	out, _ = structs.FilterTerminalAllocs(out)
  1937  	if len(out) != 10 {
  1938  		t.Fatalf("bad: %#v", out)
  1939  	}
  1940  
  1941  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1942  }
  1943  
  1944  func TestServiceSched_NodeDrain_Down(t *testing.T) {
  1945  	h := NewHarness(t)
  1946  
  1947  	// Register a draining node
  1948  	node := mock.Node()
  1949  	node.Drain = true
  1950  	node.Status = structs.NodeStatusDown
  1951  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1952  
  1953  	// Generate a fake job with allocations
  1954  	job := mock.Job()
  1955  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1956  
  1957  	var allocs []*structs.Allocation
  1958  	for i := 0; i < 10; i++ {
  1959  		alloc := mock.Alloc()
  1960  		alloc.Job = job
  1961  		alloc.JobID = job.ID
  1962  		alloc.NodeID = node.ID
  1963  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  1964  		allocs = append(allocs, alloc)
  1965  	}
  1966  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  1967  
  1968  	// Set the desired state of the allocs to stop
  1969  	var stop []*structs.Allocation
  1970  	for i := 0; i < 10; i++ {
  1971  		newAlloc := allocs[i].Copy()
  1972  		newAlloc.ClientStatus = structs.AllocDesiredStatusStop
  1973  		stop = append(stop, newAlloc)
  1974  	}
  1975  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), stop))
  1976  
  1977  	// Mark some of the allocations as running
  1978  	var running []*structs.Allocation
  1979  	for i := 4; i < 6; i++ {
  1980  		newAlloc := stop[i].Copy()
  1981  		newAlloc.ClientStatus = structs.AllocClientStatusRunning
  1982  		running = append(running, newAlloc)
  1983  	}
  1984  	noErr(t, h.State.UpdateAllocsFromClient(h.NextIndex(), running))
  1985  
  1986  	// Mark some of the allocations as complete
  1987  	var complete []*structs.Allocation
  1988  	for i := 6; i < 10; i++ {
  1989  		newAlloc := stop[i].Copy()
  1990  		newAlloc.ClientStatus = structs.AllocClientStatusComplete
  1991  		complete = append(complete, newAlloc)
  1992  	}
  1993  	noErr(t, h.State.UpdateAllocsFromClient(h.NextIndex(), complete))
  1994  
  1995  	// Create a mock evaluation to deal with the node update
  1996  	eval := &structs.Evaluation{
  1997  		ID:          structs.GenerateUUID(),
  1998  		Priority:    50,
  1999  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  2000  		JobID:       job.ID,
  2001  		NodeID:      node.ID,
  2002  	}
  2003  
  2004  	// Process the evaluation
  2005  	err := h.Process(NewServiceScheduler, eval)
  2006  	if err != nil {
  2007  		t.Fatalf("err: %v", err)
  2008  	}
  2009  
  2010  	// Ensure a single plan
  2011  	if len(h.Plans) != 1 {
  2012  		t.Fatalf("bad: %#v", h.Plans)
  2013  	}
  2014  	plan := h.Plans[0]
  2015  
  2016  	// Ensure the plan evicted non terminal allocs
  2017  	if len(plan.NodeUpdate[node.ID]) != 6 {
  2018  		t.Fatalf("bad: %#v", plan)
  2019  	}
  2020  
  2021  	// Ensure that all the allocations which were in running or pending state
  2022  	// has been marked as lost
  2023  	var lostAllocs []string
  2024  	for _, alloc := range plan.NodeUpdate[node.ID] {
  2025  		lostAllocs = append(lostAllocs, alloc.ID)
  2026  	}
  2027  	sort.Strings(lostAllocs)
  2028  
  2029  	var expectedLostAllocs []string
  2030  	for i := 0; i < 6; i++ {
  2031  		expectedLostAllocs = append(expectedLostAllocs, allocs[i].ID)
  2032  	}
  2033  	sort.Strings(expectedLostAllocs)
  2034  
  2035  	if !reflect.DeepEqual(expectedLostAllocs, lostAllocs) {
  2036  		t.Fatalf("expected: %v, actual: %v", expectedLostAllocs, lostAllocs)
  2037  	}
  2038  
  2039  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  2040  }
  2041  
  2042  func TestServiceSched_NodeDrain_Queued_Allocations(t *testing.T) {
  2043  	h := NewHarness(t)
  2044  
  2045  	// Register a draining node
  2046  	node := mock.Node()
  2047  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2048  
  2049  	// Generate a fake job with allocations and an update policy.
  2050  	job := mock.Job()
  2051  	job.TaskGroups[0].Count = 2
  2052  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  2053  
  2054  	var allocs []*structs.Allocation
  2055  	for i := 0; i < 2; i++ {
  2056  		alloc := mock.Alloc()
  2057  		alloc.Job = job
  2058  		alloc.JobID = job.ID
  2059  		alloc.NodeID = node.ID
  2060  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  2061  		allocs = append(allocs, alloc)
  2062  	}
  2063  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  2064  
  2065  	node.Drain = true
  2066  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2067  
  2068  	// Create a mock evaluation to deal with drain
  2069  	eval := &structs.Evaluation{
  2070  		ID:          structs.GenerateUUID(),
  2071  		Priority:    50,
  2072  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  2073  		JobID:       job.ID,
  2074  		NodeID:      node.ID,
  2075  	}
  2076  
  2077  	// Process the evaluation
  2078  	err := h.Process(NewServiceScheduler, eval)
  2079  	if err != nil {
  2080  		t.Fatalf("err: %v", err)
  2081  	}
  2082  
  2083  	queued := h.Evals[0].QueuedAllocations["web"]
  2084  	if queued != 2 {
  2085  		t.Fatalf("expected: %v, actual: %v", 2, queued)
  2086  	}
  2087  }
  2088  
  2089  func TestServiceSched_NodeDrain_UpdateStrategy(t *testing.T) {
  2090  	h := NewHarness(t)
  2091  
  2092  	// Register a draining node
  2093  	node := mock.Node()
  2094  	node.Drain = true
  2095  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2096  
  2097  	// Create some nodes
  2098  	for i := 0; i < 10; i++ {
  2099  		node := mock.Node()
  2100  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2101  	}
  2102  
  2103  	// Generate a fake job with allocations and an update policy.
  2104  	job := mock.Job()
  2105  	mp := 5
  2106  	job.Update = structs.UpdateStrategy{
  2107  		Stagger:     time.Second,
  2108  		MaxParallel: mp,
  2109  	}
  2110  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  2111  
  2112  	var allocs []*structs.Allocation
  2113  	for i := 0; i < 10; i++ {
  2114  		alloc := mock.Alloc()
  2115  		alloc.Job = job
  2116  		alloc.JobID = job.ID
  2117  		alloc.NodeID = node.ID
  2118  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  2119  		allocs = append(allocs, alloc)
  2120  	}
  2121  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  2122  
  2123  	// Create a mock evaluation to deal with drain
  2124  	eval := &structs.Evaluation{
  2125  		ID:          structs.GenerateUUID(),
  2126  		Priority:    50,
  2127  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  2128  		JobID:       job.ID,
  2129  		NodeID:      node.ID,
  2130  	}
  2131  
  2132  	// Process the evaluation
  2133  	err := h.Process(NewServiceScheduler, eval)
  2134  	if err != nil {
  2135  		t.Fatalf("err: %v", err)
  2136  	}
  2137  
  2138  	// Ensure a single plan
  2139  	if len(h.Plans) != 1 {
  2140  		t.Fatalf("bad: %#v", h.Plans)
  2141  	}
  2142  	plan := h.Plans[0]
  2143  
  2144  	// Ensure the plan evicted all allocs
  2145  	if len(plan.NodeUpdate[node.ID]) != mp {
  2146  		t.Fatalf("bad: %#v", plan)
  2147  	}
  2148  
  2149  	// Ensure the plan allocated
  2150  	var planned []*structs.Allocation
  2151  	for _, allocList := range plan.NodeAllocation {
  2152  		planned = append(planned, allocList...)
  2153  	}
  2154  	if len(planned) != mp {
  2155  		t.Fatalf("bad: %#v", plan)
  2156  	}
  2157  
  2158  	// Ensure there is a followup eval.
  2159  	if len(h.CreateEvals) != 1 ||
  2160  		h.CreateEvals[0].TriggeredBy != structs.EvalTriggerRollingUpdate {
  2161  		t.Fatalf("bad: %#v", h.CreateEvals)
  2162  	}
  2163  
  2164  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  2165  }
  2166  
  2167  func TestServiceSched_RetryLimit(t *testing.T) {
  2168  	h := NewHarness(t)
  2169  	h.Planner = &RejectPlan{h}
  2170  
  2171  	// Create some nodes
  2172  	for i := 0; i < 10; i++ {
  2173  		node := mock.Node()
  2174  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2175  	}
  2176  
  2177  	// Create a job
  2178  	job := mock.Job()
  2179  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  2180  
  2181  	// Create a mock evaluation to register the job
  2182  	eval := &structs.Evaluation{
  2183  		ID:          structs.GenerateUUID(),
  2184  		Priority:    job.Priority,
  2185  		TriggeredBy: structs.EvalTriggerJobRegister,
  2186  		JobID:       job.ID,
  2187  	}
  2188  
  2189  	// Process the evaluation
  2190  	err := h.Process(NewServiceScheduler, eval)
  2191  	if err != nil {
  2192  		t.Fatalf("err: %v", err)
  2193  	}
  2194  
  2195  	// Ensure multiple plans
  2196  	if len(h.Plans) == 0 {
  2197  		t.Fatalf("bad: %#v", h.Plans)
  2198  	}
  2199  
  2200  	// Lookup the allocations by JobID
  2201  	ws := memdb.NewWatchSet()
  2202  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  2203  	noErr(t, err)
  2204  
  2205  	// Ensure no allocations placed
  2206  	if len(out) != 0 {
  2207  		t.Fatalf("bad: %#v", out)
  2208  	}
  2209  
  2210  	// Should hit the retry limit
  2211  	h.AssertEvalStatus(t, structs.EvalStatusFailed)
  2212  }
  2213  
  2214  func TestBatchSched_Run_CompleteAlloc(t *testing.T) {
  2215  	h := NewHarness(t)
  2216  
  2217  	// Create a node
  2218  	node := mock.Node()
  2219  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2220  
  2221  	// Create a job
  2222  	job := mock.Job()
  2223  	job.TaskGroups[0].Count = 1
  2224  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  2225  
  2226  	// Create a complete alloc
  2227  	alloc := mock.Alloc()
  2228  	alloc.Job = job
  2229  	alloc.JobID = job.ID
  2230  	alloc.NodeID = node.ID
  2231  	alloc.Name = "my-job.web[0]"
  2232  	alloc.ClientStatus = structs.AllocClientStatusComplete
  2233  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc}))
  2234  
  2235  	// Create a mock evaluation to register the job
  2236  	eval := &structs.Evaluation{
  2237  		ID:          structs.GenerateUUID(),
  2238  		Priority:    job.Priority,
  2239  		TriggeredBy: structs.EvalTriggerJobRegister,
  2240  		JobID:       job.ID,
  2241  	}
  2242  
  2243  	// Process the evaluation
  2244  	err := h.Process(NewBatchScheduler, eval)
  2245  	if err != nil {
  2246  		t.Fatalf("err: %v", err)
  2247  	}
  2248  
  2249  	// Ensure no plan as it should be a no-op
  2250  	if len(h.Plans) != 0 {
  2251  		t.Fatalf("bad: %#v", h.Plans)
  2252  	}
  2253  
  2254  	// Lookup the allocations by JobID
  2255  	ws := memdb.NewWatchSet()
  2256  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  2257  	noErr(t, err)
  2258  
  2259  	// Ensure no allocations placed
  2260  	if len(out) != 1 {
  2261  		t.Fatalf("bad: %#v", out)
  2262  	}
  2263  
  2264  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  2265  }
  2266  
  2267  func TestBatchSched_Run_DrainedAlloc(t *testing.T) {
  2268  	h := NewHarness(t)
  2269  
  2270  	// Create a node
  2271  	node := mock.Node()
  2272  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2273  
  2274  	// Create a job
  2275  	job := mock.Job()
  2276  	job.TaskGroups[0].Count = 1
  2277  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  2278  
  2279  	// Create a complete alloc
  2280  	alloc := mock.Alloc()
  2281  	alloc.Job = job
  2282  	alloc.JobID = job.ID
  2283  	alloc.NodeID = node.ID
  2284  	alloc.Name = "my-job.web[0]"
  2285  	alloc.DesiredStatus = structs.AllocDesiredStatusStop
  2286  	alloc.ClientStatus = structs.AllocClientStatusComplete
  2287  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc}))
  2288  
  2289  	// Create a mock evaluation to register the job
  2290  	eval := &structs.Evaluation{
  2291  		ID:          structs.GenerateUUID(),
  2292  		Priority:    job.Priority,
  2293  		TriggeredBy: structs.EvalTriggerJobRegister,
  2294  		JobID:       job.ID,
  2295  	}
  2296  
  2297  	// Process the evaluation
  2298  	err := h.Process(NewBatchScheduler, eval)
  2299  	if err != nil {
  2300  		t.Fatalf("err: %v", err)
  2301  	}
  2302  
  2303  	// Ensure a plan
  2304  	if len(h.Plans) != 1 {
  2305  		t.Fatalf("bad: %#v", h.Plans)
  2306  	}
  2307  
  2308  	// Lookup the allocations by JobID
  2309  	ws := memdb.NewWatchSet()
  2310  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  2311  	noErr(t, err)
  2312  
  2313  	// Ensure a replacement alloc was placed.
  2314  	if len(out) != 2 {
  2315  		t.Fatalf("bad: %#v", out)
  2316  	}
  2317  
  2318  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  2319  }
  2320  
  2321  func TestBatchSched_Run_FailedAlloc(t *testing.T) {
  2322  	h := NewHarness(t)
  2323  
  2324  	// Create a node
  2325  	node := mock.Node()
  2326  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2327  
  2328  	// Create a job
  2329  	job := mock.Job()
  2330  	job.TaskGroups[0].Count = 1
  2331  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  2332  
  2333  	// Create a failed alloc
  2334  	alloc := mock.Alloc()
  2335  	alloc.Job = job
  2336  	alloc.JobID = job.ID
  2337  	alloc.NodeID = node.ID
  2338  	alloc.Name = "my-job.web[0]"
  2339  	alloc.ClientStatus = structs.AllocClientStatusFailed
  2340  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc}))
  2341  
  2342  	// Create a mock evaluation to register the job
  2343  	eval := &structs.Evaluation{
  2344  		ID:          structs.GenerateUUID(),
  2345  		Priority:    job.Priority,
  2346  		TriggeredBy: structs.EvalTriggerJobRegister,
  2347  		JobID:       job.ID,
  2348  	}
  2349  
  2350  	// Process the evaluation
  2351  	err := h.Process(NewBatchScheduler, eval)
  2352  	if err != nil {
  2353  		t.Fatalf("err: %v", err)
  2354  	}
  2355  
  2356  	// Ensure a plan
  2357  	if len(h.Plans) != 1 {
  2358  		t.Fatalf("bad: %#v", h.Plans)
  2359  	}
  2360  
  2361  	// Lookup the allocations by JobID
  2362  	ws := memdb.NewWatchSet()
  2363  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  2364  	noErr(t, err)
  2365  
  2366  	// Ensure a replacement alloc was placed.
  2367  	if len(out) != 2 {
  2368  		t.Fatalf("bad: %#v", out)
  2369  	}
  2370  
  2371  	// Ensure that the scheduler is recording the correct number of queued
  2372  	// allocations
  2373  	queued := h.Evals[0].QueuedAllocations["web"]
  2374  	if queued != 0 {
  2375  		t.Fatalf("expected: %v, actual: %v", 1, queued)
  2376  	}
  2377  
  2378  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  2379  }
  2380  
  2381  func TestBatchSched_Run_FailedAllocQueuedAllocations(t *testing.T) {
  2382  	h := NewHarness(t)
  2383  
  2384  	node := mock.Node()
  2385  	node.Drain = true
  2386  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2387  
  2388  	// Create a job
  2389  	job := mock.Job()
  2390  	job.TaskGroups[0].Count = 1
  2391  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  2392  
  2393  	// Create a failed alloc
  2394  	alloc := mock.Alloc()
  2395  	alloc.Job = job
  2396  	alloc.JobID = job.ID
  2397  	alloc.NodeID = node.ID
  2398  	alloc.Name = "my-job.web[0]"
  2399  	alloc.ClientStatus = structs.AllocClientStatusFailed
  2400  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc}))
  2401  
  2402  	// Create a mock evaluation to register the job
  2403  	eval := &structs.Evaluation{
  2404  		ID:          structs.GenerateUUID(),
  2405  		Priority:    job.Priority,
  2406  		TriggeredBy: structs.EvalTriggerJobRegister,
  2407  		JobID:       job.ID,
  2408  	}
  2409  
  2410  	// Process the evaluation
  2411  	err := h.Process(NewBatchScheduler, eval)
  2412  	if err != nil {
  2413  		t.Fatalf("err: %v", err)
  2414  	}
  2415  
  2416  	// Ensure that the scheduler is recording the correct number of queued
  2417  	// allocations
  2418  	queued := h.Evals[0].QueuedAllocations["web"]
  2419  	if queued != 1 {
  2420  		t.Fatalf("expected: %v, actual: %v", 1, queued)
  2421  	}
  2422  }
  2423  
  2424  func TestBatchSched_ReRun_SuccessfullyFinishedAlloc(t *testing.T) {
  2425  	h := NewHarness(t)
  2426  
  2427  	// Create two nodes, one that is drained and has a successfully finished
  2428  	// alloc and a fresh undrained one
  2429  	node := mock.Node()
  2430  	node.Drain = true
  2431  	node2 := mock.Node()
  2432  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2433  	noErr(t, h.State.UpsertNode(h.NextIndex(), node2))
  2434  
  2435  	// Create a job
  2436  	job := mock.Job()
  2437  	job.Type = structs.JobTypeBatch
  2438  	job.TaskGroups[0].Count = 1
  2439  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  2440  
  2441  	// Create a successful alloc
  2442  	alloc := mock.Alloc()
  2443  	alloc.Job = job
  2444  	alloc.JobID = job.ID
  2445  	alloc.NodeID = node.ID
  2446  	alloc.Name = "my-job.web[0]"
  2447  	alloc.ClientStatus = structs.AllocClientStatusComplete
  2448  	alloc.TaskStates = map[string]*structs.TaskState{
  2449  		"web": &structs.TaskState{
  2450  			State: structs.TaskStateDead,
  2451  			Events: []*structs.TaskEvent{
  2452  				{
  2453  					Type:     structs.TaskTerminated,
  2454  					ExitCode: 0,
  2455  				},
  2456  			},
  2457  		},
  2458  	}
  2459  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc}))
  2460  
  2461  	// Create a mock evaluation to rerun the job
  2462  	eval := &structs.Evaluation{
  2463  		ID:          structs.GenerateUUID(),
  2464  		Priority:    job.Priority,
  2465  		TriggeredBy: structs.EvalTriggerJobRegister,
  2466  		JobID:       job.ID,
  2467  	}
  2468  
  2469  	// Process the evaluation
  2470  	err := h.Process(NewBatchScheduler, eval)
  2471  	if err != nil {
  2472  		t.Fatalf("err: %v", err)
  2473  	}
  2474  
  2475  	// Ensure no plan
  2476  	if len(h.Plans) != 0 {
  2477  		t.Fatalf("bad: %#v", h.Plans)
  2478  	}
  2479  
  2480  	// Lookup the allocations by JobID
  2481  	ws := memdb.NewWatchSet()
  2482  	out, err := h.State.AllocsByJob(ws, job.ID, false)
  2483  	noErr(t, err)
  2484  
  2485  	// Ensure no replacement alloc was placed.
  2486  	if len(out) != 1 {
  2487  		t.Fatalf("bad: %#v", out)
  2488  	}
  2489  
  2490  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  2491  }
  2492  
  2493  // This test checks that terminal allocations that receive an in-place updated
  2494  // are not added to the plan
  2495  func TestBatchSched_JobModify_InPlace_Terminal(t *testing.T) {
  2496  	h := NewHarness(t)
  2497  
  2498  	// Create some nodes
  2499  	var nodes []*structs.Node
  2500  	for i := 0; i < 10; i++ {
  2501  		node := mock.Node()
  2502  		nodes = append(nodes, node)
  2503  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2504  	}
  2505  
  2506  	// Generate a fake job with allocations
  2507  	job := mock.Job()
  2508  	job.Type = structs.JobTypeBatch
  2509  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  2510  
  2511  	var allocs []*structs.Allocation
  2512  	for i := 0; i < 10; i++ {
  2513  		alloc := mock.Alloc()
  2514  		alloc.Job = job
  2515  		alloc.JobID = job.ID
  2516  		alloc.NodeID = nodes[i].ID
  2517  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
  2518  		alloc.ClientStatus = structs.AllocClientStatusComplete
  2519  		allocs = append(allocs, alloc)
  2520  	}
  2521  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
  2522  
  2523  	// Update the job
  2524  	job2 := mock.Job()
  2525  	job2.ID = job.ID
  2526  	noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
  2527  
  2528  	// Create a mock evaluation to deal with drain
  2529  	eval := &structs.Evaluation{
  2530  		ID:          structs.GenerateUUID(),
  2531  		Priority:    50,
  2532  		TriggeredBy: structs.EvalTriggerJobRegister,
  2533  		JobID:       job.ID,
  2534  	}
  2535  
  2536  	// Process the evaluation
  2537  	err := h.Process(NewBatchScheduler, eval)
  2538  	if err != nil {
  2539  		t.Fatalf("err: %v", err)
  2540  	}
  2541  
  2542  	// Ensure no plan
  2543  	if len(h.Plans) != 0 {
  2544  		t.Fatalf("bad: %#v", h.Plans)
  2545  	}
  2546  }
  2547  
  2548  func TestGenericSched_FilterCompleteAllocs(t *testing.T) {
  2549  	running := mock.Alloc()
  2550  	desiredStop := mock.Alloc()
  2551  	desiredStop.DesiredStatus = structs.AllocDesiredStatusStop
  2552  
  2553  	new := mock.Alloc()
  2554  	new.CreateIndex = 10000
  2555  
  2556  	oldSuccessful := mock.Alloc()
  2557  	oldSuccessful.CreateIndex = 30
  2558  	oldSuccessful.DesiredStatus = structs.AllocDesiredStatusStop
  2559  	oldSuccessful.ClientStatus = structs.AllocClientStatusComplete
  2560  	oldSuccessful.TaskStates = make(map[string]*structs.TaskState, 1)
  2561  	oldSuccessful.TaskStates["foo"] = &structs.TaskState{
  2562  		State:  structs.TaskStateDead,
  2563  		Events: []*structs.TaskEvent{{Type: structs.TaskTerminated, ExitCode: 0}},
  2564  	}
  2565  
  2566  	unsuccessful := mock.Alloc()
  2567  	unsuccessful.DesiredStatus = structs.AllocDesiredStatusRun
  2568  	unsuccessful.ClientStatus = structs.AllocClientStatusFailed
  2569  	unsuccessful.TaskStates = make(map[string]*structs.TaskState, 1)
  2570  	unsuccessful.TaskStates["foo"] = &structs.TaskState{
  2571  		State:  structs.TaskStateDead,
  2572  		Events: []*structs.TaskEvent{{Type: structs.TaskTerminated, ExitCode: 1}},
  2573  	}
  2574  
  2575  	cases := []struct {
  2576  		Batch          bool
  2577  		Input, Output  []*structs.Allocation
  2578  		TerminalAllocs map[string]*structs.Allocation
  2579  	}{
  2580  		{
  2581  			Input:          []*structs.Allocation{running},
  2582  			Output:         []*structs.Allocation{running},
  2583  			TerminalAllocs: map[string]*structs.Allocation{},
  2584  		},
  2585  		{
  2586  			Input:  []*structs.Allocation{running, desiredStop},
  2587  			Output: []*structs.Allocation{running},
  2588  			TerminalAllocs: map[string]*structs.Allocation{
  2589  				desiredStop.Name: desiredStop,
  2590  			},
  2591  		},
  2592  		{
  2593  			Batch:          true,
  2594  			Input:          []*structs.Allocation{running},
  2595  			Output:         []*structs.Allocation{running},
  2596  			TerminalAllocs: map[string]*structs.Allocation{},
  2597  		},
  2598  		{
  2599  			Batch:          true,
  2600  			Input:          []*structs.Allocation{new, oldSuccessful},
  2601  			Output:         []*structs.Allocation{new},
  2602  			TerminalAllocs: map[string]*structs.Allocation{},
  2603  		},
  2604  		{
  2605  			Batch:  true,
  2606  			Input:  []*structs.Allocation{unsuccessful},
  2607  			Output: []*structs.Allocation{},
  2608  			TerminalAllocs: map[string]*structs.Allocation{
  2609  				unsuccessful.Name: unsuccessful,
  2610  			},
  2611  		},
  2612  	}
  2613  
  2614  	for i, c := range cases {
  2615  		g := &GenericScheduler{batch: c.Batch}
  2616  		out, terminalAllocs := g.filterCompleteAllocs(c.Input)
  2617  
  2618  		if !reflect.DeepEqual(out, c.Output) {
  2619  			t.Log("Got:")
  2620  			for i, a := range out {
  2621  				t.Logf("%d: %#v", i, a)
  2622  			}
  2623  			t.Log("Want:")
  2624  			for i, a := range c.Output {
  2625  				t.Logf("%d: %#v", i, a)
  2626  			}
  2627  			t.Fatalf("Case %d failed", i+1)
  2628  		}
  2629  
  2630  		if !reflect.DeepEqual(terminalAllocs, c.TerminalAllocs) {
  2631  			t.Log("Got:")
  2632  			for n, a := range terminalAllocs {
  2633  				t.Logf("%v: %#v", n, a)
  2634  			}
  2635  			t.Log("Want:")
  2636  			for n, a := range c.TerminalAllocs {
  2637  				t.Logf("%v: %#v", n, a)
  2638  			}
  2639  			t.Fatalf("Case %d failed", i+1)
  2640  		}
  2641  
  2642  	}
  2643  }
  2644  
  2645  func TestGenericSched_ChainedAlloc(t *testing.T) {
  2646  	h := NewHarness(t)
  2647  
  2648  	// Create some nodes
  2649  	for i := 0; i < 10; i++ {
  2650  		node := mock.Node()
  2651  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2652  	}
  2653  
  2654  	// Create a job
  2655  	job := mock.Job()
  2656  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  2657  
  2658  	// Create a mock evaluation to register the job
  2659  	eval := &structs.Evaluation{
  2660  		ID:          structs.GenerateUUID(),
  2661  		Priority:    job.Priority,
  2662  		TriggeredBy: structs.EvalTriggerJobRegister,
  2663  		JobID:       job.ID,
  2664  	}
  2665  	// Process the evaluation
  2666  	if err := h.Process(NewServiceScheduler, eval); err != nil {
  2667  		t.Fatalf("err: %v", err)
  2668  	}
  2669  
  2670  	var allocIDs []string
  2671  	for _, allocList := range h.Plans[0].NodeAllocation {
  2672  		for _, alloc := range allocList {
  2673  			allocIDs = append(allocIDs, alloc.ID)
  2674  		}
  2675  	}
  2676  	sort.Strings(allocIDs)
  2677  
  2678  	// Create a new harness to invoke the scheduler again
  2679  	h1 := NewHarnessWithState(t, h.State)
  2680  	job1 := mock.Job()
  2681  	job1.ID = job.ID
  2682  	job1.TaskGroups[0].Tasks[0].Env["foo"] = "bar"
  2683  	job1.TaskGroups[0].Count = 12
  2684  	noErr(t, h1.State.UpsertJob(h1.NextIndex(), job1))
  2685  
  2686  	// Create a mock evaluation to update the job
  2687  	eval1 := &structs.Evaluation{
  2688  		ID:          structs.GenerateUUID(),
  2689  		Priority:    job1.Priority,
  2690  		TriggeredBy: structs.EvalTriggerJobRegister,
  2691  		JobID:       job1.ID,
  2692  	}
  2693  	// Process the evaluation
  2694  	if err := h1.Process(NewServiceScheduler, eval1); err != nil {
  2695  		t.Fatalf("err: %v", err)
  2696  	}
  2697  
  2698  	plan := h1.Plans[0]
  2699  
  2700  	// Collect all the chained allocation ids and the new allocations which
  2701  	// don't have any chained allocations
  2702  	var prevAllocs []string
  2703  	var newAllocs []string
  2704  	for _, allocList := range plan.NodeAllocation {
  2705  		for _, alloc := range allocList {
  2706  			if alloc.PreviousAllocation == "" {
  2707  				newAllocs = append(newAllocs, alloc.ID)
  2708  				continue
  2709  			}
  2710  			prevAllocs = append(prevAllocs, alloc.PreviousAllocation)
  2711  		}
  2712  	}
  2713  	sort.Strings(prevAllocs)
  2714  
  2715  	// Ensure that the new allocations has their corresponging original
  2716  	// allocation ids
  2717  	if !reflect.DeepEqual(prevAllocs, allocIDs) {
  2718  		t.Fatalf("expected: %v, actual: %v", len(allocIDs), len(prevAllocs))
  2719  	}
  2720  
  2721  	// Ensuring two new allocations don't have any chained allocations
  2722  	if len(newAllocs) != 2 {
  2723  		t.Fatalf("expected: %v, actual: %v", 2, len(newAllocs))
  2724  	}
  2725  }
  2726  
  2727  func TestServiceSched_NodeDrain_Sticky(t *testing.T) {
  2728  	h := NewHarness(t)
  2729  
  2730  	// Register a draining node
  2731  	node := mock.Node()
  2732  	node.Drain = true
  2733  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  2734  
  2735  	// Create an alloc on the draining node
  2736  	alloc := mock.Alloc()
  2737  	alloc.Name = "my-job.web[0]"
  2738  	alloc.DesiredStatus = structs.AllocDesiredStatusStop
  2739  	alloc.NodeID = node.ID
  2740  	alloc.Job.TaskGroups[0].Count = 1
  2741  	alloc.Job.TaskGroups[0].EphemeralDisk.Sticky = true
  2742  	noErr(t, h.State.UpsertJob(h.NextIndex(), alloc.Job))
  2743  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc}))
  2744  
  2745  	// Create a mock evaluation to deal with drain
  2746  	eval := &structs.Evaluation{
  2747  		ID:          structs.GenerateUUID(),
  2748  		Priority:    50,
  2749  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  2750  		JobID:       alloc.Job.ID,
  2751  		NodeID:      node.ID,
  2752  	}
  2753  
  2754  	// Process the evaluation
  2755  	err := h.Process(NewServiceScheduler, eval)
  2756  	if err != nil {
  2757  		t.Fatalf("err: %v", err)
  2758  	}
  2759  
  2760  	// Ensure a single plan
  2761  	if len(h.Plans) != 1 {
  2762  		t.Fatalf("bad: %#v", h.Plans)
  2763  	}
  2764  	plan := h.Plans[0]
  2765  
  2766  	// Ensure the plan evicted all allocs
  2767  	if len(plan.NodeUpdate[node.ID]) != 1 {
  2768  		t.Fatalf("bad: %#v", plan)
  2769  	}
  2770  
  2771  	// Ensure the plan didn't create any new allocations
  2772  	var planned []*structs.Allocation
  2773  	for _, allocList := range plan.NodeAllocation {
  2774  		planned = append(planned, allocList...)
  2775  	}
  2776  	if len(planned) != 0 {
  2777  		t.Fatalf("bad: %#v", plan)
  2778  	}
  2779  
  2780  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  2781  }