github.com/maier/nomad@v0.4.1-0.20161110003312-a9e3d0b8549d/scheduler/system_sched_test.go (about)

     1  package scheduler
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/hashicorp/nomad/nomad/mock"
    10  	"github.com/hashicorp/nomad/nomad/structs"
    11  )
    12  
    13  func TestSystemSched_JobRegister(t *testing.T) {
    14  	h := NewHarness(t)
    15  
    16  	// Create some nodes
    17  	for i := 0; i < 10; i++ {
    18  		node := mock.Node()
    19  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
    20  	}
    21  
    22  	// Create a job
    23  	job := mock.SystemJob()
    24  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
    25  
    26  	// Create a mock evaluation to deregister the job
    27  	eval := &structs.Evaluation{
    28  		ID:          structs.GenerateUUID(),
    29  		Priority:    job.Priority,
    30  		TriggeredBy: structs.EvalTriggerJobRegister,
    31  		JobID:       job.ID,
    32  	}
    33  
    34  	// Process the evaluation
    35  	err := h.Process(NewSystemScheduler, eval)
    36  	if err != nil {
    37  		t.Fatalf("err: %v", err)
    38  	}
    39  
    40  	// Ensure a single plan
    41  	if len(h.Plans) != 1 {
    42  		t.Fatalf("bad: %#v", h.Plans)
    43  	}
    44  	plan := h.Plans[0]
    45  
    46  	// Ensure the plan doesn't have annotations.
    47  	if plan.Annotations != nil {
    48  		t.Fatalf("expected no annotations")
    49  	}
    50  
    51  	// Ensure the plan allocated
    52  	var planned []*structs.Allocation
    53  	for _, allocList := range plan.NodeAllocation {
    54  		planned = append(planned, allocList...)
    55  	}
    56  	if len(planned) != 10 {
    57  		t.Fatalf("bad: %#v", plan)
    58  	}
    59  
    60  	// Lookup the allocations by JobID
    61  	out, err := h.State.AllocsByJob(job.ID)
    62  	noErr(t, err)
    63  
    64  	// Ensure all allocations placed
    65  	if len(out) != 10 {
    66  		t.Fatalf("bad: %#v", out)
    67  	}
    68  
    69  	// Check the available nodes
    70  	if count, ok := out[0].Metrics.NodesAvailable["dc1"]; !ok || count != 10 {
    71  		t.Fatalf("bad: %#v", out[0].Metrics)
    72  	}
    73  
    74  	// Ensure no allocations are queued
    75  	queued := h.Evals[0].QueuedAllocations["web"]
    76  	if queued != 0 {
    77  		t.Fatalf("expected queued allocations: %v, actual: %v", 0, queued)
    78  	}
    79  
    80  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
    81  }
    82  
    83  func TestSystemeSched_JobRegister_StickyAllocs(t *testing.T) {
    84  	h := NewHarness(t)
    85  
    86  	// Create some nodes
    87  	for i := 0; i < 10; i++ {
    88  		node := mock.Node()
    89  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
    90  	}
    91  
    92  	// Create a job
    93  	job := mock.SystemJob()
    94  	job.TaskGroups[0].EphemeralDisk.Sticky = true
    95  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
    96  
    97  	// Create a mock evaluation to register the job
    98  	eval := &structs.Evaluation{
    99  		ID:          structs.GenerateUUID(),
   100  		Priority:    job.Priority,
   101  		TriggeredBy: structs.EvalTriggerJobRegister,
   102  		JobID:       job.ID,
   103  	}
   104  
   105  	// Process the evaluation
   106  	if err := h.Process(NewSystemScheduler, eval); err != nil {
   107  		t.Fatalf("err: %v", err)
   108  	}
   109  
   110  	// Ensure the plan allocated
   111  	plan := h.Plans[0]
   112  	var planned []*structs.Allocation
   113  	for _, allocList := range plan.NodeAllocation {
   114  		planned = append(planned, allocList...)
   115  	}
   116  	if len(planned) != 10 {
   117  		t.Fatalf("bad: %#v", plan)
   118  	}
   119  
   120  	// Get an allocation and mark it as failed
   121  	alloc := planned[4].Copy()
   122  	alloc.ClientStatus = structs.AllocClientStatusFailed
   123  	noErr(t, h.State.UpdateAllocsFromClient(h.NextIndex(), []*structs.Allocation{alloc}))
   124  
   125  	// Create a mock evaluation to handle the update
   126  	eval = &structs.Evaluation{
   127  		ID:          structs.GenerateUUID(),
   128  		Priority:    job.Priority,
   129  		TriggeredBy: structs.EvalTriggerNodeUpdate,
   130  		JobID:       job.ID,
   131  	}
   132  	h1 := NewHarnessWithState(t, h.State)
   133  	if err := h1.Process(NewSystemScheduler, eval); err != nil {
   134  		t.Fatalf("err: %v", err)
   135  	}
   136  
   137  	// Ensure we have created only one new allocation
   138  	plan = h1.Plans[0]
   139  	var newPlanned []*structs.Allocation
   140  	for _, allocList := range plan.NodeAllocation {
   141  		newPlanned = append(newPlanned, allocList...)
   142  	}
   143  	if len(newPlanned) != 1 {
   144  		t.Fatalf("bad plan: %#v", plan)
   145  	}
   146  	// Ensure that the new allocation was placed on the same node as the older
   147  	// one
   148  	if newPlanned[0].NodeID != alloc.NodeID || newPlanned[0].PreviousAllocation != alloc.ID {
   149  		t.Fatalf("expected: %#v, actual: %#v", alloc, newPlanned[0])
   150  	}
   151  }
   152  
   153  func TestSystemSched_JobRegister_EphemeralDiskConstraint(t *testing.T) {
   154  	h := NewHarness(t)
   155  
   156  	// Create a nodes
   157  	node := mock.Node()
   158  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   159  
   160  	// Create a job
   161  	job := mock.SystemJob()
   162  	job.TaskGroups[0].EphemeralDisk.SizeMB = 60 * 1024
   163  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   164  
   165  	// Create another job with a lot of disk resource ask so that it doesn't fit
   166  	// the node
   167  	job1 := mock.SystemJob()
   168  	job1.TaskGroups[0].EphemeralDisk.SizeMB = 60 * 1024
   169  	noErr(t, h.State.UpsertJob(h.NextIndex(), job1))
   170  
   171  	// Create a mock evaluation to register the job
   172  	eval := &structs.Evaluation{
   173  		ID:          structs.GenerateUUID(),
   174  		Priority:    job.Priority,
   175  		TriggeredBy: structs.EvalTriggerJobRegister,
   176  		JobID:       job.ID,
   177  	}
   178  
   179  	// Process the evaluation
   180  	if err := h.Process(NewSystemScheduler, eval); err != nil {
   181  		t.Fatalf("err: %v", err)
   182  	}
   183  
   184  	// Lookup the allocations by JobID
   185  	out, err := h.State.AllocsByJob(job.ID)
   186  	noErr(t, err)
   187  
   188  	// Ensure all allocations placed
   189  	if len(out) != 1 {
   190  		t.Fatalf("bad: %#v", out)
   191  	}
   192  
   193  	// Create a new harness to test the scheduling result for the second job
   194  	h1 := NewHarnessWithState(t, h.State)
   195  	// Create a mock evaluation to register the job
   196  	eval1 := &structs.Evaluation{
   197  		ID:          structs.GenerateUUID(),
   198  		Priority:    job1.Priority,
   199  		TriggeredBy: structs.EvalTriggerJobRegister,
   200  		JobID:       job1.ID,
   201  	}
   202  
   203  	// Process the evaluation
   204  	if err := h1.Process(NewSystemScheduler, eval1); err != nil {
   205  		t.Fatalf("err: %v", err)
   206  	}
   207  
   208  	out, err = h1.State.AllocsByJob(job1.ID)
   209  	noErr(t, err)
   210  	if len(out) != 0 {
   211  		t.Fatalf("bad: %#v", out)
   212  	}
   213  }
   214  
   215  func TestSystemSched_ExhaustResources(t *testing.T) {
   216  	h := NewHarness(t)
   217  
   218  	// Create a nodes
   219  	node := mock.Node()
   220  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   221  
   222  	// Create a service job which consumes most of the system resources
   223  	svcJob := mock.Job()
   224  	svcJob.TaskGroups[0].Count = 1
   225  	svcJob.TaskGroups[0].Tasks[0].Resources.CPU = 3600
   226  	noErr(t, h.State.UpsertJob(h.NextIndex(), svcJob))
   227  
   228  	// Create a mock evaluation to register the job
   229  	eval := &structs.Evaluation{
   230  		ID:          structs.GenerateUUID(),
   231  		Priority:    svcJob.Priority,
   232  		TriggeredBy: structs.EvalTriggerJobRegister,
   233  		JobID:       svcJob.ID,
   234  	}
   235  
   236  	// Process the evaluation
   237  	err := h.Process(NewServiceScheduler, eval)
   238  	if err != nil {
   239  		t.Fatalf("err: %v", err)
   240  	}
   241  
   242  	// Create a system job
   243  	job := mock.SystemJob()
   244  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   245  
   246  	// Create a mock evaluation to register the job
   247  	eval1 := &structs.Evaluation{
   248  		ID:          structs.GenerateUUID(),
   249  		Priority:    job.Priority,
   250  		TriggeredBy: structs.EvalTriggerJobRegister,
   251  		JobID:       job.ID,
   252  	}
   253  
   254  	// Process the evaluation
   255  	if err := h.Process(NewSystemScheduler, eval1); err != nil {
   256  		t.Fatalf("err: %v", err)
   257  	}
   258  
   259  	// Ensure that we have one allocation queued from the system job eval
   260  	queued := h.Evals[1].QueuedAllocations["web"]
   261  	if queued != 1 {
   262  		t.Fatalf("expected: %v, actual: %v", 1, queued)
   263  	}
   264  }
   265  
   266  func TestSystemSched_JobRegister_Annotate(t *testing.T) {
   267  	h := NewHarness(t)
   268  
   269  	// Create some nodes
   270  	for i := 0; i < 10; i++ {
   271  		node := mock.Node()
   272  		if i < 9 {
   273  			node.NodeClass = "foo"
   274  		} else {
   275  			node.NodeClass = "bar"
   276  		}
   277  		node.ComputeClass()
   278  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   279  	}
   280  
   281  	// Create a job constraining on node class
   282  	job := mock.SystemJob()
   283  	fooConstraint := &structs.Constraint{
   284  		LTarget: "${node.class}",
   285  		RTarget: "foo",
   286  		Operand: "==",
   287  	}
   288  	job.Constraints = append(job.Constraints, fooConstraint)
   289  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   290  
   291  	// Create a mock evaluation to deregister the job
   292  	eval := &structs.Evaluation{
   293  		ID:           structs.GenerateUUID(),
   294  		Priority:     job.Priority,
   295  		TriggeredBy:  structs.EvalTriggerJobRegister,
   296  		JobID:        job.ID,
   297  		AnnotatePlan: true,
   298  	}
   299  
   300  	// Process the evaluation
   301  	err := h.Process(NewSystemScheduler, eval)
   302  	if err != nil {
   303  		t.Fatalf("err: %v", err)
   304  	}
   305  
   306  	// Ensure a single plan
   307  	if len(h.Plans) != 1 {
   308  		t.Fatalf("bad: %#v", h.Plans)
   309  	}
   310  	plan := h.Plans[0]
   311  
   312  	// Ensure the plan allocated
   313  	var planned []*structs.Allocation
   314  	for _, allocList := range plan.NodeAllocation {
   315  		planned = append(planned, allocList...)
   316  	}
   317  	if len(planned) != 9 {
   318  		t.Fatalf("bad: %#v %d", planned, len(planned))
   319  	}
   320  
   321  	// Lookup the allocations by JobID
   322  	out, err := h.State.AllocsByJob(job.ID)
   323  	noErr(t, err)
   324  
   325  	// Ensure all allocations placed
   326  	if len(out) != 9 {
   327  		t.Fatalf("bad: %#v", out)
   328  	}
   329  
   330  	// Check the available nodes
   331  	if count, ok := out[0].Metrics.NodesAvailable["dc1"]; !ok || count != 10 {
   332  		t.Fatalf("bad: %#v", out[0].Metrics)
   333  	}
   334  
   335  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   336  
   337  	// Ensure the plan had annotations.
   338  	if plan.Annotations == nil {
   339  		t.Fatalf("expected annotations")
   340  	}
   341  
   342  	desiredTGs := plan.Annotations.DesiredTGUpdates
   343  	if l := len(desiredTGs); l != 1 {
   344  		t.Fatalf("incorrect number of task groups; got %v; want %v", l, 1)
   345  	}
   346  
   347  	desiredChanges, ok := desiredTGs["web"]
   348  	if !ok {
   349  		t.Fatalf("expected task group web to have desired changes")
   350  	}
   351  
   352  	expected := &structs.DesiredUpdates{Place: 9}
   353  	if !reflect.DeepEqual(desiredChanges, expected) {
   354  		t.Fatalf("Unexpected desired updates; got %#v; want %#v", desiredChanges, expected)
   355  	}
   356  }
   357  
   358  func TestSystemSched_JobRegister_AddNode(t *testing.T) {
   359  	h := NewHarness(t)
   360  
   361  	// Create some nodes
   362  	var nodes []*structs.Node
   363  	for i := 0; i < 10; i++ {
   364  		node := mock.Node()
   365  		nodes = append(nodes, node)
   366  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   367  	}
   368  
   369  	// Generate a fake job with allocations
   370  	job := mock.SystemJob()
   371  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   372  
   373  	var allocs []*structs.Allocation
   374  	for _, node := range nodes {
   375  		alloc := mock.Alloc()
   376  		alloc.Job = job
   377  		alloc.JobID = job.ID
   378  		alloc.NodeID = node.ID
   379  		alloc.Name = "my-job.web[0]"
   380  		allocs = append(allocs, alloc)
   381  	}
   382  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
   383  
   384  	// Add a new node.
   385  	node := mock.Node()
   386  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   387  
   388  	// Create a mock evaluation to deal with the node update
   389  	eval := &structs.Evaluation{
   390  		ID:          structs.GenerateUUID(),
   391  		Priority:    50,
   392  		TriggeredBy: structs.EvalTriggerNodeUpdate,
   393  		JobID:       job.ID,
   394  	}
   395  
   396  	// Process the evaluation
   397  	err := h.Process(NewSystemScheduler, eval)
   398  	if err != nil {
   399  		t.Fatalf("err: %v", err)
   400  	}
   401  
   402  	// Ensure a single plan
   403  	if len(h.Plans) != 1 {
   404  		t.Fatalf("bad: %#v", h.Plans)
   405  	}
   406  	plan := h.Plans[0]
   407  
   408  	// Ensure the plan had no node updates
   409  	var update []*structs.Allocation
   410  	for _, updateList := range plan.NodeUpdate {
   411  		update = append(update, updateList...)
   412  	}
   413  	if len(update) != 0 {
   414  		t.Log(len(update))
   415  		t.Fatalf("bad: %#v", plan)
   416  	}
   417  
   418  	// Ensure the plan allocated on the new node
   419  	var planned []*structs.Allocation
   420  	for _, allocList := range plan.NodeAllocation {
   421  		planned = append(planned, allocList...)
   422  	}
   423  	if len(planned) != 1 {
   424  		t.Fatalf("bad: %#v", plan)
   425  	}
   426  
   427  	// Ensure it allocated on the right node
   428  	if _, ok := plan.NodeAllocation[node.ID]; !ok {
   429  		t.Fatalf("allocated on wrong node: %#v", plan)
   430  	}
   431  
   432  	// Lookup the allocations by JobID
   433  	out, err := h.State.AllocsByJob(job.ID)
   434  	noErr(t, err)
   435  
   436  	// Ensure all allocations placed
   437  	out, _ = structs.FilterTerminalAllocs(out)
   438  	if len(out) != 11 {
   439  		t.Fatalf("bad: %#v", out)
   440  	}
   441  
   442  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   443  }
   444  
   445  func TestSystemSched_JobRegister_AllocFail(t *testing.T) {
   446  	h := NewHarness(t)
   447  
   448  	// Create NO nodes
   449  	// Create a job
   450  	job := mock.SystemJob()
   451  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   452  
   453  	// Create a mock evaluation to register the job
   454  	eval := &structs.Evaluation{
   455  		ID:          structs.GenerateUUID(),
   456  		Priority:    job.Priority,
   457  		TriggeredBy: structs.EvalTriggerJobRegister,
   458  		JobID:       job.ID,
   459  	}
   460  
   461  	// Process the evaluation
   462  	err := h.Process(NewSystemScheduler, eval)
   463  	if err != nil {
   464  		t.Fatalf("err: %v", err)
   465  	}
   466  
   467  	// Ensure no plan as this should be a no-op.
   468  	if len(h.Plans) != 0 {
   469  		t.Fatalf("bad: %#v", h.Plans)
   470  	}
   471  
   472  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   473  }
   474  
   475  func TestSystemSched_JobModify(t *testing.T) {
   476  	h := NewHarness(t)
   477  
   478  	// Create some nodes
   479  	var nodes []*structs.Node
   480  	for i := 0; i < 10; i++ {
   481  		node := mock.Node()
   482  		nodes = append(nodes, node)
   483  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   484  	}
   485  
   486  	// Generate a fake job with allocations
   487  	job := mock.SystemJob()
   488  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   489  
   490  	var allocs []*structs.Allocation
   491  	for _, node := range nodes {
   492  		alloc := mock.Alloc()
   493  		alloc.Job = job
   494  		alloc.JobID = job.ID
   495  		alloc.NodeID = node.ID
   496  		alloc.Name = "my-job.web[0]"
   497  		allocs = append(allocs, alloc)
   498  	}
   499  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
   500  
   501  	// Add a few terminal status allocations, these should be ignored
   502  	var terminal []*structs.Allocation
   503  	for i := 0; i < 5; i++ {
   504  		alloc := mock.Alloc()
   505  		alloc.Job = job
   506  		alloc.JobID = job.ID
   507  		alloc.NodeID = nodes[i].ID
   508  		alloc.Name = "my-job.web[0]"
   509  		alloc.DesiredStatus = structs.AllocDesiredStatusStop
   510  		terminal = append(terminal, alloc)
   511  	}
   512  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), terminal))
   513  
   514  	// Update the job
   515  	job2 := mock.SystemJob()
   516  	job2.ID = job.ID
   517  
   518  	// Update the task, such that it cannot be done in-place
   519  	job2.TaskGroups[0].Tasks[0].Config["command"] = "/bin/other"
   520  	noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
   521  
   522  	// Create a mock evaluation to deal with drain
   523  	eval := &structs.Evaluation{
   524  		ID:          structs.GenerateUUID(),
   525  		Priority:    50,
   526  		TriggeredBy: structs.EvalTriggerJobRegister,
   527  		JobID:       job.ID,
   528  	}
   529  
   530  	// Process the evaluation
   531  	err := h.Process(NewSystemScheduler, eval)
   532  	if err != nil {
   533  		t.Fatalf("err: %v", err)
   534  	}
   535  
   536  	// Ensure a single plan
   537  	if len(h.Plans) != 1 {
   538  		t.Fatalf("bad: %#v", h.Plans)
   539  	}
   540  	plan := h.Plans[0]
   541  
   542  	// Ensure the plan evicted all allocs
   543  	var update []*structs.Allocation
   544  	for _, updateList := range plan.NodeUpdate {
   545  		update = append(update, updateList...)
   546  	}
   547  	if len(update) != len(allocs) {
   548  		t.Fatalf("bad: %#v", plan)
   549  	}
   550  
   551  	// Ensure the plan allocated
   552  	var planned []*structs.Allocation
   553  	for _, allocList := range plan.NodeAllocation {
   554  		planned = append(planned, allocList...)
   555  	}
   556  	if len(planned) != 10 {
   557  		t.Fatalf("bad: %#v", plan)
   558  	}
   559  
   560  	// Lookup the allocations by JobID
   561  	out, err := h.State.AllocsByJob(job.ID)
   562  	noErr(t, err)
   563  
   564  	// Ensure all allocations placed
   565  	out, _ = structs.FilterTerminalAllocs(out)
   566  	if len(out) != 10 {
   567  		t.Fatalf("bad: %#v", out)
   568  	}
   569  
   570  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   571  }
   572  
   573  func TestSystemSched_JobModify_Rolling(t *testing.T) {
   574  	h := NewHarness(t)
   575  
   576  	// Create some nodes
   577  	var nodes []*structs.Node
   578  	for i := 0; i < 10; i++ {
   579  		node := mock.Node()
   580  		nodes = append(nodes, node)
   581  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   582  	}
   583  
   584  	// Generate a fake job with allocations
   585  	job := mock.SystemJob()
   586  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   587  
   588  	var allocs []*structs.Allocation
   589  	for _, node := range nodes {
   590  		alloc := mock.Alloc()
   591  		alloc.Job = job
   592  		alloc.JobID = job.ID
   593  		alloc.NodeID = node.ID
   594  		alloc.Name = "my-job.web[0]"
   595  		allocs = append(allocs, alloc)
   596  	}
   597  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
   598  
   599  	// Update the job
   600  	job2 := mock.SystemJob()
   601  	job2.ID = job.ID
   602  	job2.Update = structs.UpdateStrategy{
   603  		Stagger:     30 * time.Second,
   604  		MaxParallel: 5,
   605  	}
   606  
   607  	// Update the task, such that it cannot be done in-place
   608  	job2.TaskGroups[0].Tasks[0].Config["command"] = "/bin/other"
   609  	noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
   610  
   611  	// Create a mock evaluation to deal with drain
   612  	eval := &structs.Evaluation{
   613  		ID:          structs.GenerateUUID(),
   614  		Priority:    50,
   615  		TriggeredBy: structs.EvalTriggerJobRegister,
   616  		JobID:       job.ID,
   617  	}
   618  
   619  	// Process the evaluation
   620  	err := h.Process(NewSystemScheduler, eval)
   621  	if err != nil {
   622  		t.Fatalf("err: %v", err)
   623  	}
   624  
   625  	// Ensure a single plan
   626  	if len(h.Plans) != 1 {
   627  		t.Fatalf("bad: %#v", h.Plans)
   628  	}
   629  	plan := h.Plans[0]
   630  
   631  	// Ensure the plan evicted only MaxParallel
   632  	var update []*structs.Allocation
   633  	for _, updateList := range plan.NodeUpdate {
   634  		update = append(update, updateList...)
   635  	}
   636  	if len(update) != job2.Update.MaxParallel {
   637  		t.Fatalf("bad: %#v", plan)
   638  	}
   639  
   640  	// Ensure the plan allocated
   641  	var planned []*structs.Allocation
   642  	for _, allocList := range plan.NodeAllocation {
   643  		planned = append(planned, allocList...)
   644  	}
   645  	if len(planned) != job2.Update.MaxParallel {
   646  		t.Fatalf("bad: %#v", plan)
   647  	}
   648  
   649  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   650  
   651  	// Ensure a follow up eval was created
   652  	eval = h.Evals[0]
   653  	if eval.NextEval == "" {
   654  		t.Fatalf("missing next eval")
   655  	}
   656  
   657  	// Check for create
   658  	if len(h.CreateEvals) == 0 {
   659  		t.Fatalf("missing created eval")
   660  	}
   661  	create := h.CreateEvals[0]
   662  	if eval.NextEval != create.ID {
   663  		t.Fatalf("ID mismatch")
   664  	}
   665  	if create.PreviousEval != eval.ID {
   666  		t.Fatalf("missing previous eval")
   667  	}
   668  
   669  	if create.TriggeredBy != structs.EvalTriggerRollingUpdate {
   670  		t.Fatalf("bad: %#v", create)
   671  	}
   672  }
   673  
   674  func TestSystemSched_JobModify_InPlace(t *testing.T) {
   675  	h := NewHarness(t)
   676  
   677  	// Create some nodes
   678  	var nodes []*structs.Node
   679  	for i := 0; i < 10; i++ {
   680  		node := mock.Node()
   681  		nodes = append(nodes, node)
   682  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   683  	}
   684  
   685  	// Generate a fake job with allocations
   686  	job := mock.SystemJob()
   687  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   688  
   689  	var allocs []*structs.Allocation
   690  	for _, node := range nodes {
   691  		alloc := mock.Alloc()
   692  		alloc.Job = job
   693  		alloc.JobID = job.ID
   694  		alloc.NodeID = node.ID
   695  		alloc.Name = "my-job.web[0]"
   696  		allocs = append(allocs, alloc)
   697  	}
   698  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
   699  
   700  	// Update the job
   701  	job2 := mock.SystemJob()
   702  	job2.ID = job.ID
   703  	noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
   704  
   705  	// Create a mock evaluation to deal with drain
   706  	eval := &structs.Evaluation{
   707  		ID:          structs.GenerateUUID(),
   708  		Priority:    50,
   709  		TriggeredBy: structs.EvalTriggerJobRegister,
   710  		JobID:       job.ID,
   711  	}
   712  
   713  	// Process the evaluation
   714  	err := h.Process(NewSystemScheduler, eval)
   715  	if err != nil {
   716  		t.Fatalf("err: %v", err)
   717  	}
   718  
   719  	// Ensure a single plan
   720  	if len(h.Plans) != 1 {
   721  		t.Fatalf("bad: %#v", h.Plans)
   722  	}
   723  	plan := h.Plans[0]
   724  
   725  	// Ensure the plan did not evict any allocs
   726  	var update []*structs.Allocation
   727  	for _, updateList := range plan.NodeUpdate {
   728  		update = append(update, updateList...)
   729  	}
   730  	if len(update) != 0 {
   731  		t.Fatalf("bad: %#v", plan)
   732  	}
   733  
   734  	// Ensure the plan updated the existing allocs
   735  	var planned []*structs.Allocation
   736  	for _, allocList := range plan.NodeAllocation {
   737  		planned = append(planned, allocList...)
   738  	}
   739  	if len(planned) != 10 {
   740  		t.Fatalf("bad: %#v", plan)
   741  	}
   742  	for _, p := range planned {
   743  		if p.Job != job2 {
   744  			t.Fatalf("should update job")
   745  		}
   746  	}
   747  
   748  	// Lookup the allocations by JobID
   749  	out, err := h.State.AllocsByJob(job.ID)
   750  	noErr(t, err)
   751  
   752  	// Ensure all allocations placed
   753  	if len(out) != 10 {
   754  		t.Fatalf("bad: %#v", out)
   755  	}
   756  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   757  
   758  	// Verify the network did not change
   759  	rp := structs.Port{Label: "main", Value: 5000}
   760  	for _, alloc := range out {
   761  		for _, resources := range alloc.TaskResources {
   762  			if resources.Networks[0].ReservedPorts[0] != rp {
   763  				t.Fatalf("bad: %#v", alloc)
   764  			}
   765  		}
   766  	}
   767  }
   768  
   769  func TestSystemSched_JobDeregister(t *testing.T) {
   770  	h := NewHarness(t)
   771  
   772  	// Create some nodes
   773  	var nodes []*structs.Node
   774  	for i := 0; i < 10; i++ {
   775  		node := mock.Node()
   776  		nodes = append(nodes, node)
   777  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   778  	}
   779  
   780  	// Generate a fake job with allocations
   781  	job := mock.SystemJob()
   782  
   783  	var allocs []*structs.Allocation
   784  	for _, node := range nodes {
   785  		alloc := mock.Alloc()
   786  		alloc.Job = job
   787  		alloc.JobID = job.ID
   788  		alloc.NodeID = node.ID
   789  		alloc.Name = "my-job.web[0]"
   790  		allocs = append(allocs, alloc)
   791  	}
   792  	for _, alloc := range allocs {
   793  		noErr(t, h.State.UpsertJobSummary(h.NextIndex(), mock.JobSummary(alloc.JobID)))
   794  	}
   795  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), allocs))
   796  
   797  	// Create a mock evaluation to deregister the job
   798  	eval := &structs.Evaluation{
   799  		ID:          structs.GenerateUUID(),
   800  		Priority:    50,
   801  		TriggeredBy: structs.EvalTriggerJobDeregister,
   802  		JobID:       job.ID,
   803  	}
   804  
   805  	// Process the evaluation
   806  	err := h.Process(NewSystemScheduler, eval)
   807  	if err != nil {
   808  		t.Fatalf("err: %v", err)
   809  	}
   810  
   811  	// Ensure a single plan
   812  	if len(h.Plans) != 1 {
   813  		t.Fatalf("bad: %#v", h.Plans)
   814  	}
   815  	plan := h.Plans[0]
   816  
   817  	// Ensure the plan evicted the job from all nodes.
   818  	for _, node := range nodes {
   819  		if len(plan.NodeUpdate[node.ID]) != 1 {
   820  			t.Fatalf("bad: %#v", plan)
   821  		}
   822  	}
   823  
   824  	// Lookup the allocations by JobID
   825  	out, err := h.State.AllocsByJob(job.ID)
   826  	noErr(t, err)
   827  
   828  	// Ensure no remaining allocations
   829  	out, _ = structs.FilterTerminalAllocs(out)
   830  	if len(out) != 0 {
   831  		t.Fatalf("bad: %#v", out)
   832  	}
   833  
   834  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   835  }
   836  
   837  func TestSystemSched_NodeDown(t *testing.T) {
   838  	h := NewHarness(t)
   839  
   840  	// Register a down node
   841  	node := mock.Node()
   842  	node.Status = structs.NodeStatusDown
   843  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   844  
   845  	// Generate a fake job allocated on that node.
   846  	job := mock.SystemJob()
   847  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   848  
   849  	alloc := mock.Alloc()
   850  	alloc.Job = job
   851  	alloc.JobID = job.ID
   852  	alloc.NodeID = node.ID
   853  	alloc.Name = "my-job.web[0]"
   854  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc}))
   855  
   856  	// Create a mock evaluation to deal with drain
   857  	eval := &structs.Evaluation{
   858  		ID:          structs.GenerateUUID(),
   859  		Priority:    50,
   860  		TriggeredBy: structs.EvalTriggerNodeUpdate,
   861  		JobID:       job.ID,
   862  		NodeID:      node.ID,
   863  	}
   864  
   865  	// Process the evaluation
   866  	err := h.Process(NewSystemScheduler, eval)
   867  	if err != nil {
   868  		t.Fatalf("err: %v", err)
   869  	}
   870  
   871  	// Ensure a single plan
   872  	if len(h.Plans) != 1 {
   873  		t.Fatalf("bad: %#v", h.Plans)
   874  	}
   875  	plan := h.Plans[0]
   876  
   877  	// Ensure the plan evicted all allocs
   878  	if len(plan.NodeUpdate[node.ID]) != 1 {
   879  		t.Fatalf("bad: %#v", plan)
   880  	}
   881  
   882  	// Ensure the plan updated the allocation.
   883  	var planned []*structs.Allocation
   884  	for _, allocList := range plan.NodeUpdate {
   885  		planned = append(planned, allocList...)
   886  	}
   887  	if len(planned) != 1 {
   888  		t.Fatalf("bad: %#v", plan)
   889  	}
   890  
   891  	// Ensure the allocations is stopped
   892  	if p := planned[0]; p.DesiredStatus != structs.AllocDesiredStatusStop &&
   893  		p.ClientStatus != structs.AllocClientStatusLost {
   894  		t.Fatalf("bad: %#v", planned[0])
   895  	}
   896  
   897  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   898  }
   899  
   900  func TestSystemSched_NodeDrain_Down(t *testing.T) {
   901  	h := NewHarness(t)
   902  
   903  	// Register a draining node
   904  	node := mock.Node()
   905  	node.Drain = true
   906  	node.Status = structs.NodeStatusDown
   907  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   908  
   909  	// Generate a fake job allocated on that node.
   910  	job := mock.SystemJob()
   911  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   912  
   913  	alloc := mock.Alloc()
   914  	alloc.Job = job
   915  	alloc.JobID = job.ID
   916  	alloc.NodeID = node.ID
   917  	alloc.Name = "my-job.web[0]"
   918  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc}))
   919  
   920  	// Create a mock evaluation to deal with the node update
   921  	eval := &structs.Evaluation{
   922  		ID:          structs.GenerateUUID(),
   923  		Priority:    50,
   924  		TriggeredBy: structs.EvalTriggerNodeUpdate,
   925  		JobID:       job.ID,
   926  		NodeID:      node.ID,
   927  	}
   928  
   929  	// Process the evaluation
   930  	err := h.Process(NewServiceScheduler, eval)
   931  	if err != nil {
   932  		t.Fatalf("err: %v", err)
   933  	}
   934  
   935  	// Ensure a single plan
   936  	if len(h.Plans) != 1 {
   937  		t.Fatalf("bad: %#v", h.Plans)
   938  	}
   939  	plan := h.Plans[0]
   940  
   941  	// Ensure the plan evicted non terminal allocs
   942  	if len(plan.NodeUpdate[node.ID]) != 1 {
   943  		t.Fatalf("bad: %#v", plan)
   944  	}
   945  
   946  	// Ensure that the allocation is marked as lost
   947  	var lostAllocs []string
   948  	for _, alloc := range plan.NodeUpdate[node.ID] {
   949  		lostAllocs = append(lostAllocs, alloc.ID)
   950  	}
   951  	expected := []string{alloc.ID}
   952  
   953  	if !reflect.DeepEqual(lostAllocs, expected) {
   954  		t.Fatalf("expected: %v, actual: %v", expected, lostAllocs)
   955  	}
   956  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
   957  }
   958  
   959  func TestSystemSched_NodeDrain(t *testing.T) {
   960  	h := NewHarness(t)
   961  
   962  	// Register a draining node
   963  	node := mock.Node()
   964  	node.Drain = true
   965  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
   966  
   967  	// Generate a fake job allocated on that node.
   968  	job := mock.SystemJob()
   969  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
   970  
   971  	alloc := mock.Alloc()
   972  	alloc.Job = job
   973  	alloc.JobID = job.ID
   974  	alloc.NodeID = node.ID
   975  	alloc.Name = "my-job.web[0]"
   976  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc}))
   977  
   978  	// Create a mock evaluation to deal with drain
   979  	eval := &structs.Evaluation{
   980  		ID:          structs.GenerateUUID(),
   981  		Priority:    50,
   982  		TriggeredBy: structs.EvalTriggerNodeUpdate,
   983  		JobID:       job.ID,
   984  		NodeID:      node.ID,
   985  	}
   986  
   987  	// Process the evaluation
   988  	err := h.Process(NewSystemScheduler, eval)
   989  	if err != nil {
   990  		t.Fatalf("err: %v", err)
   991  	}
   992  
   993  	// Ensure a single plan
   994  	if len(h.Plans) != 1 {
   995  		t.Fatalf("bad: %#v", h.Plans)
   996  	}
   997  	plan := h.Plans[0]
   998  
   999  	// Ensure the plan evicted all allocs
  1000  	if len(plan.NodeUpdate[node.ID]) != 1 {
  1001  		t.Fatalf("bad: %#v", plan)
  1002  	}
  1003  
  1004  	// Ensure the plan updated the allocation.
  1005  	var planned []*structs.Allocation
  1006  	for _, allocList := range plan.NodeUpdate {
  1007  		planned = append(planned, allocList...)
  1008  	}
  1009  	if len(planned) != 1 {
  1010  		t.Log(len(planned))
  1011  		t.Fatalf("bad: %#v", plan)
  1012  	}
  1013  
  1014  	// Ensure the allocations is stopped
  1015  	if planned[0].DesiredStatus != structs.AllocDesiredStatusStop {
  1016  		t.Fatalf("bad: %#v", planned[0])
  1017  	}
  1018  
  1019  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1020  }
  1021  
  1022  func TestSystemSched_NodeUpdate(t *testing.T) {
  1023  	h := NewHarness(t)
  1024  
  1025  	// Register a node
  1026  	node := mock.Node()
  1027  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1028  
  1029  	// Generate a fake job allocated on that node.
  1030  	job := mock.SystemJob()
  1031  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1032  
  1033  	alloc := mock.Alloc()
  1034  	alloc.Job = job
  1035  	alloc.JobID = job.ID
  1036  	alloc.NodeID = node.ID
  1037  	alloc.Name = "my-job.web[0]"
  1038  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc}))
  1039  
  1040  	// Create a mock evaluation to deal
  1041  	eval := &structs.Evaluation{
  1042  		ID:          structs.GenerateUUID(),
  1043  		Priority:    50,
  1044  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  1045  		JobID:       job.ID,
  1046  		NodeID:      node.ID,
  1047  	}
  1048  
  1049  	// Process the evaluation
  1050  	err := h.Process(NewSystemScheduler, eval)
  1051  	if err != nil {
  1052  		t.Fatalf("err: %v", err)
  1053  	}
  1054  
  1055  	// Ensure that queued allocations is zero
  1056  	if val, ok := h.Evals[0].QueuedAllocations["web"]; !ok || val != 0 {
  1057  		t.Fatalf("bad queued allocations: %#v", h.Evals[0].QueuedAllocations)
  1058  	}
  1059  
  1060  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1061  }
  1062  
  1063  func TestSystemSched_RetryLimit(t *testing.T) {
  1064  	h := NewHarness(t)
  1065  	h.Planner = &RejectPlan{h}
  1066  
  1067  	// Create some nodes
  1068  	for i := 0; i < 10; i++ {
  1069  		node := mock.Node()
  1070  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1071  	}
  1072  
  1073  	// Create a job
  1074  	job := mock.SystemJob()
  1075  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1076  
  1077  	// Create a mock evaluation to deregister the job
  1078  	eval := &structs.Evaluation{
  1079  		ID:          structs.GenerateUUID(),
  1080  		Priority:    job.Priority,
  1081  		TriggeredBy: structs.EvalTriggerJobRegister,
  1082  		JobID:       job.ID,
  1083  	}
  1084  
  1085  	// Process the evaluation
  1086  	err := h.Process(NewSystemScheduler, eval)
  1087  	if err != nil {
  1088  		t.Fatalf("err: %v", err)
  1089  	}
  1090  
  1091  	// Ensure multiple plans
  1092  	if len(h.Plans) == 0 {
  1093  		t.Fatalf("bad: %#v", h.Plans)
  1094  	}
  1095  
  1096  	// Lookup the allocations by JobID
  1097  	out, err := h.State.AllocsByJob(job.ID)
  1098  	noErr(t, err)
  1099  
  1100  	// Ensure no allocations placed
  1101  	if len(out) != 0 {
  1102  		t.Fatalf("bad: %#v", out)
  1103  	}
  1104  
  1105  	// Should hit the retry limit
  1106  	h.AssertEvalStatus(t, structs.EvalStatusFailed)
  1107  }
  1108  
  1109  // This test ensures that the scheduler doesn't increment the queued allocation
  1110  // count for a task group when allocations can't be created on currently
  1111  // availabe nodes because of constrain mismatches.
  1112  func TestSystemSched_Queued_With_Constraints(t *testing.T) {
  1113  	h := NewHarness(t)
  1114  
  1115  	// Register a node
  1116  	node := mock.Node()
  1117  	node.Attributes["kernel.name"] = "darwin"
  1118  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1119  
  1120  	// Generate a system job which can't be placed on the node
  1121  	job := mock.SystemJob()
  1122  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1123  
  1124  	// Create a mock evaluation to deal
  1125  	eval := &structs.Evaluation{
  1126  		ID:          structs.GenerateUUID(),
  1127  		Priority:    50,
  1128  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  1129  		JobID:       job.ID,
  1130  		NodeID:      node.ID,
  1131  	}
  1132  
  1133  	// Process the evaluation
  1134  	err := h.Process(NewSystemScheduler, eval)
  1135  	if err != nil {
  1136  		t.Fatalf("err: %v", err)
  1137  	}
  1138  
  1139  	// Ensure that queued allocations is zero
  1140  	if val, ok := h.Evals[0].QueuedAllocations["web"]; !ok || val != 0 {
  1141  		t.Fatalf("bad queued allocations: %#v", h.Evals[0].QueuedAllocations)
  1142  	}
  1143  }
  1144  
  1145  func TestSystemSched_ChainedAlloc(t *testing.T) {
  1146  	h := NewHarness(t)
  1147  
  1148  	// Create some nodes
  1149  	for i := 0; i < 10; i++ {
  1150  		node := mock.Node()
  1151  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1152  	}
  1153  
  1154  	// Create a job
  1155  	job := mock.SystemJob()
  1156  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1157  
  1158  	// Create a mock evaluation to register the job
  1159  	eval := &structs.Evaluation{
  1160  		ID:          structs.GenerateUUID(),
  1161  		Priority:    job.Priority,
  1162  		TriggeredBy: structs.EvalTriggerJobRegister,
  1163  		JobID:       job.ID,
  1164  	}
  1165  	// Process the evaluation
  1166  	if err := h.Process(NewSystemScheduler, eval); err != nil {
  1167  		t.Fatalf("err: %v", err)
  1168  	}
  1169  
  1170  	var allocIDs []string
  1171  	for _, allocList := range h.Plans[0].NodeAllocation {
  1172  		for _, alloc := range allocList {
  1173  			allocIDs = append(allocIDs, alloc.ID)
  1174  		}
  1175  	}
  1176  	sort.Strings(allocIDs)
  1177  
  1178  	// Create a new harness to invoke the scheduler again
  1179  	h1 := NewHarnessWithState(t, h.State)
  1180  	job1 := mock.SystemJob()
  1181  	job1.ID = job.ID
  1182  	job1.TaskGroups[0].Tasks[0].Env["foo"] = "bar"
  1183  	noErr(t, h1.State.UpsertJob(h1.NextIndex(), job1))
  1184  
  1185  	// Insert two more nodes
  1186  	for i := 0; i < 2; i++ {
  1187  		node := mock.Node()
  1188  		noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1189  	}
  1190  
  1191  	// Create a mock evaluation to update the job
  1192  	eval1 := &structs.Evaluation{
  1193  		ID:          structs.GenerateUUID(),
  1194  		Priority:    job1.Priority,
  1195  		TriggeredBy: structs.EvalTriggerJobRegister,
  1196  		JobID:       job1.ID,
  1197  	}
  1198  	// Process the evaluation
  1199  	if err := h1.Process(NewSystemScheduler, eval1); err != nil {
  1200  		t.Fatalf("err: %v", err)
  1201  	}
  1202  
  1203  	plan := h1.Plans[0]
  1204  
  1205  	// Collect all the chained allocation ids and the new allocations which
  1206  	// don't have any chained allocations
  1207  	var prevAllocs []string
  1208  	var newAllocs []string
  1209  	for _, allocList := range plan.NodeAllocation {
  1210  		for _, alloc := range allocList {
  1211  			if alloc.PreviousAllocation == "" {
  1212  				newAllocs = append(newAllocs, alloc.ID)
  1213  				continue
  1214  			}
  1215  			prevAllocs = append(prevAllocs, alloc.PreviousAllocation)
  1216  		}
  1217  	}
  1218  	sort.Strings(prevAllocs)
  1219  
  1220  	// Ensure that the new allocations has their corresponging original
  1221  	// allocation ids
  1222  	if !reflect.DeepEqual(prevAllocs, allocIDs) {
  1223  		t.Fatalf("expected: %v, actual: %v", len(allocIDs), len(prevAllocs))
  1224  	}
  1225  
  1226  	// Ensuring two new allocations don't have any chained allocations
  1227  	if len(newAllocs) != 2 {
  1228  		t.Fatalf("expected: %v, actual: %v", 2, len(newAllocs))
  1229  	}
  1230  }
  1231  
  1232  func TestSystemSched_PlanWithDrainedNode(t *testing.T) {
  1233  	h := NewHarness(t)
  1234  
  1235  	// Register two nodes with two different classes
  1236  	node := mock.Node()
  1237  	node.NodeClass = "green"
  1238  	node.Drain = true
  1239  	node.ComputeClass()
  1240  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1241  
  1242  	node2 := mock.Node()
  1243  	node2.NodeClass = "blue"
  1244  	node2.ComputeClass()
  1245  	noErr(t, h.State.UpsertNode(h.NextIndex(), node2))
  1246  
  1247  	// Create a Job with two task groups, each constrianed on node class
  1248  	job := mock.SystemJob()
  1249  	tg1 := job.TaskGroups[0]
  1250  	tg1.Constraints = append(tg1.Constraints,
  1251  		&structs.Constraint{
  1252  			LTarget: "${node.class}",
  1253  			RTarget: "green",
  1254  			Operand: "==",
  1255  		})
  1256  
  1257  	tg2 := tg1.Copy()
  1258  	tg2.Name = "web2"
  1259  	tg2.Constraints[0].RTarget = "blue"
  1260  	job.TaskGroups = append(job.TaskGroups, tg2)
  1261  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1262  
  1263  	// Create an allocation on each node
  1264  	alloc := mock.Alloc()
  1265  	alloc.Job = job
  1266  	alloc.JobID = job.ID
  1267  	alloc.NodeID = node.ID
  1268  	alloc.Name = "my-job.web[0]"
  1269  	alloc.TaskGroup = "web"
  1270  
  1271  	alloc2 := mock.Alloc()
  1272  	alloc2.Job = job
  1273  	alloc2.JobID = job.ID
  1274  	alloc2.NodeID = node2.ID
  1275  	alloc2.Name = "my-job.web2[0]"
  1276  	alloc2.TaskGroup = "web2"
  1277  	noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc, alloc2}))
  1278  
  1279  	// Create a mock evaluation to deal with drain
  1280  	eval := &structs.Evaluation{
  1281  		ID:          structs.GenerateUUID(),
  1282  		Priority:    50,
  1283  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  1284  		JobID:       job.ID,
  1285  		NodeID:      node.ID,
  1286  	}
  1287  
  1288  	// Process the evaluation
  1289  	err := h.Process(NewSystemScheduler, eval)
  1290  	if err != nil {
  1291  		t.Fatalf("err: %v", err)
  1292  	}
  1293  
  1294  	// Ensure a single plan
  1295  	if len(h.Plans) != 1 {
  1296  		t.Fatalf("bad: %#v", h.Plans)
  1297  	}
  1298  	plan := h.Plans[0]
  1299  
  1300  	// Ensure the plan evicted the alloc on the failed node
  1301  	planned := plan.NodeUpdate[node.ID]
  1302  	if len(planned) != 1 {
  1303  		t.Fatalf("bad: %#v", plan)
  1304  	}
  1305  
  1306  	// Ensure the plan didn't place
  1307  	if len(plan.NodeAllocation) != 0 {
  1308  		t.Fatalf("bad: %#v", plan)
  1309  	}
  1310  
  1311  	// Ensure the allocations is stopped
  1312  	if planned[0].DesiredStatus != structs.AllocDesiredStatusStop {
  1313  		t.Fatalf("bad: %#v", planned[0])
  1314  	}
  1315  
  1316  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1317  }
  1318  
  1319  func TestSystemSched_QueuedAllocsMultTG(t *testing.T) {
  1320  	h := NewHarness(t)
  1321  
  1322  	// Register two nodes with two different classes
  1323  	node := mock.Node()
  1324  	node.NodeClass = "green"
  1325  	node.ComputeClass()
  1326  	noErr(t, h.State.UpsertNode(h.NextIndex(), node))
  1327  
  1328  	node2 := mock.Node()
  1329  	node2.NodeClass = "blue"
  1330  	node2.ComputeClass()
  1331  	noErr(t, h.State.UpsertNode(h.NextIndex(), node2))
  1332  
  1333  	// Create a Job with two task groups, each constrianed on node class
  1334  	job := mock.SystemJob()
  1335  	tg1 := job.TaskGroups[0]
  1336  	tg1.Constraints = append(tg1.Constraints,
  1337  		&structs.Constraint{
  1338  			LTarget: "${node.class}",
  1339  			RTarget: "green",
  1340  			Operand: "==",
  1341  		})
  1342  
  1343  	tg2 := tg1.Copy()
  1344  	tg2.Name = "web2"
  1345  	tg2.Constraints[0].RTarget = "blue"
  1346  	job.TaskGroups = append(job.TaskGroups, tg2)
  1347  	noErr(t, h.State.UpsertJob(h.NextIndex(), job))
  1348  
  1349  	// Create a mock evaluation to deal with drain
  1350  	eval := &structs.Evaluation{
  1351  		ID:          structs.GenerateUUID(),
  1352  		Priority:    50,
  1353  		TriggeredBy: structs.EvalTriggerNodeUpdate,
  1354  		JobID:       job.ID,
  1355  		NodeID:      node.ID,
  1356  	}
  1357  
  1358  	// Process the evaluation
  1359  	err := h.Process(NewSystemScheduler, eval)
  1360  	if err != nil {
  1361  		t.Fatalf("err: %v", err)
  1362  	}
  1363  
  1364  	// Ensure a single plan
  1365  	if len(h.Plans) != 1 {
  1366  		t.Fatalf("bad: %#v", h.Plans)
  1367  	}
  1368  
  1369  	qa := h.Evals[0].QueuedAllocations
  1370  	if qa["web"] != 0 || qa["web2"] != 0 {
  1371  		t.Fatalf("bad queued allocations %#v", qa)
  1372  	}
  1373  
  1374  	h.AssertEvalStatus(t, structs.EvalStatusComplete)
  1375  }