github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/scheduler/feasible_test.go (about) 1 package scheduler 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/hashicorp/nomad/nomad/mock" 8 "github.com/hashicorp/nomad/nomad/structs" 9 ) 10 11 func TestStaticIterator_Reset(t *testing.T) { 12 _, ctx := testContext(t) 13 var nodes []*structs.Node 14 for i := 0; i < 3; i++ { 15 nodes = append(nodes, mock.Node()) 16 } 17 static := NewStaticIterator(ctx, nodes) 18 19 for i := 0; i < 6; i++ { 20 static.Reset() 21 for j := 0; j < i; j++ { 22 static.Next() 23 } 24 static.Reset() 25 26 out := collectFeasible(static) 27 if len(out) != len(nodes) { 28 t.Fatalf("out: %#v", out) 29 t.Fatalf("missing nodes %d %#v", i, static) 30 } 31 32 ids := make(map[string]struct{}) 33 for _, o := range out { 34 if _, ok := ids[o.ID]; ok { 35 t.Fatalf("duplicate") 36 } 37 ids[o.ID] = struct{}{} 38 } 39 } 40 } 41 42 func TestStaticIterator_SetNodes(t *testing.T) { 43 _, ctx := testContext(t) 44 var nodes []*structs.Node 45 for i := 0; i < 3; i++ { 46 nodes = append(nodes, mock.Node()) 47 } 48 static := NewStaticIterator(ctx, nodes) 49 50 newNodes := []*structs.Node{mock.Node()} 51 static.SetNodes(newNodes) 52 53 out := collectFeasible(static) 54 if !reflect.DeepEqual(out, newNodes) { 55 t.Fatalf("bad: %#v", out) 56 } 57 } 58 59 func TestRandomIterator(t *testing.T) { 60 _, ctx := testContext(t) 61 var nodes []*structs.Node 62 for i := 0; i < 10; i++ { 63 nodes = append(nodes, mock.Node()) 64 } 65 66 nc := make([]*structs.Node, len(nodes)) 67 copy(nc, nodes) 68 rand := NewRandomIterator(ctx, nc) 69 70 out := collectFeasible(rand) 71 if len(out) != len(nodes) { 72 t.Fatalf("missing nodes") 73 } 74 if reflect.DeepEqual(out, nodes) { 75 t.Fatalf("same order") 76 } 77 } 78 79 func TestDriverIterator(t *testing.T) { 80 _, ctx := testContext(t) 81 nodes := []*structs.Node{ 82 mock.Node(), 83 mock.Node(), 84 mock.Node(), 85 mock.Node(), 86 } 87 static := NewStaticIterator(ctx, nodes) 88 89 nodes[0].Attributes["driver.foo"] = "1" 90 nodes[1].Attributes["driver.foo"] = "0" 91 nodes[2].Attributes["driver.foo"] = "true" 92 nodes[3].Attributes["driver.foo"] = "False" 93 94 drivers := map[string]struct{}{ 95 "exec": struct{}{}, 96 "foo": struct{}{}, 97 } 98 driver := NewDriverIterator(ctx, static, drivers) 99 100 out := collectFeasible(driver) 101 if len(out) != 2 { 102 t.Fatalf("missing nodes") 103 } 104 if out[0] != nodes[0] || out[1] != nodes[2] { 105 t.Fatalf("bad: %#v", out) 106 } 107 } 108 109 func TestConstraintIterator(t *testing.T) { 110 _, ctx := testContext(t) 111 nodes := []*structs.Node{ 112 mock.Node(), 113 mock.Node(), 114 mock.Node(), 115 } 116 static := NewStaticIterator(ctx, nodes) 117 118 nodes[0].Attributes["kernel.name"] = "freebsd" 119 nodes[1].Datacenter = "dc2" 120 121 constraints := []*structs.Constraint{ 122 &structs.Constraint{ 123 Hard: true, 124 Operand: "=", 125 LTarget: "$node.datacenter", 126 RTarget: "dc1", 127 }, 128 &structs.Constraint{ 129 Hard: true, 130 Operand: "is", 131 LTarget: "$attr.kernel.name", 132 RTarget: "linux", 133 }, 134 } 135 constr := NewConstraintIterator(ctx, static, constraints) 136 137 out := collectFeasible(constr) 138 if len(out) != 1 { 139 t.Fatalf("missing nodes") 140 } 141 if out[0] != nodes[2] { 142 t.Fatalf("bad: %#v", out) 143 } 144 } 145 146 func TestResolveConstraintTarget(t *testing.T) { 147 type tcase struct { 148 target string 149 node *structs.Node 150 val interface{} 151 result bool 152 } 153 node := mock.Node() 154 cases := []tcase{ 155 { 156 target: "$node.id", 157 node: node, 158 val: node.ID, 159 result: true, 160 }, 161 { 162 target: "$node.datacenter", 163 node: node, 164 val: node.Datacenter, 165 result: true, 166 }, 167 { 168 target: "$node.name", 169 node: node, 170 val: node.Name, 171 result: true, 172 }, 173 { 174 target: "$node.foo", 175 node: node, 176 result: false, 177 }, 178 { 179 target: "$attr.kernel.name", 180 node: node, 181 val: node.Attributes["kernel.name"], 182 result: true, 183 }, 184 { 185 target: "$attr.rand", 186 node: node, 187 result: false, 188 }, 189 { 190 target: "$meta.pci-dss", 191 node: node, 192 val: node.Meta["pci-dss"], 193 result: true, 194 }, 195 { 196 target: "$meta.rand", 197 node: node, 198 result: false, 199 }, 200 } 201 202 for _, tc := range cases { 203 res, ok := resolveConstraintTarget(tc.target, tc.node) 204 if ok != tc.result { 205 t.Fatalf("TC: %#v, Result: %v %v", tc, res, ok) 206 } 207 if ok && !reflect.DeepEqual(res, tc.val) { 208 t.Fatalf("TC: %#v, Result: %v %v", tc, res, ok) 209 } 210 } 211 } 212 213 func TestCheckConstraint(t *testing.T) { 214 type tcase struct { 215 op string 216 lVal, rVal interface{} 217 result bool 218 } 219 cases := []tcase{ 220 { 221 op: "=", 222 lVal: "foo", rVal: "foo", 223 result: true, 224 }, 225 { 226 op: "is", 227 lVal: "foo", rVal: "foo", 228 result: true, 229 }, 230 { 231 op: "==", 232 lVal: "foo", rVal: "foo", 233 result: true, 234 }, 235 { 236 op: "!=", 237 lVal: "foo", rVal: "foo", 238 result: false, 239 }, 240 { 241 op: "!=", 242 lVal: "foo", rVal: "bar", 243 result: true, 244 }, 245 { 246 op: "not", 247 lVal: "foo", rVal: "bar", 248 result: true, 249 }, 250 { 251 op: "version", 252 lVal: "1.2.3", rVal: "~> 1.0", 253 result: true, 254 }, 255 { 256 op: "regexp", 257 lVal: "foobarbaz", rVal: "[\\w]+", 258 result: true, 259 }, 260 { 261 op: "<", 262 lVal: "foo", rVal: "bar", 263 result: false, 264 }, 265 } 266 267 for _, tc := range cases { 268 _, ctx := testContext(t) 269 if res := checkConstraint(ctx, tc.op, tc.lVal, tc.rVal); res != tc.result { 270 t.Fatalf("TC: %#v, Result: %v", tc, res) 271 } 272 } 273 } 274 275 func TestCheckLexicalOrder(t *testing.T) { 276 type tcase struct { 277 op string 278 lVal, rVal interface{} 279 result bool 280 } 281 cases := []tcase{ 282 { 283 op: "<", 284 lVal: "bar", rVal: "foo", 285 result: true, 286 }, 287 { 288 op: "<=", 289 lVal: "foo", rVal: "foo", 290 result: true, 291 }, 292 { 293 op: ">", 294 lVal: "bar", rVal: "foo", 295 result: false, 296 }, 297 { 298 op: ">=", 299 lVal: "bar", rVal: "bar", 300 result: true, 301 }, 302 { 303 op: ">", 304 lVal: 1, rVal: "foo", 305 result: false, 306 }, 307 } 308 for _, tc := range cases { 309 if res := checkLexicalOrder(tc.op, tc.lVal, tc.rVal); res != tc.result { 310 t.Fatalf("TC: %#v, Result: %v", tc, res) 311 } 312 } 313 } 314 315 func TestCheckVersionConstraint(t *testing.T) { 316 type tcase struct { 317 lVal, rVal interface{} 318 result bool 319 } 320 cases := []tcase{ 321 { 322 lVal: "1.2.3", rVal: "~> 1.0", 323 result: true, 324 }, 325 { 326 lVal: "1.2.3", rVal: ">= 1.0, < 1.4", 327 result: true, 328 }, 329 { 330 lVal: "2.0.1", rVal: "~> 1.0", 331 result: false, 332 }, 333 { 334 lVal: "1.4", rVal: ">= 1.0, < 1.4", 335 result: false, 336 }, 337 { 338 lVal: 1, rVal: "~> 1.0", 339 result: true, 340 }, 341 } 342 for _, tc := range cases { 343 _, ctx := testContext(t) 344 if res := checkVersionConstraint(ctx, tc.lVal, tc.rVal); res != tc.result { 345 t.Fatalf("TC: %#v, Result: %v", tc, res) 346 } 347 } 348 } 349 350 func TestCheckRegexpConstraint(t *testing.T) { 351 type tcase struct { 352 lVal, rVal interface{} 353 result bool 354 } 355 cases := []tcase{ 356 { 357 lVal: "foobar", rVal: "bar", 358 result: true, 359 }, 360 { 361 lVal: "foobar", rVal: "^foo", 362 result: true, 363 }, 364 { 365 lVal: "foobar", rVal: "^bar", 366 result: false, 367 }, 368 { 369 lVal: "zipzap", rVal: "foo", 370 result: false, 371 }, 372 { 373 lVal: 1, rVal: "foo", 374 result: false, 375 }, 376 } 377 for _, tc := range cases { 378 _, ctx := testContext(t) 379 if res := checkRegexpConstraint(ctx, tc.lVal, tc.rVal); res != tc.result { 380 t.Fatalf("TC: %#v, Result: %v", tc, res) 381 } 382 } 383 } 384 385 func collectFeasible(iter FeasibleIterator) (out []*structs.Node) { 386 for { 387 next := iter.Next() 388 if next == nil { 389 break 390 } 391 out = append(out, next) 392 } 393 return 394 }