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