github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/core/constraints/constraints_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package constraints_test 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "strings" 10 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 goyaml "gopkg.in/yaml.v2" 14 15 "github.com/juju/juju/core/constraints" 16 "github.com/juju/juju/core/instance" 17 ) 18 19 type ConstraintsSuite struct{} 20 21 var _ = gc.Suite(&ConstraintsSuite{}) 22 23 var parseConstraintsTests = []struct { 24 summary string 25 args []string 26 err string 27 }{ 28 // Simple errors. 29 { 30 summary: "nothing at all", 31 }, { 32 summary: "empty", 33 args: []string{" "}, 34 }, { 35 summary: "complete nonsense", 36 args: []string{"cheese"}, 37 err: `malformed constraint "cheese"`, 38 }, { 39 summary: "missing name", 40 args: []string{"=cheese"}, 41 err: `malformed constraint "=cheese"`, 42 }, { 43 summary: "unknown constraint", 44 args: []string{"cheese=edam"}, 45 err: `unknown constraint "cheese"`, 46 }, 47 48 // "container" in detail. 49 { 50 summary: "set container empty", 51 args: []string{"container="}, 52 }, { 53 summary: "set container to none", 54 args: []string{"container=none"}, 55 }, { 56 summary: "set container lxd", 57 args: []string{"container=lxd"}, 58 }, { 59 summary: "set nonsense container", 60 args: []string{"container=foo"}, 61 err: `bad "container" constraint: invalid container type "foo"`, 62 }, { 63 summary: "double set container together", 64 args: []string{"container=lxd container=lxd"}, 65 err: `bad "container" constraint: already set`, 66 }, { 67 summary: "double set container separately", 68 args: []string{"container=lxd", "container="}, 69 err: `bad "container" constraint: already set`, 70 }, 71 72 // "arch" in detail. 73 { 74 summary: "set arch empty", 75 args: []string{"arch="}, 76 }, { 77 summary: "set arch amd64", 78 args: []string{"arch=amd64"}, 79 }, { 80 summary: "set arch i386", 81 args: []string{"arch=i386"}, 82 }, { 83 summary: "set arch armhf", 84 args: []string{"arch=armhf"}, 85 }, { 86 summary: "set nonsense arch 1", 87 args: []string{"arch=cheese"}, 88 err: `bad "arch" constraint: "cheese" not recognized`, 89 }, { 90 summary: "set nonsense arch 2", 91 args: []string{"arch=123.45"}, 92 err: `bad "arch" constraint: "123.45" not recognized`, 93 }, { 94 summary: "double set arch together", 95 args: []string{"arch=amd64 arch=amd64"}, 96 err: `bad "arch" constraint: already set`, 97 }, { 98 summary: "double set arch separately", 99 args: []string{"arch=armhf", "arch="}, 100 err: `bad "arch" constraint: already set`, 101 }, 102 103 // "cores" in detail. 104 { 105 summary: "set cores empty", 106 args: []string{"cores="}, 107 }, { 108 summary: "set cores zero", 109 args: []string{"cores=0"}, 110 }, { 111 summary: "set cores", 112 args: []string{"cores=4"}, 113 }, { 114 summary: "set nonsense cores 1", 115 args: []string{"cores=cheese"}, 116 err: `bad "cores" constraint: must be a non-negative integer`, 117 }, { 118 summary: "set nonsense cores 2", 119 args: []string{"cores=-1"}, 120 err: `bad "cores" constraint: must be a non-negative integer`, 121 }, { 122 summary: "set nonsense cores 3", 123 args: []string{"cores=123.45"}, 124 err: `bad "cores" constraint: must be a non-negative integer`, 125 }, { 126 summary: "double set cores together", 127 args: []string{"cores=128 cores=1"}, 128 err: `bad "cores" constraint: already set`, 129 }, { 130 summary: "double set cores separately", 131 args: []string{"cores=128", "cores=1"}, 132 err: `bad "cores" constraint: already set`, 133 }, 134 135 // "cpu-cores" 136 { 137 summary: "set cpu-cores", 138 args: []string{"cpu-cores=4"}, 139 }, 140 141 // "cpu-power" in detail. 142 { 143 summary: "set cpu-power empty", 144 args: []string{"cpu-power="}, 145 }, { 146 summary: "set cpu-power zero", 147 args: []string{"cpu-power=0"}, 148 }, { 149 summary: "set cpu-power", 150 args: []string{"cpu-power=44"}, 151 }, { 152 summary: "set nonsense cpu-power 1", 153 args: []string{"cpu-power=cheese"}, 154 err: `bad "cpu-power" constraint: must be a non-negative integer`, 155 }, { 156 summary: "set nonsense cpu-power 2", 157 args: []string{"cpu-power=-1"}, 158 err: `bad "cpu-power" constraint: must be a non-negative integer`, 159 }, { 160 summary: "double set cpu-power together", 161 args: []string{" cpu-power=300 cpu-power=1700 "}, 162 err: `bad "cpu-power" constraint: already set`, 163 }, { 164 summary: "double set cpu-power separately", 165 args: []string{"cpu-power=300 ", " cpu-power=1700"}, 166 err: `bad "cpu-power" constraint: already set`, 167 }, 168 169 // "mem" in detail. 170 { 171 summary: "set mem empty", 172 args: []string{"mem="}, 173 }, { 174 summary: "set mem zero", 175 args: []string{"mem=0"}, 176 }, { 177 summary: "set mem without suffix", 178 args: []string{"mem=512"}, 179 }, { 180 summary: "set mem with M suffix", 181 args: []string{"mem=512M"}, 182 }, { 183 summary: "set mem with G suffix", 184 args: []string{"mem=1.5G"}, 185 }, { 186 summary: "set mem with T suffix", 187 args: []string{"mem=36.2T"}, 188 }, { 189 summary: "set mem with P suffix", 190 args: []string{"mem=18.9P"}, 191 }, { 192 summary: "set nonsense mem 1", 193 args: []string{"mem=cheese"}, 194 err: `bad "mem" constraint: must be a non-negative float with optional M/G/T/P suffix`, 195 }, { 196 summary: "set nonsense mem 2", 197 args: []string{"mem=-1"}, 198 err: `bad "mem" constraint: must be a non-negative float with optional M/G/T/P suffix`, 199 }, { 200 summary: "set nonsense mem 3", 201 args: []string{"mem=32Y"}, 202 err: `bad "mem" constraint: must be a non-negative float with optional M/G/T/P suffix`, 203 }, { 204 summary: "double set mem together", 205 args: []string{"mem=1G mem=2G"}, 206 err: `bad "mem" constraint: already set`, 207 }, { 208 summary: "double set mem separately", 209 args: []string{"mem=1G", "mem=2G"}, 210 err: `bad "mem" constraint: already set`, 211 }, 212 213 // "root-disk" in detail. 214 { 215 summary: "set root-disk empty", 216 args: []string{"root-disk="}, 217 }, { 218 summary: "set root-disk zero", 219 args: []string{"root-disk=0"}, 220 }, { 221 summary: "set root-disk without suffix", 222 args: []string{"root-disk=512"}, 223 }, { 224 summary: "set root-disk with M suffix", 225 args: []string{"root-disk=512M"}, 226 }, { 227 summary: "set root-disk with G suffix", 228 args: []string{"root-disk=1.5G"}, 229 }, { 230 summary: "set root-disk with T suffix", 231 args: []string{"root-disk=36.2T"}, 232 }, { 233 summary: "set root-disk with P suffix", 234 args: []string{"root-disk=18.9P"}, 235 }, { 236 summary: "set nonsense root-disk 1", 237 args: []string{"root-disk=cheese"}, 238 err: `bad "root-disk" constraint: must be a non-negative float with optional M/G/T/P suffix`, 239 }, { 240 summary: "set nonsense root-disk 2", 241 args: []string{"root-disk=-1"}, 242 err: `bad "root-disk" constraint: must be a non-negative float with optional M/G/T/P suffix`, 243 }, { 244 summary: "set nonsense root-disk 3", 245 args: []string{"root-disk=32Y"}, 246 err: `bad "root-disk" constraint: must be a non-negative float with optional M/G/T/P suffix`, 247 }, { 248 summary: "double set root-disk together", 249 args: []string{"root-disk=1G root-disk=2G"}, 250 err: `bad "root-disk" constraint: already set`, 251 }, { 252 summary: "double set root-disk separately", 253 args: []string{"root-disk=1G", "root-disk=2G"}, 254 err: `bad "root-disk" constraint: already set`, 255 }, 256 257 // tags 258 { 259 summary: "single tag", 260 args: []string{"tags=foo"}, 261 }, { 262 summary: "multiple tags", 263 args: []string{"tags=foo,bar"}, 264 }, { 265 summary: "no tags", 266 args: []string{"tags="}, 267 }, 268 269 // spaces 270 { 271 summary: "single space", 272 args: []string{"spaces=space1"}, 273 }, { 274 summary: "multiple spaces - positive", 275 args: []string{"spaces=space1,space2"}, 276 }, { 277 summary: "multiple spaces - negative", 278 args: []string{"spaces=^dmz,^public"}, 279 }, { 280 summary: "multiple spaces - positive and negative", 281 args: []string{"spaces=admin,^area52,dmz,^public"}, 282 }, { 283 summary: "no spaces", 284 args: []string{"spaces="}, 285 }, 286 287 // instance type 288 { 289 summary: "set instance type", 290 args: []string{"instance-type=foo"}, 291 }, { 292 summary: "instance type empty", 293 args: []string{"instance-type="}, 294 }, 295 296 // "virt-type" in detail. 297 { 298 summary: "set virt-type empty", 299 args: []string{"virt-type="}, 300 }, { 301 summary: "set virt-type kvm", 302 args: []string{"virt-type=kvm"}, 303 }, { 304 summary: "set virt-type lxd", 305 args: []string{"virt-type=lxd"}, 306 }, { 307 summary: "double set virt-type together", 308 args: []string{"virt-type=kvm virt-type=kvm"}, 309 err: `bad "virt-type" constraint: already set`, 310 }, { 311 summary: "double set virt-type separately", 312 args: []string{"virt-type=kvm", "virt-type="}, 313 err: `bad "virt-type" constraint: already set`, 314 }, 315 316 // Zones 317 { 318 summary: "single zone", 319 args: []string{"zones=az1"}, 320 }, { 321 summary: "multiple zones", 322 args: []string{"zones=az1,az2"}, 323 }, { 324 summary: "no zones", 325 args: []string{"zones="}, 326 }, 327 328 // Everything at once. 329 { 330 summary: "kitchen sink together", 331 args: []string{ 332 "root-disk=8G mem=2T arch=i386 cores=4096 cpu-power=9001 container=lxd " + 333 "tags=foo,bar spaces=space1,^space2 instance-type=foo", 334 "virt-type=kvm zones=az1,az2"}, 335 }, { 336 summary: "kitchen sink separately", 337 args: []string{ 338 "root-disk=8G", "mem=2T", "cores=4096", "cpu-power=9001", "arch=armhf", 339 "container=lxd", "tags=foo,bar", "spaces=space1,^space2", 340 "instance-type=foo", "virt-type=kvm", "zones=az1,az2"}, 341 }, 342 } 343 344 func (s *ConstraintsSuite) TestParseConstraints(c *gc.C) { 345 // TODO(dimitern): This test is inadequate and needs to check for 346 // more than just the reparsed output of String() matches the 347 // expected. 348 for i, t := range parseConstraintsTests { 349 c.Logf("test %d: %s", i, t.summary) 350 cons0, err := constraints.Parse(t.args...) 351 if t.err == "" { 352 c.Assert(err, jc.ErrorIsNil) 353 } else { 354 c.Assert(err, gc.ErrorMatches, t.err) 355 continue 356 } 357 cons1, err := constraints.Parse(cons0.String()) 358 c.Check(err, jc.ErrorIsNil) 359 c.Check(cons1, gc.DeepEquals, cons0) 360 } 361 } 362 363 func (s *ConstraintsSuite) TestParseAliases(c *gc.C) { 364 v, aliases, err := constraints.ParseWithAliases("cpu-cores=5 arch=amd64") 365 c.Assert(err, jc.ErrorIsNil) 366 c.Assert(v, gc.DeepEquals, constraints.Value{ 367 CpuCores: uint64p(5), 368 Arch: strp("amd64"), 369 }) 370 c.Assert(aliases, gc.DeepEquals, map[string]string{ 371 "cpu-cores": "cores", 372 }) 373 } 374 375 func (s *ConstraintsSuite) TestMerge(c *gc.C) { 376 con1 := constraints.MustParse("arch=amd64 mem=4G") 377 con2 := constraints.MustParse("cores=42") 378 con3 := constraints.MustParse( 379 "root-disk=8G container=lxd spaces=space1,^space2", 380 ) 381 merged, err := constraints.Merge(con1, con2) 382 c.Assert(err, jc.ErrorIsNil) 383 c.Assert(merged, jc.DeepEquals, constraints.MustParse("arch=amd64 mem=4G cores=42")) 384 merged, err = constraints.Merge(con1) 385 c.Assert(err, jc.ErrorIsNil) 386 c.Assert(merged, jc.DeepEquals, con1) 387 merged, err = constraints.Merge(con1, con2, con3) 388 c.Assert(err, jc.ErrorIsNil) 389 c.Assert(merged, jc.DeepEquals, constraints. 390 MustParse("arch=amd64 mem=4G cores=42 root-disk=8G container=lxd spaces=space1,^space2"), 391 ) 392 merged, err = constraints.Merge() 393 c.Assert(err, jc.ErrorIsNil) 394 c.Assert(merged, jc.DeepEquals, constraints.Value{}) 395 foo := "foo" 396 merged, err = constraints.Merge(constraints.Value{Arch: &foo}, con2) 397 c.Assert(err, gc.NotNil) 398 c.Assert(err, gc.ErrorMatches, `bad "arch" constraint: "foo" not recognized`) 399 c.Assert(merged, jc.DeepEquals, constraints.Value{}) 400 merged, err = constraints.Merge(con1, con1) 401 c.Assert(err, gc.NotNil) 402 c.Assert(err, gc.ErrorMatches, `bad "arch" constraint: already set`) 403 c.Assert(merged, jc.DeepEquals, constraints.Value{}) 404 } 405 406 func (s *ConstraintsSuite) TestParseMissingTagsAndSpaces(c *gc.C) { 407 con := constraints.MustParse("arch=amd64 mem=4G cores=1 root-disk=8G") 408 c.Check(con.Tags, gc.IsNil) 409 c.Check(con.Spaces, gc.IsNil) 410 } 411 412 func (s *ConstraintsSuite) TestParseNoTagsNoSpaces(c *gc.C) { 413 con := constraints.MustParse( 414 "arch=amd64 mem=4G cores=1 root-disk=8G tags= spaces=", 415 ) 416 c.Assert(con.Tags, gc.Not(gc.IsNil)) 417 c.Assert(con.Spaces, gc.Not(gc.IsNil)) 418 c.Check(*con.Tags, gc.HasLen, 0) 419 c.Check(*con.Spaces, gc.HasLen, 0) 420 } 421 422 func (s *ConstraintsSuite) TestIncludeExcludeAndHasSpaces(c *gc.C) { 423 con := constraints.MustParse("spaces=space1,^space2,space3,^space4") 424 c.Assert(con.Spaces, gc.Not(gc.IsNil)) 425 c.Check(*con.Spaces, gc.HasLen, 4) 426 c.Check(con.IncludeSpaces(), jc.SameContents, []string{"space1", "space3"}) 427 c.Check(con.ExcludeSpaces(), jc.SameContents, []string{"space2", "space4"}) 428 c.Check(con.HasSpaces(), jc.IsTrue) 429 con = constraints.MustParse("mem=4G") 430 c.Check(con.HasSpaces(), jc.IsFalse) 431 con = constraints.MustParse("mem=4G spaces=space-foo,^space-bar") 432 c.Check(con.IncludeSpaces(), jc.SameContents, []string{"space-foo"}) 433 c.Check(con.ExcludeSpaces(), jc.SameContents, []string{"space-bar"}) 434 c.Check(con.HasSpaces(), jc.IsTrue) 435 } 436 437 func (s *ConstraintsSuite) TestInvalidSpaces(c *gc.C) { 438 invalidNames := []string{ 439 "%$pace", "^foo#2", "+", "tcp:ip", 440 "^^myspace", "^^^^^^^^", "space^x", 441 "&-foo", "space/3", "^bar=4", "&#!", 442 } 443 for _, name := range invalidNames { 444 con, err := constraints.Parse("spaces=" + name) 445 expectName := strings.TrimPrefix(name, "^") 446 expectErr := fmt.Sprintf(`bad "spaces" constraint: %q is not a valid space name`, expectName) 447 c.Check(err, gc.NotNil) 448 c.Check(err.Error(), gc.Equals, expectErr) 449 c.Check(con, jc.DeepEquals, constraints.Value{}) 450 } 451 } 452 453 func (s *ConstraintsSuite) TestHasZones(c *gc.C) { 454 con := constraints.MustParse("zones=az1,az2,az3") 455 c.Assert(con.Zones, gc.Not(gc.IsNil)) 456 c.Check(*con.Zones, gc.HasLen, 3) 457 c.Check(con.HasZones(), jc.IsTrue) 458 459 con = constraints.MustParse("zones=") 460 c.Check(con.HasZones(), jc.IsFalse) 461 462 con = constraints.MustParse("spaces=space1,^space2") 463 c.Check(con.HasZones(), jc.IsFalse) 464 } 465 466 func (s *ConstraintsSuite) TestIsEmpty(c *gc.C) { 467 con := constraints.Value{} 468 c.Check(&con, jc.Satisfies, constraints.IsEmpty) 469 con = constraints.MustParse("arch=amd64") 470 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 471 con = constraints.MustParse("") 472 c.Check(&con, jc.Satisfies, constraints.IsEmpty) 473 con = constraints.MustParse("tags=") 474 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 475 con = constraints.MustParse("spaces=") 476 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 477 con = constraints.MustParse("mem=") 478 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 479 con = constraints.MustParse("arch=") 480 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 481 con = constraints.MustParse("root-disk=") 482 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 483 con = constraints.MustParse("cpu-power=") 484 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 485 con = constraints.MustParse("cores=") 486 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 487 con = constraints.MustParse("container=") 488 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 489 con = constraints.MustParse("instance-type=") 490 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 491 con = constraints.MustParse("zones=") 492 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) 493 } 494 495 func uint64p(i uint64) *uint64 { 496 return &i 497 } 498 499 func strp(s string) *string { 500 return &s 501 } 502 503 func ctypep(ctype string) *instance.ContainerType { 504 res := instance.ContainerType(ctype) 505 return &res 506 } 507 508 type roundTrip struct { 509 Name string 510 Value constraints.Value 511 } 512 513 var constraintsRoundtripTests = []roundTrip{ 514 {"empty", constraints.Value{}}, 515 {"Arch1", constraints.Value{Arch: strp("")}}, 516 {"Arch2", constraints.Value{Arch: strp("amd64")}}, 517 {"Container1", constraints.Value{Container: ctypep("")}}, 518 {"Container2", constraints.Value{Container: ctypep("lxd")}}, 519 {"Container3", constraints.Value{Container: nil}}, 520 {"CpuCores1", constraints.Value{CpuCores: nil}}, 521 {"CpuCores2", constraints.Value{CpuCores: uint64p(0)}}, 522 {"CpuCores3", constraints.Value{CpuCores: uint64p(128)}}, 523 {"CpuPower1", constraints.Value{CpuPower: nil}}, 524 {"CpuPower2", constraints.Value{CpuPower: uint64p(0)}}, 525 {"CpuPower3", constraints.Value{CpuPower: uint64p(250)}}, 526 {"Mem1", constraints.Value{Mem: nil}}, 527 {"Mem2", constraints.Value{Mem: uint64p(0)}}, 528 {"Mem3", constraints.Value{Mem: uint64p(98765)}}, 529 {"RootDisk1", constraints.Value{RootDisk: nil}}, 530 {"RootDisk2", constraints.Value{RootDisk: uint64p(0)}}, 531 {"RootDisk2", constraints.Value{RootDisk: uint64p(109876)}}, 532 {"Tags1", constraints.Value{Tags: nil}}, 533 {"Tags2", constraints.Value{Tags: &[]string{}}}, 534 {"Tags3", constraints.Value{Tags: &[]string{"foo", "bar"}}}, 535 {"Spaces1", constraints.Value{Spaces: nil}}, 536 {"Spaces2", constraints.Value{Spaces: &[]string{}}}, 537 {"Spaces3", constraints.Value{Spaces: &[]string{"space1", "^space2"}}}, 538 {"InstanceType1", constraints.Value{InstanceType: strp("")}}, 539 {"InstanceType2", constraints.Value{InstanceType: strp("foo")}}, 540 {"Zones1", constraints.Value{Zones: nil}}, 541 {"Zones2", constraints.Value{Zones: &[]string{}}}, 542 {"Zones3", constraints.Value{Zones: &[]string{"az1", "az2"}}}, 543 {"All", constraints.Value{ 544 Arch: strp("i386"), 545 Container: ctypep("lxd"), 546 CpuCores: uint64p(4096), 547 CpuPower: uint64p(9001), 548 Mem: uint64p(18000000000), 549 RootDisk: uint64p(24000000000), 550 Tags: &[]string{"foo", "bar"}, 551 Spaces: &[]string{"space1", "^space2"}, 552 InstanceType: strp("foo"), 553 Zones: &[]string{"az1", "az2"}, 554 }}, 555 } 556 557 func (s *ConstraintsSuite) TestRoundtripGnuflagValue(c *gc.C) { 558 for _, t := range constraintsRoundtripTests { 559 c.Logf("test %s", t.Name) 560 var cons constraints.Value 561 val := constraints.ConstraintsValue{&cons} 562 err := val.Set(t.Value.String()) 563 c.Check(err, jc.ErrorIsNil) 564 c.Check(cons, jc.DeepEquals, t.Value) 565 } 566 } 567 568 func (s *ConstraintsSuite) TestRoundtripString(c *gc.C) { 569 for _, t := range constraintsRoundtripTests { 570 c.Logf("test %s", t.Name) 571 cons, err := constraints.Parse(t.Value.String()) 572 c.Check(err, jc.ErrorIsNil) 573 c.Check(cons, jc.DeepEquals, t.Value) 574 } 575 } 576 577 func (s *ConstraintsSuite) TestRoundtripJson(c *gc.C) { 578 for _, t := range constraintsRoundtripTests { 579 c.Logf("test %s", t.Name) 580 data, err := json.Marshal(t.Value) 581 c.Assert(err, jc.ErrorIsNil) 582 var cons constraints.Value 583 err = json.Unmarshal(data, &cons) 584 c.Check(err, jc.ErrorIsNil) 585 c.Check(cons, jc.DeepEquals, t.Value) 586 } 587 } 588 589 func (s *ConstraintsSuite) TestRoundtripYaml(c *gc.C) { 590 for _, t := range constraintsRoundtripTests { 591 c.Logf("test %s", t.Name) 592 data, err := goyaml.Marshal(t.Value) 593 c.Assert(err, jc.ErrorIsNil) 594 var cons constraints.Value 595 err = goyaml.Unmarshal(data, &cons) 596 c.Check(err, jc.ErrorIsNil) 597 c.Check(cons, jc.DeepEquals, t.Value) 598 } 599 } 600 601 var hasContainerTests = []struct { 602 constraints string 603 hasContainer bool 604 }{ 605 { 606 hasContainer: false, 607 }, { 608 constraints: "container=lxd", 609 hasContainer: true, 610 }, { 611 constraints: "container=none", 612 hasContainer: false, 613 }, 614 } 615 616 func (s *ConstraintsSuite) TestHasContainer(c *gc.C) { 617 for i, t := range hasContainerTests { 618 c.Logf("test %d", i) 619 cons := constraints.MustParse(t.constraints) 620 c.Check(cons.HasContainer(), gc.Equals, t.hasContainer) 621 } 622 } 623 624 func (s *ConstraintsSuite) TestHasInstanceType(c *gc.C) { 625 cons := constraints.MustParse("arch=amd64") 626 c.Check(cons.HasInstanceType(), jc.IsFalse) 627 cons = constraints.MustParse("arch=amd64 instance-type=foo") 628 c.Check(cons.HasInstanceType(), jc.IsTrue) 629 } 630 631 const initialWithoutCons = "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cores=4 spaces=space1,^space2 tags=foo " + 632 "container=lxd instance-type=bar zones=az1,az2" 633 634 var withoutTests = []struct { 635 initial string 636 without []string 637 final string 638 }{{ 639 initial: initialWithoutCons, 640 without: []string{"root-disk"}, 641 final: "mem=4G arch=amd64 cpu-power=1000 cores=4 tags=foo spaces=space1,^space2 container=lxd instance-type=bar zones=az1,az2", 642 }, { 643 initial: initialWithoutCons, 644 without: []string{"mem"}, 645 final: "root-disk=8G arch=amd64 cpu-power=1000 cores=4 tags=foo spaces=space1,^space2 container=lxd instance-type=bar zones=az1,az2", 646 }, { 647 initial: initialWithoutCons, 648 without: []string{"arch"}, 649 final: "root-disk=8G mem=4G cpu-power=1000 cores=4 tags=foo spaces=space1,^space2 container=lxd instance-type=bar zones=az1,az2", 650 }, { 651 initial: initialWithoutCons, 652 without: []string{"cpu-power"}, 653 final: "root-disk=8G mem=4G arch=amd64 cores=4 tags=foo spaces=space1,^space2 container=lxd instance-type=bar zones=az1,az2", 654 }, { 655 initial: initialWithoutCons, 656 without: []string{"cores"}, 657 final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 tags=foo spaces=space1,^space2 container=lxd instance-type=bar zones=az1,az2", 658 }, { 659 initial: initialWithoutCons, 660 without: []string{"tags"}, 661 final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cores=4 spaces=space1,^space2 container=lxd instance-type=bar zones=az1,az2", 662 }, { 663 initial: initialWithoutCons, 664 without: []string{"spaces"}, 665 final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cores=4 tags=foo container=lxd instance-type=bar zones=az1,az2", 666 }, { 667 initial: initialWithoutCons, 668 without: []string{"container"}, 669 final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cores=4 tags=foo spaces=space1,^space2 instance-type=bar zones=az1,az2", 670 }, { 671 initial: initialWithoutCons, 672 without: []string{"instance-type"}, 673 final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cores=4 tags=foo spaces=space1,^space2 container=lxd zones=az1,az2", 674 }, { 675 initial: initialWithoutCons, 676 without: []string{"root-disk", "mem", "arch"}, 677 final: "cpu-power=1000 cores=4 tags=foo spaces=space1,^space2 container=lxd instance-type=bar zones=az1,az2", 678 }, { 679 initial: initialWithoutCons, 680 without: []string{"zones"}, 681 final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cores=4 spaces=space1,^space2 tags=foo container=lxd instance-type=bar", 682 }} 683 684 func (s *ConstraintsSuite) TestWithout(c *gc.C) { 685 for i, t := range withoutTests { 686 c.Logf("test %d", i) 687 initial := constraints.MustParse(t.initial) 688 final := constraints.Without(initial, t.without...) 689 c.Check(final, jc.DeepEquals, constraints.MustParse(t.final)) 690 } 691 } 692 693 var hasAnyTests = []struct { 694 cons string 695 attrs []string 696 expected []string 697 }{ 698 { 699 cons: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 spaces=space1,^space2 cores=4", 700 attrs: []string{"root-disk", "tags", "mem", "spaces"}, 701 expected: []string{"root-disk", "mem", "spaces"}, 702 }, 703 { 704 cons: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cores=4", 705 attrs: []string{"root-disk", "tags", "mem"}, 706 expected: []string{"root-disk", "mem"}, 707 }, 708 { 709 cons: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cores=4", 710 attrs: []string{"tags", "spaces"}, 711 expected: []string{}, 712 }, 713 } 714 715 func (s *ConstraintsSuite) TestHasAny(c *gc.C) { 716 for i, t := range hasAnyTests { 717 c.Logf("test %d", i) 718 cons := constraints.MustParse(t.cons) 719 obtained := constraints.HasAny(cons, t.attrs...) 720 c.Check(obtained, jc.DeepEquals, t.expected) 721 } 722 }