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