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