github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/maas/constraints_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package maas 5 6 import ( 7 "net/url" 8 9 "github.com/juju/errors" 10 "github.com/juju/gomaasapi" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/constraints" 15 ) 16 17 func (*environSuite) TestConvertConstraints(c *gc.C) { 18 for i, test := range []struct { 19 cons constraints.Value 20 expected url.Values 21 }{{ 22 cons: constraints.Value{Arch: stringp("arm")}, 23 expected: url.Values{"arch": {"arm"}}, 24 }, { 25 cons: constraints.Value{CpuCores: uint64p(4)}, 26 expected: url.Values{"cpu_count": {"4"}}, 27 }, { 28 cons: constraints.Value{Mem: uint64p(1024)}, 29 expected: url.Values{"mem": {"1024"}}, 30 }, { // Spaces are converted to bindings and not_networks, but only in acquireNode 31 cons: constraints.Value{Spaces: stringslicep("foo", "bar", "^baz", "^oof")}, 32 expected: url.Values{}, 33 }, { 34 cons: constraints.Value{Tags: stringslicep("tag1", "tag2", "^tag3", "^tag4")}, 35 expected: url.Values{ 36 "tags": {"tag1,tag2"}, 37 "not_tags": {"tag3,tag4"}, 38 }, 39 }, { // CpuPower is ignored. 40 cons: constraints.Value{CpuPower: uint64p(1024)}, 41 expected: url.Values{}, 42 }, { // RootDisk is ignored. 43 cons: constraints.Value{RootDisk: uint64p(8192)}, 44 expected: url.Values{}, 45 }, { 46 cons: constraints.Value{Tags: stringslicep("foo", "bar")}, 47 expected: url.Values{"tags": {"foo,bar"}}, 48 }, { 49 cons: constraints.Value{ 50 Arch: stringp("arm"), 51 CpuCores: uint64p(4), 52 Mem: uint64p(1024), 53 CpuPower: uint64p(1024), 54 RootDisk: uint64p(8192), 55 Spaces: stringslicep("foo", "^bar"), 56 Tags: stringslicep("^tag1", "tag2"), 57 }, 58 expected: url.Values{ 59 "arch": {"arm"}, 60 "cpu_count": {"4"}, 61 "mem": {"1024"}, 62 "tags": {"tag2"}, 63 "not_tags": {"tag1"}, 64 }, 65 }} { 66 c.Logf("test #%d: cons=%s", i, test.cons.String()) 67 c.Check(convertConstraints(test.cons), jc.DeepEquals, test.expected) 68 } 69 } 70 71 func (*environSuite) TestConvertConstraints2(c *gc.C) { 72 for i, test := range []struct { 73 cons constraints.Value 74 expected gomaasapi.AllocateMachineArgs 75 }{{ 76 cons: constraints.Value{Arch: stringp("arm")}, 77 expected: gomaasapi.AllocateMachineArgs{Architecture: "arm"}, 78 }, { 79 cons: constraints.Value{CpuCores: uint64p(4)}, 80 expected: gomaasapi.AllocateMachineArgs{MinCPUCount: 4}, 81 }, { 82 cons: constraints.Value{Mem: uint64p(1024)}, 83 expected: gomaasapi.AllocateMachineArgs{MinMemory: 1024}, 84 }, { // Spaces are converted to bindings and not_networks, but only in acquireNode 85 cons: constraints.Value{Spaces: stringslicep("foo", "bar", "^baz", "^oof")}, 86 expected: gomaasapi.AllocateMachineArgs{}, 87 }, { 88 cons: constraints.Value{Tags: stringslicep("tag1", "tag2", "^tag3", "^tag4")}, 89 expected: gomaasapi.AllocateMachineArgs{ 90 Tags: []string{"tag1", "tag2"}, 91 NotTags: []string{"tag3", "tag4"}, 92 }, 93 }, { // CpuPower is ignored. 94 cons: constraints.Value{CpuPower: uint64p(1024)}, 95 expected: gomaasapi.AllocateMachineArgs{}, 96 }, { // RootDisk is ignored. 97 cons: constraints.Value{RootDisk: uint64p(8192)}, 98 expected: gomaasapi.AllocateMachineArgs{}, 99 }, { 100 cons: constraints.Value{Tags: stringslicep("foo", "bar")}, 101 expected: gomaasapi.AllocateMachineArgs{ 102 Tags: []string{"foo", "bar"}, 103 }, 104 }, { 105 cons: constraints.Value{ 106 Arch: stringp("arm"), 107 CpuCores: uint64p(4), 108 Mem: uint64p(1024), 109 CpuPower: uint64p(1024), 110 RootDisk: uint64p(8192), 111 Spaces: stringslicep("foo", "^bar"), 112 Tags: stringslicep("^tag1", "tag2"), 113 }, 114 expected: gomaasapi.AllocateMachineArgs{ 115 Architecture: "arm", 116 MinCPUCount: 4, 117 MinMemory: 1024, 118 Tags: []string{"tag2"}, 119 NotTags: []string{"tag1"}, 120 }, 121 }} { 122 c.Logf("test #%d: cons2=%s", i, test.cons.String()) 123 c.Check(convertConstraints2(test.cons), jc.DeepEquals, test.expected) 124 } 125 } 126 127 var nilStringSlice []string 128 129 func (*environSuite) TestConvertTagsToParams(c *gc.C) { 130 for i, test := range []struct { 131 tags *[]string 132 expected url.Values 133 }{{ 134 tags: nil, 135 expected: url.Values{}, 136 }, { 137 tags: &nilStringSlice, 138 expected: url.Values{}, 139 }, { 140 tags: &[]string{}, 141 expected: url.Values{}, 142 }, { 143 tags: stringslicep(""), 144 expected: url.Values{}, 145 }, { 146 tags: stringslicep("foo"), 147 expected: url.Values{ 148 "tags": {"foo"}, 149 }, 150 }, { 151 tags: stringslicep("^bar"), 152 expected: url.Values{ 153 "not_tags": {"bar"}, 154 }, 155 }, { 156 tags: stringslicep("foo", "^bar", "baz", "^oof"), 157 expected: url.Values{ 158 "tags": {"foo,baz"}, 159 "not_tags": {"bar,oof"}, 160 }, 161 }, { 162 tags: stringslicep("", "^bar", "^", "^oof"), 163 expected: url.Values{ 164 "not_tags": {"bar,oof"}, 165 }, 166 }, { 167 tags: stringslicep("foo", "^", " b a z ", "^^ ^"), 168 expected: url.Values{ 169 "tags": {"foo, b a z "}, 170 "not_tags": {"^ ^"}, 171 }, 172 }, { 173 tags: stringslicep("", "^bar", " ", " ^ o of "), 174 expected: url.Values{ 175 "tags": {" , ^ o of "}, 176 "not_tags": {"bar"}, 177 }, 178 }, { 179 tags: stringslicep("foo", "foo", "^bar", "^bar"), 180 expected: url.Values{ 181 "tags": {"foo,foo"}, 182 "not_tags": {"bar,bar"}, 183 }, 184 }} { 185 c.Logf("test #%d: tags=%v", i, test.tags) 186 var vals = url.Values{} 187 convertTagsToParams(vals, test.tags) 188 c.Check(vals, jc.DeepEquals, test.expected) 189 } 190 } 191 192 func uint64p(val uint64) *uint64 { 193 return &val 194 } 195 196 func stringp(val string) *string { 197 return &val 198 } 199 200 func stringslicep(values ...string) *[]string { 201 return &values 202 } 203 204 func (suite *environSuite) TestSelectNodeValidZone(c *gc.C) { 205 env := suite.makeEnviron() 206 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0", "zone": "bar"}`) 207 208 snArgs := selectNodeArgs{ 209 AvailabilityZones: []string{"foo", "bar"}, 210 Constraints: constraints.Value{}, 211 } 212 213 node, err := env.selectNode(snArgs) 214 c.Assert(err, jc.ErrorIsNil) 215 c.Assert(node, gc.NotNil) 216 } 217 218 func (suite *environSuite) TestSelectNodeInvalidZone(c *gc.C) { 219 env := suite.makeEnviron() 220 221 snArgs := selectNodeArgs{ 222 AvailabilityZones: []string{"foo", "bar"}, 223 Constraints: constraints.Value{}, 224 } 225 226 _, err := env.selectNode(snArgs) 227 c.Assert(err, gc.NotNil) 228 c.Assert(err, gc.ErrorMatches, `cannot run instances: ServerError: 409 Conflict \(\)`) 229 } 230 231 func (suite *environSuite) TestAcquireNode(c *gc.C) { 232 env := suite.makeEnviron() 233 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 234 235 _, err := env.acquireNode("", "", constraints.Value{}, nil, nil) 236 237 c.Check(err, jc.ErrorIsNil) 238 operations := suite.testMAASObject.TestServer.NodeOperations() 239 actions, found := operations["node0"] 240 c.Assert(found, jc.IsTrue) 241 c.Check(actions, gc.DeepEquals, []string{"acquire"}) 242 243 // no "name" parameter should have been passed through 244 values := suite.testMAASObject.TestServer.NodeOperationRequestValues()["node0"][0] 245 _, found = values["name"] 246 c.Assert(found, jc.IsFalse) 247 } 248 249 func (suite *environSuite) TestAcquireNodeByName(c *gc.C) { 250 env := suite.makeEnviron() 251 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 252 253 _, err := env.acquireNode("host0", "", constraints.Value{}, nil, nil) 254 255 c.Check(err, jc.ErrorIsNil) 256 operations := suite.testMAASObject.TestServer.NodeOperations() 257 actions, found := operations["node0"] 258 c.Assert(found, jc.IsTrue) 259 c.Check(actions, gc.DeepEquals, []string{"acquire"}) 260 261 // no "name" parameter should have been passed through 262 values := suite.testMAASObject.TestServer.NodeOperationRequestValues()["node0"][0] 263 nodeName := values.Get("name") 264 c.Assert(nodeName, gc.Equals, "host0") 265 } 266 267 func (suite *environSuite) TestAcquireNodeTakesConstraintsIntoAccount(c *gc.C) { 268 env := suite.makeEnviron() 269 suite.testMAASObject.TestServer.NewNode( 270 `{"system_id": "node0", "hostname": "host0", "architecture": "arm/generic", "memory": 2048}`, 271 ) 272 constraints := constraints.Value{Arch: stringp("arm"), Mem: uint64p(1024)} 273 274 _, err := env.acquireNode("", "", constraints, nil, nil) 275 276 c.Check(err, jc.ErrorIsNil) 277 requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() 278 nodeRequestValues, found := requestValues["node0"] 279 c.Assert(found, jc.IsTrue) 280 c.Assert(nodeRequestValues[0].Get("arch"), gc.Equals, "arm") 281 c.Assert(nodeRequestValues[0].Get("mem"), gc.Equals, "1024") 282 } 283 284 func (suite *environSuite) TestParseDelimitedValues(c *gc.C) { 285 for i, test := range []struct { 286 about string 287 input []string 288 positives []string 289 negatives []string 290 }{{ 291 about: "nil input", 292 input: nil, 293 positives: []string{}, 294 negatives: []string{}, 295 }, { 296 about: "empty input", 297 input: []string{}, 298 positives: []string{}, 299 negatives: []string{}, 300 }, { 301 about: "values list with embedded whitespace", 302 input: []string{" val1 ", " val2", " ^ not Val 3 ", " ", " ", "^", "", "^ notVal4 "}, 303 positives: []string{" val1 ", " val2", " ^ not Val 3 ", " ", " "}, 304 negatives: []string{" notVal4 "}, 305 }, { 306 about: "only positives", 307 input: []string{"val1", "val2", "val3"}, 308 positives: []string{"val1", "val2", "val3"}, 309 negatives: []string{}, 310 }, { 311 about: "only negatives", 312 input: []string{"^val1", "^val2", "^val3"}, 313 positives: []string{}, 314 negatives: []string{"val1", "val2", "val3"}, 315 }, { 316 about: "multi-caret negatives", 317 input: []string{"^foo^", "^v^a^l2", " ^^ ^", "^v^al3", "^^", "^"}, 318 positives: []string{" ^^ ^"}, 319 negatives: []string{"foo^", "v^a^l2", "v^al3", "^"}, 320 }, { 321 about: "both positives and negatives", 322 input: []string{"^val1", "val2", "^val3", "val4"}, 323 positives: []string{"val2", "val4"}, 324 negatives: []string{"val1", "val3"}, 325 }, { 326 about: "single positive value", 327 input: []string{"val1"}, 328 positives: []string{"val1"}, 329 negatives: []string{}, 330 }, { 331 about: "single negative value", 332 input: []string{"^val1"}, 333 positives: []string{}, 334 negatives: []string{"val1"}, 335 }} { 336 c.Logf("test %d: %s", i, test.about) 337 positives, negatives := parseDelimitedValues(test.input) 338 c.Check(positives, jc.DeepEquals, test.positives) 339 c.Check(negatives, jc.DeepEquals, test.negatives) 340 } 341 } 342 343 func (suite *environSuite) TestAcquireNodePassedAgentName(c *gc.C) { 344 env := suite.makeEnviron() 345 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 346 347 _, err := env.acquireNode("", "", constraints.Value{}, nil, nil) 348 349 c.Check(err, jc.ErrorIsNil) 350 requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() 351 nodeRequestValues, found := requestValues["node0"] 352 c.Assert(found, jc.IsTrue) 353 c.Assert(nodeRequestValues[0].Get("agent_name"), gc.Equals, exampleAgentName) 354 } 355 356 func (suite *environSuite) TestAcquireNodePassesPositiveAndNegativeTags(c *gc.C) { 357 env := suite.makeEnviron() 358 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0"}`) 359 360 _, err := env.acquireNode( 361 "", "", 362 constraints.Value{Tags: stringslicep("tag1", "^tag2", "tag3", "^tag4")}, 363 nil, nil, 364 ) 365 366 c.Check(err, jc.ErrorIsNil) 367 requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() 368 nodeValues, found := requestValues["node0"] 369 c.Assert(found, jc.IsTrue) 370 c.Assert(nodeValues[0].Get("tags"), gc.Equals, "tag1,tag3") 371 c.Assert(nodeValues[0].Get("not_tags"), gc.Equals, "tag2,tag4") 372 } 373 374 func (suite *environSuite) TestAcquireNodePassesPositiveAndNegativeSpaces(c *gc.C) { 375 suite.createFourSpaces(c) 376 env := suite.makeEnviron() 377 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0"}`) 378 379 _, err := env.acquireNode( 380 "", "", 381 constraints.Value{Spaces: stringslicep("space-1", "^space-2", "space-3", "^space-4")}, 382 nil, nil, 383 ) 384 c.Check(err, jc.ErrorIsNil) 385 requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() 386 nodeValues, found := requestValues["node0"] 387 c.Assert(found, jc.IsTrue) 388 c.Check(nodeValues[0].Get("interfaces"), gc.Equals, "0:space=2;1:space=4") 389 c.Check(nodeValues[0].Get("not_networks"), gc.Equals, "space:3,space:5") 390 } 391 392 func (suite *environSuite) createFourSpaces(c *gc.C) { 393 server := suite.testMAASObject.TestServer 394 server.SetVersionJSON(`{"capabilities": ["network-deployment-ubuntu"]}`) 395 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "space-1"})) 396 suite.addSubnet(c, 1, 1, "node1") 397 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "space-2"})) 398 suite.addSubnet(c, 2, 2, "node1") 399 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "space-3"})) 400 suite.addSubnet(c, 3, 3, "node1") 401 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "space-4"})) 402 suite.addSubnet(c, 4, 4, "node1") 403 } 404 405 func (suite *environSuite) TestAcquireNodeDisambiguatesNamedLabelsFromIndexedUpToALimit(c *gc.C) { 406 suite.createFourSpaces(c) 407 var shortLimit uint = 0 408 suite.PatchValue(&numericLabelLimit, shortLimit) 409 env := suite.makeEnviron() 410 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0"}`) 411 412 _, err := env.acquireNode( 413 "", "", 414 constraints.Value{Spaces: stringslicep("space-1", "^space-2", "space-3", "^space-4")}, 415 []interfaceBinding{{"0", "first-clash"}, {"1", "final-clash"}}, 416 nil, 417 ) 418 c.Assert(err, gc.ErrorMatches, `too many conflicting numeric labels, giving up.`) 419 } 420 421 func (suite *environSuite) TestAcquireNodeStorage(c *gc.C) { 422 server := suite.testMAASObject.TestServer 423 for i, test := range []struct { 424 volumes []volumeInfo 425 expected string 426 }{{ 427 volumes: nil, 428 expected: "", 429 }, { 430 volumes: []volumeInfo{{"volume-1", 1234, nil}}, 431 expected: "volume-1:1234", 432 }, { 433 volumes: []volumeInfo{{"", 1234, []string{"tag1", "tag2"}}}, 434 expected: "1234(tag1,tag2)", 435 }, { 436 volumes: []volumeInfo{{"volume-1", 1234, []string{"tag1", "tag2"}}}, 437 expected: "volume-1:1234(tag1,tag2)", 438 }, { 439 volumes: []volumeInfo{ 440 {"volume-1", 1234, []string{"tag1", "tag2"}}, 441 {"volume-2", 4567, []string{"tag1", "tag3"}}, 442 }, 443 expected: "volume-1:1234(tag1,tag2),volume-2:4567(tag1,tag3)", 444 }} { 445 c.Logf("test #%d: volumes=%v", i, test.volumes) 446 env := suite.makeEnviron() 447 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "space-1"})) 448 server.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 449 suite.addSubnet(c, 1, 1, "node0") 450 _, err := env.acquireNode("", "", constraints.Value{}, nil, test.volumes) 451 c.Check(err, jc.ErrorIsNil) 452 requestValues := server.NodeOperationRequestValues() 453 nodeRequestValues, found := requestValues["node0"] 454 if c.Check(found, jc.IsTrue) { 455 c.Check(nodeRequestValues[0].Get("storage"), gc.Equals, test.expected) 456 } 457 suite.testMAASObject.TestServer.Clear() 458 } 459 } 460 461 func (suite *environSuite) TestAcquireNodeInterfaces(c *gc.C) { 462 server := suite.testMAASObject.TestServer 463 // Add some constraints, including spaces to verify specified bindings 464 // always override any spaces constraints. 465 cons := constraints.Value{ 466 Spaces: stringslicep("foo", "^bar"), 467 } 468 // In the tests below "space:5" means foo, "space:6" means bar. 469 for i, test := range []struct { 470 interfaces []interfaceBinding 471 expectedPositives string 472 expectedNegatives string 473 expectedError string 474 }{{ // without specified bindings, spaces constraints are used instead. 475 interfaces: nil, 476 expectedPositives: "0:space=5", 477 expectedNegatives: "space:6", 478 expectedError: "", 479 }, { 480 interfaces: []interfaceBinding{{"name-1", "space-1"}}, 481 expectedPositives: "name-1:space=space-1;0:space=5", 482 expectedNegatives: "space:6", 483 }, { 484 interfaces: []interfaceBinding{ 485 {"name-1", "1"}, 486 {"name-2", "2"}, 487 {"name-3", "3"}, 488 }, 489 expectedPositives: "name-1:space=1;name-2:space=2;name-3:space=3;0:space=5", 490 expectedNegatives: "space:6", 491 }, { 492 interfaces: []interfaceBinding{{"", "anything"}}, 493 expectedError: "interface bindings cannot have empty names", 494 }, { 495 interfaces: []interfaceBinding{{"shared-db", "6"}}, 496 expectedError: `negative space "bar" from constraints clashes with interface bindings`, 497 }, { 498 interfaces: []interfaceBinding{ 499 {"shared-db", "1"}, 500 {"db", "1"}, 501 }, 502 expectedPositives: "shared-db:space=1;db:space=1;0:space=5", 503 expectedNegatives: "space:6", 504 }, { 505 interfaces: []interfaceBinding{{"", ""}}, 506 expectedError: "interface bindings cannot have empty names", 507 }, { 508 interfaces: []interfaceBinding{ 509 {"valid", "ok"}, 510 {"", "valid-but-ignored-space"}, 511 {"valid-name-empty-space", ""}, 512 {"", ""}, 513 }, 514 expectedError: "interface bindings cannot have empty names", 515 }, { 516 interfaces: []interfaceBinding{{"foo", ""}}, 517 expectedError: `invalid interface binding "foo": space provider ID is required`, 518 }, { 519 interfaces: []interfaceBinding{ 520 {"bar", ""}, 521 {"valid", "ok"}, 522 {"", "valid-but-ignored-space"}, 523 {"", ""}, 524 }, 525 expectedError: `invalid interface binding "bar": space provider ID is required`, 526 }, { 527 interfaces: []interfaceBinding{ 528 {"dup-name", "1"}, 529 {"dup-name", "2"}, 530 }, 531 expectedError: `duplicated interface binding "dup-name"`, 532 }, { 533 interfaces: []interfaceBinding{ 534 {"valid-1", "0"}, 535 {"dup-name", "1"}, 536 {"dup-name", "2"}, 537 {"valid-2", "3"}, 538 }, 539 expectedError: `duplicated interface binding "dup-name"`, 540 }} { 541 suite.testMAASObject.TestServer.Clear() 542 c.Logf("test #%d: interfaces=%v", i, test.interfaces) 543 suite.createFourSpaces(c) 544 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "foo"})) 545 suite.addSubnetWithSpace(c, 6, 6, "foo", "node1") 546 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "bar"})) 547 suite.addSubnetWithSpace(c, 7, 7, "bar", "node1") 548 env := suite.makeEnviron() 549 server.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 550 _, err := env.acquireNode("", "", cons, test.interfaces, nil) 551 if test.expectedError != "" { 552 c.Check(err, gc.ErrorMatches, test.expectedError) 553 c.Check(err, jc.Satisfies, errors.IsNotValid) 554 continue 555 } 556 c.Check(err, jc.ErrorIsNil) 557 requestValues := server.NodeOperationRequestValues() 558 nodeRequestValues, found := requestValues["node0"] 559 if c.Check(found, jc.IsTrue) { 560 561 c.Check(nodeRequestValues[0].Get("interfaces"), gc.Equals, test.expectedPositives) 562 c.Check(nodeRequestValues[0].Get("not_networks"), gc.Equals, test.expectedNegatives) 563 } 564 } 565 } 566 567 func (suite *environSuite) createFooBarSpaces(c *gc.C) { 568 server := suite.testMAASObject.TestServer 569 server.SetVersionJSON(`{"capabilities": ["network-deployment-ubuntu"]}`) 570 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "foo"})) 571 suite.addSubnetWithSpace(c, 1, 2, "foo", "node1") 572 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "bar"})) 573 suite.addSubnetWithSpace(c, 2, 3, "bar", "node1") 574 } 575 576 func (suite *environSuite) TestAcquireNodeConvertsSpaceNames(c *gc.C) { 577 server := suite.testMAASObject.TestServer 578 suite.createFooBarSpaces(c) 579 cons := constraints.Value{ 580 Spaces: stringslicep("foo", "^bar"), 581 } 582 env := suite.makeEnviron() 583 server.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 584 _, err := env.acquireNode("", "", cons, nil, nil) 585 c.Assert(err, jc.ErrorIsNil) 586 requestValues := server.NodeOperationRequestValues() 587 nodeRequestValues, found := requestValues["node0"] 588 c.Assert(found, jc.IsTrue) 589 c.Check(nodeRequestValues[0].Get("interfaces"), gc.Equals, "0:space=2") 590 c.Check(nodeRequestValues[0].Get("not_networks"), gc.Equals, "space:3") 591 } 592 593 func (suite *environSuite) TestAcquireNodeTranslatesSpaceNames(c *gc.C) { 594 server := suite.testMAASObject.TestServer 595 suite.createFooBarSpaces(c) 596 cons := constraints.Value{ 597 Spaces: stringslicep("foo-1", "^bar-3"), 598 } 599 env := suite.makeEnviron() 600 server.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 601 _, err := env.acquireNode("", "", cons, nil, nil) 602 c.Assert(err, jc.ErrorIsNil) 603 requestValues := server.NodeOperationRequestValues() 604 nodeRequestValues, found := requestValues["node0"] 605 c.Assert(found, jc.IsTrue) 606 c.Check(nodeRequestValues[0].Get("interfaces"), gc.Equals, "0:space=2") 607 c.Check(nodeRequestValues[0].Get("not_networks"), gc.Equals, "space:3") 608 } 609 610 func (suite *environSuite) TestAcquireNodeUnrecognisedSpace(c *gc.C) { 611 server := suite.testMAASObject.TestServer 612 suite.createFooBarSpaces(c) 613 cons := constraints.Value{ 614 Spaces: stringslicep("baz"), 615 } 616 env := suite.makeEnviron() 617 server.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 618 _, err := env.acquireNode("", "", cons, nil, nil) 619 c.Assert(err, gc.ErrorMatches, `unrecognised space in constraint "baz"`) 620 }