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