github.com/djenriquez/nomad-1@v0.8.1/scheduler/context_test.go (about)

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