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