github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/scheduler/context_test.go (about) 1 package scheduler 2 3 import ( 4 "log" 5 "os" 6 "testing" 7 8 "github.com/hashicorp/nomad/nomad/mock" 9 "github.com/hashicorp/nomad/nomad/state" 10 "github.com/hashicorp/nomad/nomad/structs" 11 ) 12 13 func testContext(t testing.TB) (*state.StateStore, *EvalContext) { 14 state, err := state.NewStateStore(os.Stderr) 15 if err != nil { 16 t.Fatalf("err: %v", err) 17 } 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 &RankedNode{ 33 Node: &structs.Node{ 34 // Perfect fit 35 ID: structs.GenerateUUID(), 36 Resources: &structs.Resources{ 37 CPU: 2048, 38 MemoryMB: 2048, 39 }, 40 }, 41 }, 42 &RankedNode{ 43 Node: &structs.Node{ 44 // Perfect fit 45 ID: structs.GenerateUUID(), 46 Resources: &structs.Resources{ 47 CPU: 2048, 48 MemoryMB: 2048, 49 }, 50 }, 51 }, 52 } 53 54 // Add existing allocations 55 alloc1 := &structs.Allocation{ 56 ID: structs.GenerateUUID(), 57 EvalID: structs.GenerateUUID(), 58 NodeID: nodes[0].Node.ID, 59 JobID: structs.GenerateUUID(), 60 Resources: &structs.Resources{ 61 CPU: 2048, 62 MemoryMB: 2048, 63 }, 64 DesiredStatus: structs.AllocDesiredStatusRun, 65 ClientStatus: structs.AllocClientStatusPending, 66 } 67 alloc2 := &structs.Allocation{ 68 ID: structs.GenerateUUID(), 69 EvalID: structs.GenerateUUID(), 70 NodeID: nodes[1].Node.ID, 71 JobID: structs.GenerateUUID(), 72 Resources: &structs.Resources{ 73 CPU: 1024, 74 MemoryMB: 1024, 75 }, 76 DesiredStatus: structs.AllocDesiredStatusRun, 77 ClientStatus: structs.AllocClientStatusPending, 78 } 79 noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2})) 80 81 // Add a planned eviction to alloc1 82 plan := ctx.Plan() 83 plan.NodeUpdate[nodes[0].Node.ID] = []*structs.Allocation{alloc1} 84 85 // Add a planned placement to node1 86 plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{ 87 &structs.Allocation{ 88 Resources: &structs.Resources{ 89 CPU: 1024, 90 MemoryMB: 1024, 91 }, 92 }, 93 } 94 95 proposed, err := ctx.ProposedAllocs(nodes[0].Node.ID) 96 if err != nil { 97 t.Fatalf("err: %v", err) 98 } 99 if len(proposed) != 0 { 100 t.Fatalf("bad: %#v", proposed) 101 } 102 103 proposed, err = ctx.ProposedAllocs(nodes[1].Node.ID) 104 if err != nil { 105 t.Fatalf("err: %v", err) 106 } 107 if len(proposed) != 2 { 108 t.Fatalf("bad: %#v", proposed) 109 } 110 } 111 112 func TestEvalEligibility_JobStatus(t *testing.T) { 113 e := NewEvalEligibility() 114 cc := uint64(100) 115 116 // Get the job before its been set. 117 if status := e.JobStatus(cc); status != EvalComputedClassUnknown { 118 t.Fatalf("JobStatus() returned %v; want %v", status, EvalComputedClassUnknown) 119 } 120 121 // Set the job and get its status. 122 e.SetJobEligibility(false, cc) 123 if status := e.JobStatus(cc); status != EvalComputedClassIneligible { 124 t.Fatalf("JobStatus() returned %v; want %v", status, EvalComputedClassIneligible) 125 } 126 127 e.SetJobEligibility(true, cc) 128 if status := e.JobStatus(cc); status != EvalComputedClassEligible { 129 t.Fatalf("JobStatus() returned %v; want %v", status, EvalComputedClassEligible) 130 } 131 132 // Check that if I pass class zero it returns escaped 133 if status := e.JobStatus(0); status != EvalComputedClassEscaped { 134 t.Fatalf("JobStatus() returned %v; want %v", status, EvalComputedClassEscaped) 135 } 136 } 137 138 func TestEvalEligibility_TaskGroupStatus(t *testing.T) { 139 e := NewEvalEligibility() 140 cc := uint64(100) 141 tg := "foo" 142 143 // Get the tg before its been set. 144 if status := e.TaskGroupStatus(tg, cc); status != EvalComputedClassUnknown { 145 t.Fatalf("TaskGroupStatus() returned %v; want %v", status, EvalComputedClassUnknown) 146 } 147 148 // Set the tg and get its status. 149 e.SetTaskGroupEligibility(false, tg, cc) 150 if status := e.TaskGroupStatus(tg, cc); status != EvalComputedClassIneligible { 151 t.Fatalf("TaskGroupStatus() returned %v; want %v", status, EvalComputedClassIneligible) 152 } 153 154 e.SetTaskGroupEligibility(true, tg, cc) 155 if status := e.TaskGroupStatus(tg, cc); status != EvalComputedClassEligible { 156 t.Fatalf("TaskGroupStatus() returned %v; want %v", status, EvalComputedClassEligible) 157 } 158 159 // Check that if I pass class zero it returns escaped 160 if status := e.TaskGroupStatus(tg, 0); status != EvalComputedClassEscaped { 161 t.Fatalf("TaskGroupStatus() returned %v; want %v", status, EvalComputedClassEscaped) 162 } 163 } 164 165 func TestEvalEligibility_SetJob(t *testing.T) { 166 e := NewEvalEligibility() 167 ne1 := &structs.Constraint{ 168 LTarget: "$attr.kernel.name", 169 RTarget: "linux", 170 Operand: "=", 171 } 172 e1 := &structs.Constraint{ 173 LTarget: "$attr.unique.kernel.name", 174 RTarget: "linux", 175 Operand: "=", 176 } 177 e2 := &structs.Constraint{ 178 LTarget: "$meta.unique.key_foo", 179 RTarget: "linux", 180 Operand: "<", 181 } 182 e3 := &structs.Constraint{ 183 LTarget: "$meta.unique.key_foo", 184 RTarget: "Windows", 185 Operand: "<", 186 } 187 188 job := mock.Job() 189 jobCon := []*structs.Constraint{ne1, e1, e2} 190 job.Constraints = jobCon 191 192 // Set the task constraints 193 tg := job.TaskGroups[0] 194 tg.Constraints = []*structs.Constraint{e1} 195 tg.Tasks[0].Constraints = []*structs.Constraint{e3} 196 197 e.SetJob(job) 198 if !e.HasEscaped() { 199 t.Fatalf("HasEscaped() should be true") 200 } 201 202 if !e.jobEscaped { 203 t.Fatalf("SetJob() should mark job as escaped") 204 } 205 if escaped, ok := e.tgEscapedConstraints[tg.Name]; !ok || !escaped { 206 t.Fatalf("SetJob() should mark task group as escaped") 207 } 208 }