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