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