github.com/anuvu/nomad@v0.8.7-atom1/scheduler/context_test.go (about)

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