github.com/zoomfoo/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/scheduler/context_test.go (about)

     1  package scheduler
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/hashicorp/nomad/helper/testlog"
     8  	"github.com/hashicorp/nomad/helper/uuid"
     9  	"github.com/hashicorp/nomad/nomad/mock"
    10  	"github.com/hashicorp/nomad/nomad/state"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  )
    13  
    14  func testContext(t testing.TB) (*state.StateStore, *EvalContext) {
    15  	state := state.TestStateStore(t)
    16  	plan := &structs.Plan{
    17  		NodeUpdate:     make(map[string][]*structs.Allocation),
    18  		NodeAllocation: make(map[string][]*structs.Allocation),
    19  	}
    20  
    21  	logger := testlog.Logger(t)
    22  
    23  	ctx := NewEvalContext(state, plan, logger)
    24  	return state, ctx
    25  }
    26  
    27  func TestEvalContext_ProposedAlloc(t *testing.T) {
    28  	state, ctx := testContext(t)
    29  	nodes := []*RankedNode{
    30  		{
    31  			Node: &structs.Node{
    32  				// Perfect fit
    33  				ID: uuid.Generate(),
    34  				Resources: &structs.Resources{
    35  					CPU:      2048,
    36  					MemoryMB: 2048,
    37  				},
    38  			},
    39  		},
    40  		{
    41  			Node: &structs.Node{
    42  				// Perfect fit
    43  				ID: uuid.Generate(),
    44  				Resources: &structs.Resources{
    45  					CPU:      2048,
    46  					MemoryMB: 2048,
    47  				},
    48  			},
    49  		},
    50  	}
    51  
    52  	// Add existing allocations
    53  	j1, j2 := mock.Job(), mock.Job()
    54  	alloc1 := &structs.Allocation{
    55  		ID:        uuid.Generate(),
    56  		Namespace: structs.DefaultNamespace,
    57  		EvalID:    uuid.Generate(),
    58  		NodeID:    nodes[0].Node.ID,
    59  		JobID:     j1.ID,
    60  		Job:       j1,
    61  		Resources: &structs.Resources{
    62  			CPU:      2048,
    63  			MemoryMB: 2048,
    64  		},
    65  		DesiredStatus: structs.AllocDesiredStatusRun,
    66  		ClientStatus:  structs.AllocClientStatusPending,
    67  		TaskGroup:     "web",
    68  	}
    69  	alloc2 := &structs.Allocation{
    70  		ID:        uuid.Generate(),
    71  		Namespace: structs.DefaultNamespace,
    72  		EvalID:    uuid.Generate(),
    73  		NodeID:    nodes[1].Node.ID,
    74  		JobID:     j2.ID,
    75  		Job:       j2,
    76  		Resources: &structs.Resources{
    77  			CPU:      1024,
    78  			MemoryMB: 1024,
    79  		},
    80  		DesiredStatus: structs.AllocDesiredStatusRun,
    81  		ClientStatus:  structs.AllocClientStatusPending,
    82  		TaskGroup:     "web",
    83  	}
    84  	noErr(t, state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)))
    85  	noErr(t, state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)))
    86  	noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2}))
    87  
    88  	// Add a planned eviction to alloc1
    89  	plan := ctx.Plan()
    90  	plan.NodeUpdate[nodes[0].Node.ID] = []*structs.Allocation{alloc1}
    91  
    92  	// Add a planned placement to node1
    93  	plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{
    94  		{
    95  			Resources: &structs.Resources{
    96  				CPU:      1024,
    97  				MemoryMB: 1024,
    98  			},
    99  		},
   100  	}
   101  
   102  	proposed, err := ctx.ProposedAllocs(nodes[0].Node.ID)
   103  	if err != nil {
   104  		t.Fatalf("err: %v", err)
   105  	}
   106  	if len(proposed) != 0 {
   107  		t.Fatalf("bad: %#v", proposed)
   108  	}
   109  
   110  	proposed, err = ctx.ProposedAllocs(nodes[1].Node.ID)
   111  	if err != nil {
   112  		t.Fatalf("err: %v", err)
   113  	}
   114  	if len(proposed) != 2 {
   115  		t.Fatalf("bad: %#v", proposed)
   116  	}
   117  }
   118  
   119  func TestEvalEligibility_JobStatus(t *testing.T) {
   120  	e := NewEvalEligibility()
   121  	cc := "v1:100"
   122  
   123  	// Get the job before its been set.
   124  	if status := e.JobStatus(cc); status != EvalComputedClassUnknown {
   125  		t.Fatalf("JobStatus() returned %v; want %v", status, EvalComputedClassUnknown)
   126  	}
   127  
   128  	// Set the job and get its status.
   129  	e.SetJobEligibility(false, cc)
   130  	if status := e.JobStatus(cc); status != EvalComputedClassIneligible {
   131  		t.Fatalf("JobStatus() returned %v; want %v", status, EvalComputedClassIneligible)
   132  	}
   133  
   134  	e.SetJobEligibility(true, cc)
   135  	if status := e.JobStatus(cc); status != EvalComputedClassEligible {
   136  		t.Fatalf("JobStatus() returned %v; want %v", status, EvalComputedClassEligible)
   137  	}
   138  
   139  	// Check that if I pass an empty class it returns escaped
   140  	if status := e.JobStatus(""); status != EvalComputedClassEscaped {
   141  		t.Fatalf("JobStatus() returned %v; want %v", status, EvalComputedClassEscaped)
   142  	}
   143  }
   144  
   145  func TestEvalEligibility_TaskGroupStatus(t *testing.T) {
   146  	e := NewEvalEligibility()
   147  	cc := "v1:100"
   148  	tg := "foo"
   149  
   150  	// Get the tg before its been set.
   151  	if status := e.TaskGroupStatus(tg, cc); status != EvalComputedClassUnknown {
   152  		t.Fatalf("TaskGroupStatus() returned %v; want %v", status, EvalComputedClassUnknown)
   153  	}
   154  
   155  	// Set the tg and get its status.
   156  	e.SetTaskGroupEligibility(false, tg, cc)
   157  	if status := e.TaskGroupStatus(tg, cc); status != EvalComputedClassIneligible {
   158  		t.Fatalf("TaskGroupStatus() returned %v; want %v", status, EvalComputedClassIneligible)
   159  	}
   160  
   161  	e.SetTaskGroupEligibility(true, tg, cc)
   162  	if status := e.TaskGroupStatus(tg, cc); status != EvalComputedClassEligible {
   163  		t.Fatalf("TaskGroupStatus() returned %v; want %v", status, EvalComputedClassEligible)
   164  	}
   165  
   166  	// Check that if I pass an empty class it returns escaped
   167  	if status := e.TaskGroupStatus(tg, ""); status != EvalComputedClassEscaped {
   168  		t.Fatalf("TaskGroupStatus() returned %v; want %v", status, EvalComputedClassEscaped)
   169  	}
   170  }
   171  
   172  func TestEvalEligibility_SetJob(t *testing.T) {
   173  	e := NewEvalEligibility()
   174  	ne1 := &structs.Constraint{
   175  		LTarget: "${attr.kernel.name}",
   176  		RTarget: "linux",
   177  		Operand: "=",
   178  	}
   179  	e1 := &structs.Constraint{
   180  		LTarget: "${attr.unique.kernel.name}",
   181  		RTarget: "linux",
   182  		Operand: "=",
   183  	}
   184  	e2 := &structs.Constraint{
   185  		LTarget: "${meta.unique.key_foo}",
   186  		RTarget: "linux",
   187  		Operand: "<",
   188  	}
   189  	e3 := &structs.Constraint{
   190  		LTarget: "${meta.unique.key_foo}",
   191  		RTarget: "Windows",
   192  		Operand: "<",
   193  	}
   194  
   195  	job := mock.Job()
   196  	jobCon := []*structs.Constraint{ne1, e1, e2}
   197  	job.Constraints = jobCon
   198  
   199  	// Set the task constraints
   200  	tg := job.TaskGroups[0]
   201  	tg.Constraints = []*structs.Constraint{e1}
   202  	tg.Tasks[0].Constraints = []*structs.Constraint{e3}
   203  
   204  	e.SetJob(job)
   205  	if !e.HasEscaped() {
   206  		t.Fatalf("HasEscaped() should be true")
   207  	}
   208  
   209  	if !e.jobEscaped {
   210  		t.Fatalf("SetJob() should mark job as escaped")
   211  	}
   212  	if escaped, ok := e.tgEscapedConstraints[tg.Name]; !ok || !escaped {
   213  		t.Fatalf("SetJob() should mark task group as escaped")
   214  	}
   215  }
   216  
   217  func TestEvalEligibility_GetClasses(t *testing.T) {
   218  	e := NewEvalEligibility()
   219  	e.SetJobEligibility(true, "v1:1")
   220  	e.SetJobEligibility(false, "v1:2")
   221  	e.SetTaskGroupEligibility(true, "foo", "v1:3")
   222  	e.SetTaskGroupEligibility(false, "bar", "v1:4")
   223  	e.SetTaskGroupEligibility(true, "bar", "v1:5")
   224  
   225  	// Mark an existing eligible class as ineligible in the TG.
   226  	e.SetTaskGroupEligibility(false, "fizz", "v1:1")
   227  	e.SetTaskGroupEligibility(false, "fizz", "v1:3")
   228  
   229  	expClasses := map[string]bool{
   230  		"v1:1": true,
   231  		"v1:2": false,
   232  		"v1:3": true,
   233  		"v1:4": false,
   234  		"v1:5": true,
   235  	}
   236  
   237  	actClasses := e.GetClasses()
   238  	if !reflect.DeepEqual(actClasses, expClasses) {
   239  		t.Fatalf("GetClasses() returned %#v; want %#v", actClasses, expClasses)
   240  	}
   241  }