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