github.com/smithx10/nomad@v0.9.1-rc1/scheduler/context_test.go (about)

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