github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/maas/maas2_environ_whitebox_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package maas 5 6 import ( 7 "fmt" 8 "net/http" 9 10 "github.com/juju/errors" 11 "github.com/juju/gomaasapi" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils/arch" 15 "github.com/juju/utils/set" 16 "github.com/juju/version" 17 gc "gopkg.in/check.v1" 18 "gopkg.in/juju/names.v2" 19 goyaml "gopkg.in/yaml.v2" 20 21 "github.com/juju/juju/cloud" 22 "github.com/juju/juju/cloudconfig/cloudinit" 23 "github.com/juju/juju/constraints" 24 "github.com/juju/juju/environs" 25 "github.com/juju/juju/environs/bootstrap" 26 "github.com/juju/juju/environs/config" 27 "github.com/juju/juju/environs/tags" 28 envjujutesting "github.com/juju/juju/environs/testing" 29 envtools "github.com/juju/juju/environs/tools" 30 "github.com/juju/juju/instance" 31 jujutesting "github.com/juju/juju/juju/testing" 32 "github.com/juju/juju/network" 33 coretesting "github.com/juju/juju/testing" 34 ) 35 36 type maas2EnvironSuite struct { 37 maas2Suite 38 } 39 40 var _ = gc.Suite(&maas2EnvironSuite{}) 41 42 func (suite *maas2EnvironSuite) getEnvWithServer(c *gc.C) (*maasEnviron, error) { 43 testServer := gomaasapi.NewSimpleServer() 44 testServer.AddGetResponse("/api/2.0/version/", http.StatusOK, maas2VersionResponse) 45 testServer.AddGetResponse("/api/2.0/users/?op=whoami", http.StatusOK, "{}") 46 // Weirdly, rather than returning a 404 when the version is 47 // unknown, MAAS2 returns some HTML (the login page). 48 testServer.AddGetResponse("/api/1.0/version/", http.StatusOK, "<html></html>") 49 testServer.Start() 50 suite.AddCleanup(func(*gc.C) { testServer.Close() }) 51 cred := cloud.NewCredential(cloud.OAuth1AuthType, map[string]string{ 52 "maas-oauth": "a:b:c", 53 }) 54 cloud := environs.CloudSpec{ 55 Type: "maas", 56 Name: "maas", 57 Endpoint: testServer.Server.URL, 58 Credential: &cred, 59 } 60 attrs := coretesting.FakeConfig().Merge(maasEnvAttrs) 61 cfg, err := config.New(config.NoDefaults, attrs) 62 c.Assert(err, jc.ErrorIsNil) 63 return NewEnviron(cloud, cfg) 64 } 65 66 func (suite *maas2EnvironSuite) TestNewEnvironWithController(c *gc.C) { 67 env, err := suite.getEnvWithServer(c) 68 c.Assert(err, jc.ErrorIsNil) 69 c.Assert(env, gc.NotNil) 70 } 71 72 func (suite *maas2EnvironSuite) injectControllerWithSpacesAndCheck(c *gc.C, spaces []gomaasapi.Space, expected gomaasapi.AllocateMachineArgs) (*maasEnviron, *fakeController) { 73 var env *maasEnviron 74 check := func(args gomaasapi.AllocateMachineArgs) { 75 expected.AgentName = env.Config().UUID() 76 c.Assert(args, gc.DeepEquals, expected) 77 } 78 controller := &fakeController{ 79 allocateMachineArgsCheck: check, 80 allocateMachine: newFakeMachine("Bruce Sterling", arch.HostArch(), ""), 81 allocateMachineMatches: gomaasapi.ConstraintMatches{ 82 Storage: map[string][]gomaasapi.BlockDevice{}, 83 }, 84 spaces: spaces, 85 } 86 suite.injectController(controller) 87 suite.setupFakeTools(c) 88 env = suite.makeEnviron(c, nil) 89 return env, controller 90 } 91 92 func (suite *maas2EnvironSuite) makeEnvironWithMachines(c *gc.C, expectedSystemIDs []string, returnSystemIDs []string) *maasEnviron { 93 var env *maasEnviron 94 checkArgs := func(args gomaasapi.MachinesArgs) { 95 c.Check(args.SystemIDs, gc.DeepEquals, expectedSystemIDs) 96 c.Check(args.AgentName, gc.Equals, env.Config().UUID()) 97 } 98 machines := make([]gomaasapi.Machine, len(returnSystemIDs)) 99 for index, id := range returnSystemIDs { 100 machines[index] = &fakeMachine{systemID: id} 101 } 102 controller := &fakeController{ 103 machines: machines, 104 machinesArgsCheck: checkArgs, 105 } 106 env = suite.makeEnviron(c, controller) 107 return env 108 } 109 110 func (suite *maas2EnvironSuite) TestAllInstances(c *gc.C) { 111 env := suite.makeEnvironWithMachines( 112 c, []string{}, []string{"tuco", "tio", "gus"}, 113 ) 114 result, err := env.AllInstances() 115 c.Assert(err, jc.ErrorIsNil) 116 expectedMachines := set.NewStrings("tuco", "tio", "gus") 117 actualMachines := set.NewStrings() 118 for _, instance := range result { 119 actualMachines.Add(string(instance.Id())) 120 } 121 c.Assert(actualMachines, gc.DeepEquals, expectedMachines) 122 } 123 124 func (suite *maas2EnvironSuite) TestAllInstancesError(c *gc.C) { 125 controller := &fakeController{machinesError: errors.New("Something terrible!")} 126 env := suite.makeEnviron(c, controller) 127 _, err := env.AllInstances() 128 c.Assert(err, gc.ErrorMatches, "Something terrible!") 129 } 130 131 func (suite *maas2EnvironSuite) TestInstances(c *gc.C) { 132 env := suite.makeEnvironWithMachines( 133 c, []string{"jake", "bonnibel"}, []string{"jake", "bonnibel"}, 134 ) 135 result, err := env.Instances([]instance.Id{"jake", "bonnibel"}) 136 c.Assert(err, jc.ErrorIsNil) 137 expectedMachines := set.NewStrings("jake", "bonnibel") 138 actualMachines := set.NewStrings() 139 for _, machine := range result { 140 actualMachines.Add(string(machine.Id())) 141 } 142 c.Assert(actualMachines, gc.DeepEquals, expectedMachines) 143 } 144 145 func (suite *maas2EnvironSuite) TestInstancesPartialResult(c *gc.C) { 146 env := suite.makeEnvironWithMachines( 147 c, []string{"jake", "bonnibel"}, []string{"tuco", "bonnibel"}, 148 ) 149 result, err := env.Instances([]instance.Id{"jake", "bonnibel"}) 150 c.Check(err, gc.Equals, environs.ErrPartialInstances) 151 c.Assert(result, gc.HasLen, 2) 152 c.Assert(result[0], gc.IsNil) 153 c.Assert(result[1].Id(), gc.Equals, instance.Id("bonnibel")) 154 } 155 156 func (suite *maas2EnvironSuite) TestAvailabilityZones(c *gc.C) { 157 controller := &fakeController{ 158 zones: []gomaasapi.Zone{ 159 &fakeZone{name: "mossack"}, 160 &fakeZone{name: "fonseca"}, 161 }, 162 } 163 env := suite.makeEnviron(c, controller) 164 result, err := env.AvailabilityZones() 165 c.Assert(err, jc.ErrorIsNil) 166 expectedZones := set.NewStrings("mossack", "fonseca") 167 actualZones := set.NewStrings() 168 for _, zone := range result { 169 actualZones.Add(zone.Name()) 170 } 171 c.Assert(actualZones, gc.DeepEquals, expectedZones) 172 } 173 174 func (suite *maas2EnvironSuite) TestAvailabilityZonesError(c *gc.C) { 175 controller := &fakeController{ 176 zonesError: errors.New("a bad thing"), 177 } 178 env := suite.makeEnviron(c, controller) 179 _, err := env.AvailabilityZones() 180 c.Assert(err, gc.ErrorMatches, "a bad thing") 181 } 182 183 func (suite *maas2EnvironSuite) TestSpaces(c *gc.C) { 184 controller := &fakeController{ 185 spaces: []gomaasapi.Space{ 186 fakeSpace{ 187 name: "pepper", 188 id: 1234, 189 }, 190 fakeSpace{ 191 name: "freckles", 192 id: 4567, 193 subnets: []gomaasapi.Subnet{ 194 fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24"}, 195 fakeSubnet{id: 98, vlan: fakeVLAN{vid: 67}, cidr: "192.168.11.0/24"}, 196 }, 197 }, 198 }, 199 } 200 env := suite.makeEnviron(c, controller) 201 result, err := env.Spaces() 202 c.Assert(err, jc.ErrorIsNil) 203 c.Assert(result, gc.HasLen, 1) 204 c.Assert(result[0].Name, gc.Equals, "freckles") 205 c.Assert(result[0].ProviderId, gc.Equals, network.Id("4567")) 206 subnets := result[0].Subnets 207 c.Assert(subnets, gc.HasLen, 2) 208 c.Assert(subnets[0].ProviderId, gc.Equals, network.Id("99")) 209 c.Assert(subnets[0].VLANTag, gc.Equals, 66) 210 c.Assert(subnets[0].CIDR, gc.Equals, "192.168.10.0/24") 211 c.Assert(subnets[0].SpaceProviderId, gc.Equals, network.Id("4567")) 212 c.Assert(subnets[1].ProviderId, gc.Equals, network.Id("98")) 213 c.Assert(subnets[1].VLANTag, gc.Equals, 67) 214 c.Assert(subnets[1].CIDR, gc.Equals, "192.168.11.0/24") 215 c.Assert(subnets[1].SpaceProviderId, gc.Equals, network.Id("4567")) 216 } 217 218 func (suite *maas2EnvironSuite) TestSpacesError(c *gc.C) { 219 controller := &fakeController{ 220 spacesError: errors.New("Joe Manginiello"), 221 } 222 env := suite.makeEnviron(c, controller) 223 _, err := env.Spaces() 224 c.Assert(err, gc.ErrorMatches, "Joe Manginiello") 225 } 226 227 func collectReleaseArgs(controller *fakeController) []gomaasapi.ReleaseMachinesArgs { 228 args := []gomaasapi.ReleaseMachinesArgs{} 229 for _, call := range controller.Stub.Calls() { 230 if call.FuncName == "ReleaseMachines" { 231 args = append(args, call.Args[0].(gomaasapi.ReleaseMachinesArgs)) 232 } 233 } 234 return args 235 } 236 237 func (suite *maas2EnvironSuite) TestStopInstancesReturnsIfParameterEmpty(c *gc.C) { 238 controller := newFakeController() 239 err := suite.makeEnviron(c, controller).StopInstances() 240 c.Check(err, jc.ErrorIsNil) 241 c.Assert(collectReleaseArgs(controller), gc.HasLen, 0) 242 } 243 244 func (suite *maas2EnvironSuite) TestStopInstancesStopsAndReleasesInstances(c *gc.C) { 245 // Return a cannot complete indicating that test1 is in the wrong state. 246 // The release operation will still release the others and succeed. 247 controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"}) 248 err := suite.makeEnviron(c, controller).StopInstances("test1", "test2", "test3") 249 c.Check(err, jc.ErrorIsNil) 250 args := collectReleaseArgs(controller) 251 c.Assert(args, gc.HasLen, 1) 252 c.Assert(args[0].SystemIDs, gc.DeepEquals, []string{"test1", "test2", "test3"}) 253 } 254 255 func (suite *maas2EnvironSuite) TestStopInstancesIgnoresConflict(c *gc.C) { 256 // Return a cannot complete indicating that test1 is in the wrong state. 257 // The release operation will still release the others and succeed. 258 controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"}) 259 controller.SetErrors(gomaasapi.NewCannotCompleteError("test1 not allocated")) 260 err := suite.makeEnviron(c, controller).StopInstances("test1", "test2", "test3") 261 c.Check(err, jc.ErrorIsNil) 262 263 args := collectReleaseArgs(controller) 264 c.Assert(args, gc.HasLen, 1) 265 c.Assert(args[0].SystemIDs, gc.DeepEquals, []string{"test1", "test2", "test3"}) 266 } 267 268 func (suite *maas2EnvironSuite) TestStopInstancesIgnoresMissingNodeAndRecurses(c *gc.C) { 269 controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"}) 270 controller.SetErrors( 271 gomaasapi.NewBadRequestError("no such machine: test1"), 272 gomaasapi.NewBadRequestError("no such machine: test1"), 273 ) 274 err := suite.makeEnviron(c, controller).StopInstances("test1", "test2", "test3") 275 c.Check(err, jc.ErrorIsNil) 276 args := collectReleaseArgs(controller) 277 c.Assert(args, gc.HasLen, 4) 278 c.Assert(args[0].SystemIDs, gc.DeepEquals, []string{"test1", "test2", "test3"}) 279 c.Assert(args[1].SystemIDs, gc.DeepEquals, []string{"test1"}) 280 c.Assert(args[2].SystemIDs, gc.DeepEquals, []string{"test2"}) 281 c.Assert(args[3].SystemIDs, gc.DeepEquals, []string{"test3"}) 282 } 283 284 func (suite *maas2EnvironSuite) checkStopInstancesFails(c *gc.C, withError error) { 285 controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"}) 286 controller.SetErrors(withError) 287 err := suite.makeEnviron(c, controller).StopInstances("test1", "test2", "test3") 288 c.Check(err, gc.ErrorMatches, fmt.Sprintf("cannot release nodes: %s", withError)) 289 // Only tries once. 290 c.Assert(collectReleaseArgs(controller), gc.HasLen, 1) 291 } 292 293 func (suite *maas2EnvironSuite) TestStopInstancesReturnsUnexpectedMAASError(c *gc.C) { 294 suite.checkStopInstancesFails(c, gomaasapi.NewNoMatchError("Something else bad!")) 295 } 296 297 func (suite *maas2EnvironSuite) TestStopInstancesReturnsUnexpectedError(c *gc.C) { 298 suite.checkStopInstancesFails(c, errors.New("Something completely unexpected!")) 299 } 300 301 func (suite *maas2EnvironSuite) TestStartInstanceError(c *gc.C) { 302 suite.injectController(&fakeController{ 303 allocateMachineError: errors.New("Charles Babbage"), 304 }) 305 env := suite.makeEnviron(c, nil) 306 _, err := env.StartInstance(environs.StartInstanceParams{}) 307 c.Assert(err, gc.ErrorMatches, ".* cannot run instance: Charles Babbage") 308 } 309 310 func (suite *maas2EnvironSuite) TestStartInstance(c *gc.C) { 311 env, _ := suite.injectControllerWithSpacesAndCheck(c, nil, gomaasapi.AllocateMachineArgs{}) 312 313 params := environs.StartInstanceParams{ControllerUUID: suite.controllerUUID} 314 result, err := jujutesting.StartInstanceWithParams(env, "1", params) 315 c.Assert(err, jc.ErrorIsNil) 316 c.Assert(result.Instance.Id(), gc.Equals, instance.Id("Bruce Sterling")) 317 } 318 319 func (suite *maas2EnvironSuite) TestStartInstanceAppliesResourceTags(c *gc.C) { 320 env, controller := suite.injectControllerWithSpacesAndCheck(c, nil, gomaasapi.AllocateMachineArgs{}) 321 config := env.Config() 322 _, ok := config.ResourceTags() 323 c.Assert(ok, jc.IsTrue) 324 params := environs.StartInstanceParams{ControllerUUID: suite.controllerUUID} 325 _, err := jujutesting.StartInstanceWithParams(env, "1", params) 326 c.Assert(err, jc.ErrorIsNil) 327 328 machine := controller.allocateMachine.(*fakeMachine) 329 machine.CheckCallNames(c, "Start", "SetOwnerData") 330 c.Assert(machine.Calls()[1].Args[0], gc.DeepEquals, map[string]string{ 331 "claude": "rains", 332 tags.JujuController: suite.controllerUUID, 333 tags.JujuModel: config.UUID(), 334 }) 335 } 336 337 func (suite *maas2EnvironSuite) TestStartInstanceParams(c *gc.C) { 338 var env *maasEnviron 339 suite.injectController(&fakeController{ 340 allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) { 341 c.Assert(args, gc.DeepEquals, gomaasapi.AllocateMachineArgs{ 342 AgentName: env.Config().UUID(), 343 Zone: "foo", 344 MinMemory: 8192, 345 }) 346 }, 347 allocateMachine: newFakeMachine("Bruce Sterling", arch.HostArch(), ""), 348 allocateMachineMatches: gomaasapi.ConstraintMatches{ 349 Storage: map[string][]gomaasapi.BlockDevice{}, 350 }, 351 zones: []gomaasapi.Zone{&fakeZone{name: "foo"}}, 352 }) 353 suite.setupFakeTools(c) 354 env = suite.makeEnviron(c, nil) 355 params := environs.StartInstanceParams{ 356 ControllerUUID: suite.controllerUUID, 357 Placement: "zone=foo", 358 Constraints: constraints.MustParse("mem=8G"), 359 } 360 result, err := jujutesting.StartInstanceWithParams(env, "1", params) 361 c.Assert(err, jc.ErrorIsNil) 362 c.Assert(result.Instance.Id(), gc.Equals, instance.Id("Bruce Sterling")) 363 } 364 365 func (suite *maas2EnvironSuite) TestAcquireNodePassedAgentName(c *gc.C) { 366 var env *maasEnviron 367 suite.injectController(&fakeController{ 368 allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) { 369 c.Assert(args, gc.DeepEquals, gomaasapi.AllocateMachineArgs{ 370 AgentName: env.Config().UUID()}) 371 }, 372 allocateMachine: &fakeMachine{ 373 systemID: "Bruce Sterling", 374 architecture: arch.HostArch(), 375 }, 376 }) 377 suite.setupFakeTools(c) 378 env = suite.makeEnviron(c, nil) 379 380 _, err := env.acquireNode2("", "", constraints.Value{}, nil, nil) 381 382 c.Check(err, jc.ErrorIsNil) 383 } 384 385 func (suite *maas2EnvironSuite) TestAcquireNodePassesPositiveAndNegativeTags(c *gc.C) { 386 var env *maasEnviron 387 expected := gomaasapi.AllocateMachineArgs{ 388 Tags: []string{"tag1", "tag3"}, 389 NotTags: []string{"tag2", "tag4"}, 390 } 391 env, _ = suite.injectControllerWithSpacesAndCheck(c, nil, expected) 392 _, err := env.acquireNode2( 393 "", "", 394 constraints.Value{Tags: stringslicep("tag1", "^tag2", "tag3", "^tag4")}, 395 nil, nil, 396 ) 397 c.Check(err, jc.ErrorIsNil) 398 } 399 400 func getFourSpaces() []gomaasapi.Space { 401 return []gomaasapi.Space{ 402 fakeSpace{ 403 name: "space-1", 404 subnets: []gomaasapi.Subnet{fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24"}}, 405 id: 5, 406 }, 407 fakeSpace{ 408 name: "space-2", 409 subnets: []gomaasapi.Subnet{fakeSubnet{id: 100, vlan: fakeVLAN{vid: 66}, cidr: "192.168.11.0/24"}}, 410 id: 6, 411 }, 412 fakeSpace{ 413 name: "space-3", 414 subnets: []gomaasapi.Subnet{fakeSubnet{id: 101, vlan: fakeVLAN{vid: 66}, cidr: "192.168.12.0/24"}}, 415 id: 7, 416 }, 417 fakeSpace{ 418 name: "space-4", 419 subnets: []gomaasapi.Subnet{fakeSubnet{id: 102, vlan: fakeVLAN{vid: 66}, cidr: "192.168.13.0/24"}}, 420 id: 8, 421 }, 422 } 423 } 424 425 func (suite *maas2EnvironSuite) TestAcquireNodePassesPositiveAndNegativeSpaces(c *gc.C) { 426 expected := gomaasapi.AllocateMachineArgs{ 427 NotSpace: []string{"6", "8"}, 428 Interfaces: []gomaasapi.InterfaceSpec{ 429 {Label: "0", Space: "5"}, 430 {Label: "1", Space: "7"}, 431 }, 432 } 433 env, _ := suite.injectControllerWithSpacesAndCheck(c, getFourSpaces(), expected) 434 435 _, err := env.acquireNode2( 436 "", "", 437 constraints.Value{Spaces: stringslicep("space-1", "^space-2", "space-3", "^space-4")}, 438 nil, nil, 439 ) 440 c.Check(err, jc.ErrorIsNil) 441 } 442 443 func (suite *maas2EnvironSuite) TestAcquireNodeDisambiguatesNamedLabelsFromIndexedUpToALimit(c *gc.C) { 444 env, _ := suite.injectControllerWithSpacesAndCheck(c, getFourSpaces(), gomaasapi.AllocateMachineArgs{}) 445 var shortLimit uint = 0 446 suite.PatchValue(&numericLabelLimit, shortLimit) 447 448 _, err := env.acquireNode2( 449 "", "", 450 constraints.Value{Spaces: stringslicep("space-1", "^space-2", "space-3", "^space-4")}, 451 []interfaceBinding{{"0", "first-clash"}, {"1", "final-clash"}}, 452 nil, 453 ) 454 c.Assert(err, gc.ErrorMatches, `too many conflicting numeric labels, giving up.`) 455 } 456 457 func (suite *maas2EnvironSuite) TestAcquireNodeStorage(c *gc.C) { 458 var env *maasEnviron 459 var getStorage func() []gomaasapi.StorageSpec 460 suite.injectController(&fakeController{ 461 allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) { 462 c.Assert(args, jc.DeepEquals, gomaasapi.AllocateMachineArgs{ 463 AgentName: env.Config().UUID(), 464 Storage: getStorage(), 465 }) 466 }, 467 allocateMachine: &fakeMachine{ 468 systemID: "Bruce Sterling", 469 architecture: arch.HostArch(), 470 }, 471 }) 472 suite.setupFakeTools(c) 473 for i, test := range []struct { 474 volumes []volumeInfo 475 expected []gomaasapi.StorageSpec 476 }{{ 477 volumes: nil, 478 expected: []gomaasapi.StorageSpec{}, 479 }, { 480 volumes: []volumeInfo{{"volume-1", 1234, nil}}, 481 expected: []gomaasapi.StorageSpec{{"volume-1", 1234, nil}}, 482 }, { 483 volumes: []volumeInfo{{"", 1234, []string{"tag1", "tag2"}}}, 484 expected: []gomaasapi.StorageSpec{{"", 1234, []string{"tag1", "tag2"}}}, 485 }, { 486 volumes: []volumeInfo{{"volume-1", 1234, []string{"tag1", "tag2"}}}, 487 expected: []gomaasapi.StorageSpec{{"volume-1", 1234, []string{"tag1", "tag2"}}}, 488 }, { 489 volumes: []volumeInfo{ 490 {"volume-1", 1234, []string{"tag1", "tag2"}}, 491 {"volume-2", 4567, []string{"tag1", "tag3"}}, 492 }, 493 expected: []gomaasapi.StorageSpec{ 494 {"volume-1", 1234, []string{"tag1", "tag2"}}, 495 {"volume-2", 4567, []string{"tag1", "tag3"}}, 496 }, 497 }} { 498 c.Logf("test #%d: volumes=%v", i, test.volumes) 499 getStorage = func() []gomaasapi.StorageSpec { 500 return test.expected 501 } 502 env = suite.makeEnviron(c, nil) 503 _, err := env.acquireNode2("", "", constraints.Value{}, nil, test.volumes) 504 c.Check(err, jc.ErrorIsNil) 505 } 506 } 507 508 func (suite *maas2EnvironSuite) TestAcquireNodeInterfaces(c *gc.C) { 509 var env *maasEnviron 510 var getNegatives func() []string 511 var getPositives func() []gomaasapi.InterfaceSpec 512 suite.injectController(&fakeController{ 513 allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) { 514 c.Assert(args, gc.DeepEquals, gomaasapi.AllocateMachineArgs{ 515 AgentName: env.Config().UUID(), 516 Interfaces: getPositives(), 517 NotSpace: getNegatives(), 518 }) 519 }, 520 allocateMachine: &fakeMachine{ 521 systemID: "Bruce Sterling", 522 architecture: arch.HostArch(), 523 }, 524 spaces: getTwoSpaces(), 525 }) 526 suite.setupFakeTools(c) 527 // Add some constraints, including spaces to verify specified bindings 528 // always override any spaces constraints. 529 cons := constraints.Value{ 530 Spaces: stringslicep("foo", "^bar"), 531 } 532 // In the tests below "space:5" means foo, "space:6" means bar. 533 for i, test := range []struct { 534 interfaces []interfaceBinding 535 expectedPositives []gomaasapi.InterfaceSpec 536 expectedNegatives []string 537 expectedError string 538 }{{ // without specified bindings, spaces constraints are used instead. 539 interfaces: nil, 540 expectedPositives: []gomaasapi.InterfaceSpec{{"0", "2"}}, 541 expectedNegatives: []string{"3"}, 542 expectedError: "", 543 }, { 544 interfaces: []interfaceBinding{{"name-1", "space-1"}}, 545 expectedPositives: []gomaasapi.InterfaceSpec{{"name-1", "space-1"}, {"0", "2"}}, 546 expectedNegatives: []string{"3"}, 547 }, { 548 interfaces: []interfaceBinding{ 549 {"name-1", "7"}, 550 {"name-2", "8"}, 551 {"name-3", "9"}, 552 }, 553 expectedPositives: []gomaasapi.InterfaceSpec{{"name-1", "7"}, {"name-2", "8"}, {"name-3", "9"}, {"0", "2"}}, 554 expectedNegatives: []string{"3"}, 555 }, { 556 interfaces: []interfaceBinding{{"", "anything"}}, 557 expectedError: "interface bindings cannot have empty names", 558 }, { 559 interfaces: []interfaceBinding{{"shared-db", "3"}}, 560 expectedError: `negative space "bar" from constraints clashes with interface bindings`, 561 }, { 562 interfaces: []interfaceBinding{ 563 {"shared-db", "1"}, 564 {"db", "1"}, 565 }, 566 expectedPositives: []gomaasapi.InterfaceSpec{{"shared-db", "1"}, {"db", "1"}, {"0", "2"}}, 567 expectedNegatives: []string{"3"}, 568 }, { 569 interfaces: []interfaceBinding{{"", ""}}, 570 expectedError: "interface bindings cannot have empty names", 571 }, { 572 interfaces: []interfaceBinding{ 573 {"valid", "ok"}, 574 {"", "valid-but-ignored-space"}, 575 {"valid-name-empty-space", ""}, 576 {"", ""}, 577 }, 578 expectedError: "interface bindings cannot have empty names", 579 }, { 580 interfaces: []interfaceBinding{{"foo", ""}}, 581 expectedError: `invalid interface binding "foo": space provider ID is required`, 582 }, { 583 interfaces: []interfaceBinding{ 584 {"bar", ""}, 585 {"valid", "ok"}, 586 {"", "valid-but-ignored-space"}, 587 {"", ""}, 588 }, 589 expectedError: `invalid interface binding "bar": space provider ID is required`, 590 }, { 591 interfaces: []interfaceBinding{ 592 {"dup-name", "1"}, 593 {"dup-name", "2"}, 594 }, 595 expectedError: `duplicated interface binding "dup-name"`, 596 }, { 597 interfaces: []interfaceBinding{ 598 {"valid-1", "0"}, 599 {"dup-name", "1"}, 600 {"dup-name", "2"}, 601 {"valid-2", "3"}, 602 }, 603 expectedError: `duplicated interface binding "dup-name"`, 604 }} { 605 c.Logf("test #%d: interfaces=%v", i, test.interfaces) 606 env = suite.makeEnviron(c, nil) 607 getNegatives = func() []string { 608 return test.expectedNegatives 609 } 610 getPositives = func() []gomaasapi.InterfaceSpec { 611 return test.expectedPositives 612 } 613 _, err := env.acquireNode2("", "", cons, test.interfaces, nil) 614 if test.expectedError != "" { 615 c.Check(err, gc.ErrorMatches, test.expectedError) 616 c.Check(err, jc.Satisfies, errors.IsNotValid) 617 continue 618 } 619 c.Check(err, jc.ErrorIsNil) 620 } 621 } 622 623 func getTwoSpaces() []gomaasapi.Space { 624 return []gomaasapi.Space{ 625 fakeSpace{ 626 name: "foo", 627 subnets: []gomaasapi.Subnet{fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24"}}, 628 id: 2, 629 }, 630 fakeSpace{ 631 name: "bar", 632 subnets: []gomaasapi.Subnet{fakeSubnet{id: 100, vlan: fakeVLAN{vid: 66}, cidr: "192.168.11.0/24"}}, 633 id: 3, 634 }, 635 } 636 } 637 638 func (suite *maas2EnvironSuite) TestAcquireNodeConvertsSpaceNames(c *gc.C) { 639 expected := gomaasapi.AllocateMachineArgs{ 640 NotSpace: []string{"3"}, 641 Interfaces: []gomaasapi.InterfaceSpec{{Label: "0", Space: "2"}}, 642 } 643 env, _ := suite.injectControllerWithSpacesAndCheck(c, getTwoSpaces(), expected) 644 cons := constraints.Value{ 645 Spaces: stringslicep("foo", "^bar"), 646 } 647 _, err := env.acquireNode2("", "", cons, nil, nil) 648 c.Assert(err, jc.ErrorIsNil) 649 } 650 651 func (suite *maas2EnvironSuite) TestAcquireNodeTranslatesSpaceNames(c *gc.C) { 652 expected := gomaasapi.AllocateMachineArgs{ 653 NotSpace: []string{"3"}, 654 Interfaces: []gomaasapi.InterfaceSpec{{Label: "0", Space: "2"}}, 655 } 656 env, _ := suite.injectControllerWithSpacesAndCheck(c, getTwoSpaces(), expected) 657 cons := constraints.Value{ 658 Spaces: stringslicep("foo-1", "^bar-3"), 659 } 660 _, err := env.acquireNode2("", "", cons, nil, nil) 661 c.Assert(err, jc.ErrorIsNil) 662 } 663 664 func (suite *maas2EnvironSuite) TestAcquireNodeUnrecognisedSpace(c *gc.C) { 665 suite.injectController(&fakeController{}) 666 env := suite.makeEnviron(c, nil) 667 cons := constraints.Value{ 668 Spaces: stringslicep("baz"), 669 } 670 _, err := env.acquireNode2("", "", cons, nil, nil) 671 c.Assert(err, gc.ErrorMatches, `unrecognised space in constraint "baz"`) 672 } 673 674 func (suite *maas2EnvironSuite) TestWaitForNodeDeploymentError(c *gc.C) { 675 machine := newFakeMachine("Bruce Sterling", arch.HostArch(), "") 676 controller := newFakeController() 677 controller.allocateMachine = machine 678 controller.allocateMachineMatches = gomaasapi.ConstraintMatches{ 679 Storage: map[string][]gomaasapi.BlockDevice{}, 680 } 681 controller.machines = []gomaasapi.Machine{machine} 682 suite.injectController(controller) 683 suite.setupFakeTools(c) 684 env := suite.makeEnviron(c, nil) 685 err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 686 ControllerConfig: coretesting.FakeControllerConfig(), 687 AdminSecret: jujutesting.AdminSecret, 688 CAPrivateKey: coretesting.CAKey, 689 }) 690 c.Assert(err, gc.ErrorMatches, "bootstrap instance started but did not change to Deployed state.*") 691 } 692 693 func (suite *maas2EnvironSuite) TestWaitForNodeDeploymentSucceeds(c *gc.C) { 694 machine := newFakeMachine("Bruce Sterling", arch.HostArch(), "Deployed") 695 controller := newFakeController() 696 controller.allocateMachine = machine 697 controller.allocateMachineMatches = gomaasapi.ConstraintMatches{ 698 Storage: map[string][]gomaasapi.BlockDevice{}, 699 } 700 controller.machines = []gomaasapi.Machine{machine} 701 suite.injectController(controller) 702 suite.setupFakeTools(c) 703 env := suite.makeEnviron(c, nil) 704 err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 705 ControllerConfig: coretesting.FakeControllerConfig(), 706 AdminSecret: jujutesting.AdminSecret, 707 CAPrivateKey: coretesting.CAKey, 708 }) 709 c.Assert(err, jc.ErrorIsNil) 710 } 711 712 func (suite *maas2EnvironSuite) TestSubnetsNoFilters(c *gc.C) { 713 suite.injectController(&fakeController{ 714 spaces: getFourSpaces(), 715 }) 716 env := suite.makeEnviron(c, nil) 717 subnets, err := env.Subnets("", nil) 718 c.Assert(err, jc.ErrorIsNil) 719 expected := []network.SubnetInfo{ 720 {CIDR: "192.168.10.0/24", ProviderId: "99", VLANTag: 66, SpaceProviderId: "5"}, 721 {CIDR: "192.168.11.0/24", ProviderId: "100", VLANTag: 66, SpaceProviderId: "6"}, 722 {CIDR: "192.168.12.0/24", ProviderId: "101", VLANTag: 66, SpaceProviderId: "7"}, 723 {CIDR: "192.168.13.0/24", ProviderId: "102", VLANTag: 66, SpaceProviderId: "8"}, 724 } 725 c.Assert(subnets, jc.DeepEquals, expected) 726 } 727 728 func (suite *maas2EnvironSuite) TestSubnetsNoFiltersError(c *gc.C) { 729 suite.injectController(&fakeController{ 730 spacesError: errors.New("bang"), 731 }) 732 env := suite.makeEnviron(c, nil) 733 _, err := env.Subnets("", nil) 734 c.Assert(err, gc.ErrorMatches, "bang") 735 } 736 737 func (suite *maas2EnvironSuite) TestSubnetsSubnetIds(c *gc.C) { 738 suite.injectController(&fakeController{ 739 spaces: getFourSpaces(), 740 }) 741 env := suite.makeEnviron(c, nil) 742 subnets, err := env.Subnets("", []network.Id{"99", "100"}) 743 c.Assert(err, jc.ErrorIsNil) 744 expected := []network.SubnetInfo{ 745 {CIDR: "192.168.10.0/24", ProviderId: "99", VLANTag: 66, SpaceProviderId: "5"}, 746 {CIDR: "192.168.11.0/24", ProviderId: "100", VLANTag: 66, SpaceProviderId: "6"}, 747 } 748 c.Assert(subnets, jc.DeepEquals, expected) 749 } 750 751 func (suite *maas2EnvironSuite) TestSubnetsSubnetIdsMissing(c *gc.C) { 752 suite.injectController(&fakeController{ 753 spaces: getFourSpaces(), 754 }) 755 env := suite.makeEnviron(c, nil) 756 _, err := env.Subnets("", []network.Id{"99", "missing"}) 757 msg := "failed to find the following subnets: missing" 758 c.Assert(err, gc.ErrorMatches, msg) 759 } 760 761 func (suite *maas2EnvironSuite) TestSubnetsInstIdNotFound(c *gc.C) { 762 suite.injectController(&fakeController{}) 763 env := suite.makeEnviron(c, nil) 764 _, err := env.Subnets("foo", nil) 765 c.Assert(err, jc.Satisfies, errors.IsNotFound) 766 } 767 768 func (suite *maas2EnvironSuite) TestSubnetsInstId(c *gc.C) { 769 interfaces := []gomaasapi.Interface{ 770 &fakeInterface{ 771 links: []gomaasapi.Link{ 772 &fakeLink{subnet: fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24", space: "space-1"}}, 773 &fakeLink{subnet: fakeSubnet{id: 100, vlan: fakeVLAN{vid: 0}, cidr: "192.168.11.0/24", space: "space-2"}}, 774 }, 775 }, 776 &fakeInterface{ 777 links: []gomaasapi.Link{ 778 &fakeLink{subnet: fakeSubnet{id: 101, vlan: fakeVLAN{vid: 2}, cidr: "192.168.12.0/24", space: "space-3"}}, 779 }, 780 }, 781 } 782 machine := &fakeMachine{ 783 systemID: "William Gibson", 784 interfaceSet: interfaces, 785 } 786 machine2 := &fakeMachine{systemID: "Bruce Sterling"} 787 suite.injectController(&fakeController{ 788 machines: []gomaasapi.Machine{machine, machine2}, 789 spaces: getFourSpaces(), 790 }) 791 env := suite.makeEnviron(c, nil) 792 subnets, err := env.Subnets("William Gibson", nil) 793 c.Assert(err, jc.ErrorIsNil) 794 expected := []network.SubnetInfo{ 795 {CIDR: "192.168.10.0/24", ProviderId: "99", VLANTag: 66, SpaceProviderId: "5"}, 796 {CIDR: "192.168.11.0/24", ProviderId: "100", VLANTag: 0, SpaceProviderId: "6"}, 797 {CIDR: "192.168.12.0/24", ProviderId: "101", VLANTag: 2, SpaceProviderId: "7"}, 798 } 799 c.Assert(subnets, jc.DeepEquals, expected) 800 } 801 802 func (suite *maas2EnvironSuite) TestStartInstanceNetworkInterfaces(c *gc.C) { 803 vlan0 := fakeVLAN{ 804 id: 5001, 805 vid: 0, 806 mtu: 1500, 807 } 808 809 vlan50 := fakeVLAN{ 810 id: 5004, 811 vid: 50, 812 mtu: 1500, 813 } 814 815 subnetPXE := fakeSubnet{ 816 id: 3, 817 space: "default", 818 vlan: vlan0, 819 gateway: "10.20.19.2", 820 cidr: "10.20.19.0/24", 821 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 822 } 823 824 exampleInterfaces := []gomaasapi.Interface{ 825 &fakeInterface{ 826 id: 91, 827 name: "eth0", 828 type_: "physical", 829 enabled: true, 830 macAddress: "52:54:00:70:9b:fe", 831 vlan: vlan0, 832 links: []gomaasapi.Link{ 833 &fakeLink{ 834 id: 436, 835 subnet: &subnetPXE, 836 ipAddress: "10.20.19.103", 837 mode: "static", 838 }, 839 &fakeLink{ 840 id: 437, 841 subnet: &subnetPXE, 842 ipAddress: "10.20.19.104", 843 mode: "static", 844 }, 845 }, 846 parents: []string{}, 847 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 848 }, 849 &fakeInterface{ 850 id: 150, 851 name: "eth0.50", 852 type_: "vlan", 853 enabled: true, 854 macAddress: "52:54:00:70:9b:fe", 855 vlan: vlan50, 856 links: []gomaasapi.Link{ 857 &fakeLink{ 858 id: 517, 859 subnet: &fakeSubnet{ 860 id: 5, 861 space: "admin", 862 vlan: vlan50, 863 gateway: "10.50.19.2", 864 cidr: "10.50.19.0/24", 865 dnsServers: []string{}, 866 }, 867 ipAddress: "10.50.19.103", 868 mode: "static", 869 }, 870 }, 871 parents: []string{"eth0"}, 872 children: []string{}, 873 }, 874 } 875 var env *maasEnviron 876 machine := newFakeMachine("Bruce Sterling", arch.HostArch(), "") 877 machine.interfaceSet = exampleInterfaces 878 controller := &fakeController{ 879 allocateMachine: machine, 880 allocateMachineMatches: gomaasapi.ConstraintMatches{ 881 Storage: map[string][]gomaasapi.BlockDevice{}, 882 }, 883 } 884 suite.injectController(controller) 885 suite.setupFakeTools(c) 886 env = suite.makeEnviron(c, nil) 887 888 params := environs.StartInstanceParams{ControllerUUID: suite.controllerUUID} 889 result, err := jujutesting.StartInstanceWithParams(env, "1", params) 890 c.Assert(err, jc.ErrorIsNil) 891 expected := []network.InterfaceInfo{{ 892 DeviceIndex: 0, 893 MACAddress: "52:54:00:70:9b:fe", 894 CIDR: "10.20.19.0/24", 895 ProviderId: "91", 896 ProviderSubnetId: "3", 897 AvailabilityZones: nil, 898 VLANTag: 0, 899 ProviderVLANId: "5001", 900 ProviderAddressId: "436", 901 InterfaceName: "eth0", 902 InterfaceType: "ethernet", 903 Disabled: false, 904 NoAutoStart: false, 905 ConfigType: "static", 906 Address: network.NewAddressOnSpace("default", "10.20.19.103"), 907 DNSServers: network.NewAddressesOnSpace("default", "10.20.19.2", "10.20.19.3"), 908 DNSSearchDomains: nil, 909 MTU: 1500, 910 GatewayAddress: network.NewAddressOnSpace("default", "10.20.19.2"), 911 }, { 912 DeviceIndex: 0, 913 MACAddress: "52:54:00:70:9b:fe", 914 CIDR: "10.20.19.0/24", 915 ProviderId: "91", 916 ProviderSubnetId: "3", 917 AvailabilityZones: nil, 918 VLANTag: 0, 919 ProviderVLANId: "5001", 920 ProviderAddressId: "437", 921 InterfaceName: "eth0", 922 InterfaceType: "ethernet", 923 Disabled: false, 924 NoAutoStart: false, 925 ConfigType: "static", 926 Address: network.NewAddressOnSpace("default", "10.20.19.104"), 927 DNSServers: network.NewAddressesOnSpace("default", "10.20.19.2", "10.20.19.3"), 928 DNSSearchDomains: nil, 929 MTU: 1500, 930 GatewayAddress: network.NewAddressOnSpace("default", "10.20.19.2"), 931 }, { 932 DeviceIndex: 1, 933 MACAddress: "52:54:00:70:9b:fe", 934 CIDR: "10.50.19.0/24", 935 ProviderId: "150", 936 ProviderSubnetId: "5", 937 AvailabilityZones: nil, 938 VLANTag: 50, 939 ProviderVLANId: "5004", 940 ProviderAddressId: "517", 941 InterfaceName: "eth0.50", 942 ParentInterfaceName: "eth0", 943 InterfaceType: "802.1q", 944 Disabled: false, 945 NoAutoStart: false, 946 ConfigType: "static", 947 Address: network.NewAddressOnSpace("admin", "10.50.19.103"), 948 DNSServers: nil, 949 DNSSearchDomains: nil, 950 MTU: 1500, 951 GatewayAddress: network.NewAddressOnSpace("admin", "10.50.19.2"), 952 }, 953 } 954 c.Assert(result.NetworkInfo, jc.DeepEquals, expected) 955 } 956 957 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSingleNic(c *gc.C) { 958 vlan1 := fakeVLAN{ 959 id: 5001, 960 mtu: 1500, 961 } 962 vlan2 := fakeVLAN{ 963 id: 5002, 964 mtu: 1500, 965 } 966 subnet1 := fakeSubnet{ 967 id: 3, 968 space: "default", 969 vlan: vlan1, 970 gateway: "10.20.19.2", 971 cidr: "10.20.19.0/24", 972 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 973 } 974 subnet2 := fakeSubnet{ 975 id: 4, 976 space: "freckles", 977 vlan: vlan2, 978 gateway: "192.168.1.1", 979 cidr: "192.168.1.0/24", 980 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 981 } 982 983 interfaces := []gomaasapi.Interface{ 984 &fakeInterface{ 985 id: 91, 986 name: "eth0", 987 type_: "physical", 988 enabled: true, 989 macAddress: "52:54:00:70:9b:fe", 990 vlan: vlan1, 991 links: []gomaasapi.Link{ 992 &fakeLink{ 993 id: 436, 994 subnet: &subnet1, 995 ipAddress: "10.20.19.103", 996 mode: "static", 997 }, 998 }, 999 parents: []string{}, 1000 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 1001 }, 1002 } 1003 deviceInterfaces := []gomaasapi.Interface{ 1004 &fakeInterface{ 1005 id: 93, 1006 name: "eth1", 1007 type_: "physical", 1008 enabled: true, 1009 macAddress: "53:54:00:70:9b:ff", 1010 vlan: vlan2, 1011 links: []gomaasapi.Link{ 1012 &fakeLink{ 1013 id: 480, 1014 subnet: &subnet2, 1015 ipAddress: "192.168.1.127", 1016 mode: "static", 1017 }, 1018 }, 1019 parents: []string{}, 1020 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 1021 }, 1022 } 1023 var env *maasEnviron 1024 device := &fakeDevice{ 1025 interfaceSet: deviceInterfaces, 1026 systemID: "foo", 1027 } 1028 controller := &fakeController{ 1029 Stub: &testing.Stub{}, 1030 machines: []gomaasapi.Machine{&fakeMachine{ 1031 Stub: &testing.Stub{}, 1032 systemID: "1", 1033 architecture: arch.HostArch(), 1034 interfaceSet: interfaces, 1035 createDevice: device, 1036 }}, 1037 spaces: []gomaasapi.Space{ 1038 fakeSpace{ 1039 name: "freckles", 1040 id: 4567, 1041 subnets: []gomaasapi.Subnet{subnet1, subnet2}, 1042 }, 1043 }, 1044 devices: []gomaasapi.Device{device}, 1045 } 1046 suite.injectController(controller) 1047 suite.setupFakeTools(c) 1048 env = suite.makeEnviron(c, nil) 1049 1050 prepared := []network.InterfaceInfo{{ 1051 MACAddress: "52:54:00:70:9b:fe", 1052 CIDR: "10.20.19.0/24", 1053 InterfaceName: "eth0", 1054 }} 1055 ignored := names.NewMachineTag("1/lxd/0") 1056 result, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared) 1057 c.Assert(err, jc.ErrorIsNil) 1058 expected := []network.InterfaceInfo{{ 1059 DeviceIndex: 0, 1060 MACAddress: "53:54:00:70:9b:ff", 1061 CIDR: "192.168.1.0/24", 1062 ProviderId: "93", 1063 ProviderSubnetId: "4", 1064 VLANTag: 0, 1065 ProviderVLANId: "5002", 1066 ProviderAddressId: "480", 1067 InterfaceName: "eth1", 1068 InterfaceType: "ethernet", 1069 ConfigType: "static", 1070 Address: network.NewAddressOnSpace("freckles", "192.168.1.127"), 1071 DNSServers: network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"), 1072 MTU: 1500, 1073 GatewayAddress: network.NewAddressOnSpace("freckles", "192.168.1.1"), 1074 }} 1075 c.Assert(result, jc.DeepEquals, expected) 1076 } 1077 1078 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesDualNic(c *gc.C) { 1079 vlan1 := fakeVLAN{ 1080 id: 5001, 1081 mtu: 1500, 1082 } 1083 vlan2 := fakeVLAN{ 1084 id: 5002, 1085 mtu: 1500, 1086 } 1087 subnet1 := fakeSubnet{ 1088 id: 3, 1089 space: "freckles", 1090 vlan: vlan1, 1091 gateway: "10.20.19.2", 1092 cidr: "10.20.19.0/24", 1093 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 1094 } 1095 subnet2 := fakeSubnet{ 1096 id: 4, 1097 space: "freckles", 1098 vlan: vlan2, 1099 gateway: "192.168.1.1", 1100 cidr: "192.168.1.0/24", 1101 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 1102 } 1103 1104 interfaces := []gomaasapi.Interface{ 1105 &fakeInterface{ 1106 id: 91, 1107 name: "eth0", 1108 type_: "physical", 1109 enabled: true, 1110 macAddress: "52:54:00:70:9b:fe", 1111 vlan: vlan1, 1112 links: []gomaasapi.Link{ 1113 &fakeLink{ 1114 id: 436, 1115 subnet: &subnet1, 1116 ipAddress: "10.20.19.103", 1117 mode: "static", 1118 }, 1119 }, 1120 parents: []string{}, 1121 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 1122 }, 1123 &fakeInterface{ 1124 id: 92, 1125 name: "eth1", 1126 type_: "physical", 1127 enabled: true, 1128 macAddress: "52:54:00:70:9b:ff", 1129 vlan: vlan2, 1130 links: []gomaasapi.Link{ 1131 &fakeLink{ 1132 id: 437, 1133 subnet: &subnet2, 1134 ipAddress: "192.168.1.4", 1135 mode: "static", 1136 }, 1137 }, 1138 }, 1139 } 1140 deviceInterfaces := []gomaasapi.Interface{ 1141 &fakeInterface{ 1142 id: 93, 1143 name: "eth0", 1144 type_: "physical", 1145 enabled: true, 1146 macAddress: "53:54:00:70:9b:ff", 1147 vlan: vlan1, 1148 links: []gomaasapi.Link{ 1149 &fakeLink{ 1150 id: 480, 1151 subnet: &subnet1, 1152 ipAddress: "10.20.19.127", 1153 mode: "static", 1154 }, 1155 }, 1156 parents: []string{}, 1157 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 1158 Stub: &testing.Stub{}, 1159 }, 1160 } 1161 newInterface := &fakeInterface{ 1162 id: 94, 1163 name: "eth1", 1164 type_: "physical", 1165 enabled: true, 1166 macAddress: "52:54:00:70:9b:f4", 1167 vlan: vlan2, 1168 links: []gomaasapi.Link{ 1169 &fakeLink{ 1170 id: 481, 1171 subnet: &subnet2, 1172 ipAddress: "192.168.1.127", 1173 mode: "static", 1174 }, 1175 }, 1176 Stub: &testing.Stub{}, 1177 } 1178 device := &fakeDevice{ 1179 interfaceSet: deviceInterfaces, 1180 systemID: "foo", 1181 interface_: newInterface, 1182 Stub: &testing.Stub{}, 1183 } 1184 controller := &fakeController{ 1185 Stub: &testing.Stub{}, 1186 machines: []gomaasapi.Machine{&fakeMachine{ 1187 Stub: &testing.Stub{}, 1188 systemID: "1", 1189 architecture: arch.HostArch(), 1190 interfaceSet: interfaces, 1191 createDevice: device, 1192 }}, 1193 spaces: []gomaasapi.Space{ 1194 fakeSpace{ 1195 name: "freckles", 1196 id: 4567, 1197 subnets: []gomaasapi.Subnet{subnet1, subnet2}, 1198 }, 1199 }, 1200 devices: []gomaasapi.Device{device}, 1201 } 1202 suite.injectController(controller) 1203 env := suite.makeEnviron(c, nil) 1204 1205 prepared := []network.InterfaceInfo{{ 1206 MACAddress: "53:54:00:70:9b:ff", 1207 CIDR: "10.20.19.0/24", 1208 InterfaceName: "eth0", 1209 }, { 1210 MACAddress: "52:54:00:70:9b:f4", 1211 CIDR: "192.168.1.0/24", 1212 InterfaceName: "eth1", 1213 }} 1214 expected := []network.InterfaceInfo{{ 1215 DeviceIndex: 0, 1216 MACAddress: "53:54:00:70:9b:ff", 1217 CIDR: "10.20.19.0/24", 1218 ProviderId: "93", 1219 ProviderSubnetId: "3", 1220 ProviderVLANId: "5001", 1221 ProviderAddressId: "480", 1222 InterfaceName: "eth0", 1223 InterfaceType: "ethernet", 1224 ConfigType: "static", 1225 Address: network.NewAddressOnSpace("freckles", "10.20.19.127"), 1226 DNSServers: network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"), 1227 MTU: 1500, 1228 GatewayAddress: network.NewAddressOnSpace("freckles", "10.20.19.2"), 1229 }, { 1230 DeviceIndex: 0, 1231 MACAddress: "52:54:00:70:9b:f4", 1232 CIDR: "192.168.1.0/24", 1233 ProviderId: "94", 1234 ProviderSubnetId: "4", 1235 ProviderVLANId: "5002", 1236 ProviderAddressId: "481", 1237 InterfaceName: "eth1", 1238 InterfaceType: "ethernet", 1239 ConfigType: "static", 1240 Address: network.NewAddressOnSpace("freckles", "192.168.1.127"), 1241 DNSServers: network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"), 1242 MTU: 1500, 1243 GatewayAddress: network.NewAddressOnSpace("freckles", "192.168.1.1"), 1244 }} 1245 ignored := names.NewMachineTag("1/lxd/0") 1246 result, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared) 1247 c.Assert(err, jc.ErrorIsNil) 1248 c.Assert(result, jc.DeepEquals, expected) 1249 } 1250 1251 func (suite *maas2EnvironSuite) assertAllocateContainerAddressesFails(c *gc.C, controller *fakeController, prepared []network.InterfaceInfo, errorMatches string) { 1252 if prepared == nil { 1253 prepared = []network.InterfaceInfo{{}} 1254 } 1255 suite.injectController(controller) 1256 env := suite.makeEnviron(c, nil) 1257 ignored := names.NewMachineTag("1/lxd/0") 1258 _, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared) 1259 c.Assert(err, gc.ErrorMatches, errorMatches) 1260 } 1261 1262 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSpacesError(c *gc.C) { 1263 controller := &fakeController{spacesError: errors.New("boom")} 1264 suite.assertAllocateContainerAddressesFails(c, controller, nil, "boom") 1265 } 1266 1267 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesPrimaryInterfaceMissing(c *gc.C) { 1268 controller := &fakeController{} 1269 suite.assertAllocateContainerAddressesFails(c, controller, nil, "cannot find primary interface for container") 1270 } 1271 1272 func makeFakeSubnet(id int) fakeSubnet { 1273 return fakeSubnet{ 1274 id: id, 1275 space: "freckles", 1276 gateway: fmt.Sprintf("10.20.%d.2", 16+id), 1277 cidr: fmt.Sprintf("10.20.%d.0/24", 16+id), 1278 } 1279 } 1280 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesMachinesError(c *gc.C) { 1281 var env *maasEnviron 1282 subnet := makeFakeSubnet(3) 1283 checkMachinesArgs := func(args gomaasapi.MachinesArgs) { 1284 expected := gomaasapi.MachinesArgs{ 1285 AgentName: env.Config().UUID(), 1286 SystemIDs: []string{"1"}, 1287 } 1288 c.Assert(args, jc.DeepEquals, expected) 1289 } 1290 controller := &fakeController{ 1291 machinesError: errors.New("boom"), 1292 machinesArgsCheck: checkMachinesArgs, 1293 spaces: []gomaasapi.Space{ 1294 fakeSpace{ 1295 name: "freckles", 1296 id: 4567, 1297 subnets: []gomaasapi.Subnet{subnet}, 1298 }, 1299 }, 1300 } 1301 suite.injectController(controller) 1302 env = suite.makeEnviron(c, nil) 1303 prepared := []network.InterfaceInfo{ 1304 {InterfaceName: "eth0", CIDR: "10.20.19.0/24"}, 1305 } 1306 ignored := names.NewMachineTag("1/lxd/0") 1307 _, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared) 1308 c.Assert(err, gc.ErrorMatches, "boom") 1309 } 1310 1311 func getArgs(c *gc.C, calls []testing.StubCall) interface{} { 1312 c.Assert(calls, gc.HasLen, 1) 1313 args := calls[0].Args 1314 c.Assert(args, gc.HasLen, 1) 1315 return args[0] 1316 } 1317 1318 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesCreateDevicerror(c *gc.C) { 1319 subnet := makeFakeSubnet(3) 1320 var env *maasEnviron 1321 machine := &fakeMachine{ 1322 Stub: &testing.Stub{}, 1323 systemID: "1", 1324 } 1325 machine.SetErrors(errors.New("boom")) 1326 controller := &fakeController{ 1327 machines: []gomaasapi.Machine{machine}, 1328 spaces: []gomaasapi.Space{ 1329 fakeSpace{ 1330 name: "freckles", 1331 id: 4567, 1332 subnets: []gomaasapi.Subnet{subnet}, 1333 }, 1334 }, 1335 } 1336 suite.injectController(controller) 1337 env = suite.makeEnviron(c, nil) 1338 prepared := []network.InterfaceInfo{ 1339 {InterfaceName: "eth0", CIDR: "10.20.19.0/24", MACAddress: "DEADBEEF"}, 1340 } 1341 ignored := names.NewMachineTag("1/lxd/0") 1342 _, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared) 1343 c.Assert(err, gc.ErrorMatches, "boom") 1344 args := getArgs(c, machine.Calls()) 1345 maasArgs, ok := args.(gomaasapi.CreateMachineDeviceArgs) 1346 c.Assert(ok, jc.IsTrue) 1347 expected := gomaasapi.CreateMachineDeviceArgs{ 1348 Hostname: "juju-06f00d-1-lxd-0", 1349 Subnet: subnet, 1350 MACAddress: "DEADBEEF", 1351 InterfaceName: "eth0", 1352 } 1353 c.Assert(maasArgs, jc.DeepEquals, expected) 1354 } 1355 1356 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSubnetMissing(c *gc.C) { 1357 subnet := makeFakeSubnet(3) 1358 var env *maasEnviron 1359 device := &fakeDevice{ 1360 Stub: &testing.Stub{}, 1361 interfaceSet: []gomaasapi.Interface{ 1362 &fakeInterface{ 1363 id: 93, 1364 name: "eth0", 1365 type_: "physical", 1366 enabled: true, 1367 macAddress: "53:54:00:70:9b:ff", 1368 vlan: &fakeVLAN{vid: 0}, 1369 links: []gomaasapi.Link{ 1370 &fakeLink{ 1371 id: 480, 1372 mode: "link_up", 1373 }, 1374 }, 1375 parents: []string{}, 1376 children: []string{}, 1377 Stub: &testing.Stub{}, 1378 }, 1379 }, 1380 interface_: &fakeInterface{ 1381 id: 94, 1382 name: "eth1", 1383 type_: "physical", 1384 enabled: true, 1385 macAddress: "53:54:00:70:9b:f1", 1386 vlan: &fakeVLAN{vid: 0}, 1387 links: []gomaasapi.Link{ 1388 &fakeLink{ 1389 id: 481, 1390 mode: "link_up", 1391 }, 1392 }, 1393 parents: []string{}, 1394 children: []string{}, 1395 Stub: &testing.Stub{}, 1396 }, 1397 systemID: "foo", 1398 } 1399 machine := &fakeMachine{ 1400 Stub: &testing.Stub{}, 1401 systemID: "1", 1402 createDevice: device, 1403 } 1404 controller := &fakeController{ 1405 Stub: &testing.Stub{}, 1406 machines: []gomaasapi.Machine{machine}, 1407 spaces: []gomaasapi.Space{ 1408 fakeSpace{ 1409 name: "freckles", 1410 id: 4567, 1411 subnets: []gomaasapi.Subnet{subnet}, 1412 }, 1413 }, 1414 devices: []gomaasapi.Device{device}, 1415 } 1416 suite.injectController(controller) 1417 env = suite.makeEnviron(c, nil) 1418 prepared := []network.InterfaceInfo{ 1419 {InterfaceName: "eth0", CIDR: "", MACAddress: "DEADBEEF"}, 1420 {InterfaceName: "eth1", CIDR: "", MACAddress: "DEADBEEE"}, 1421 } 1422 ignored := names.NewMachineTag("1/lxd/0") 1423 allocated, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared) 1424 c.Assert(err, jc.ErrorIsNil) 1425 c.Assert(allocated, jc.DeepEquals, []network.InterfaceInfo{{ 1426 MACAddress: "53:54:00:70:9b:ff", 1427 ProviderId: "93", 1428 ProviderVLANId: "0", 1429 VLANTag: 0, 1430 InterfaceName: "eth0", 1431 InterfaceType: "ethernet", 1432 Disabled: false, 1433 NoAutoStart: false, 1434 ConfigType: "manual", 1435 MTU: 1500, 1436 }, { 1437 MACAddress: "53:54:00:70:9b:f1", 1438 ProviderId: "94", 1439 ProviderVLANId: "0", 1440 VLANTag: 0, 1441 InterfaceName: "eth1", 1442 InterfaceType: "ethernet", 1443 Disabled: false, 1444 NoAutoStart: false, 1445 ConfigType: "manual", 1446 MTU: 1500, 1447 }}) 1448 } 1449 1450 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesCreateInterfaceError(c *gc.C) { 1451 subnet := makeFakeSubnet(3) 1452 subnet2 := makeFakeSubnet(4) 1453 subnet2.vlan = fakeVLAN{vid: 66} 1454 var env *maasEnviron 1455 device := &fakeDevice{ 1456 Stub: &testing.Stub{}, 1457 interfaceSet: []gomaasapi.Interface{&fakeInterface{}}, 1458 systemID: "foo", 1459 } 1460 device.SetErrors(errors.New("boom")) 1461 machine := &fakeMachine{ 1462 Stub: &testing.Stub{}, 1463 systemID: "1", 1464 createDevice: device, 1465 } 1466 controller := &fakeController{ 1467 machines: []gomaasapi.Machine{machine}, 1468 spaces: []gomaasapi.Space{ 1469 fakeSpace{ 1470 name: "freckles", 1471 id: 4567, 1472 subnets: []gomaasapi.Subnet{subnet, subnet2}, 1473 }, 1474 }, 1475 } 1476 suite.injectController(controller) 1477 env = suite.makeEnviron(c, nil) 1478 prepared := []network.InterfaceInfo{ 1479 {InterfaceName: "eth0", CIDR: "10.20.19.0/24", MACAddress: "DEADBEEF"}, 1480 {InterfaceName: "eth1", CIDR: "10.20.20.0/24", MACAddress: "DEADBEEE"}, 1481 } 1482 ignored := names.NewMachineTag("1/lxd/0") 1483 _, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared) 1484 c.Assert(err, gc.ErrorMatches, "creating device interface: boom") 1485 args := getArgs(c, device.Calls()) 1486 maasArgs, ok := args.(gomaasapi.CreateInterfaceArgs) 1487 c.Assert(ok, jc.IsTrue) 1488 expected := gomaasapi.CreateInterfaceArgs{ 1489 MACAddress: "DEADBEEE", 1490 Name: "eth1", 1491 VLAN: subnet2.VLAN(), 1492 } 1493 c.Assert(maasArgs, jc.DeepEquals, expected) 1494 } 1495 1496 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesLinkSubnetError(c *gc.C) { 1497 subnet := makeFakeSubnet(3) 1498 subnet2 := makeFakeSubnet(4) 1499 subnet2.vlan = fakeVLAN{vid: 66} 1500 var env *maasEnviron 1501 interface_ := &fakeInterface{Stub: &testing.Stub{}} 1502 interface_.SetErrors(errors.New("boom")) 1503 device := &fakeDevice{ 1504 Stub: &testing.Stub{}, 1505 interfaceSet: []gomaasapi.Interface{&fakeInterface{}}, 1506 interface_: interface_, 1507 systemID: "foo", 1508 } 1509 machine := &fakeMachine{ 1510 Stub: &testing.Stub{}, 1511 systemID: "1", 1512 createDevice: device, 1513 } 1514 controller := &fakeController{ 1515 Stub: &testing.Stub{}, 1516 machines: []gomaasapi.Machine{machine}, 1517 spaces: []gomaasapi.Space{ 1518 fakeSpace{ 1519 name: "freckles", 1520 id: 4567, 1521 subnets: []gomaasapi.Subnet{subnet, subnet2}, 1522 }, 1523 }, 1524 devices: []gomaasapi.Device{device}, 1525 } 1526 suite.injectController(controller) 1527 env = suite.makeEnviron(c, nil) 1528 prepared := []network.InterfaceInfo{ 1529 {InterfaceName: "eth0", CIDR: "10.20.19.0/24", MACAddress: "DEADBEEF"}, 1530 {InterfaceName: "eth1", CIDR: "10.20.20.0/24", MACAddress: "DEADBEEE"}, 1531 } 1532 ignored := names.NewMachineTag("1/lxd/0") 1533 allocated, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared) 1534 c.Assert(err, jc.ErrorIsNil) 1535 c.Assert(allocated, jc.DeepEquals, []network.InterfaceInfo{{ 1536 CIDR: "", 1537 ProviderId: "0", 1538 ProviderSubnetId: "", 1539 ProviderVLANId: "0", 1540 VLANTag: 0, 1541 InterfaceName: "", 1542 InterfaceType: "ethernet", 1543 ConfigType: "", 1544 MTU: 1500, 1545 Disabled: true, 1546 NoAutoStart: true, 1547 }, { 1548 CIDR: "", 1549 ProviderId: "0", 1550 ProviderSubnetId: "", 1551 ProviderVLANId: "0", 1552 VLANTag: 0, 1553 InterfaceName: "", 1554 InterfaceType: "ethernet", 1555 ConfigType: "", 1556 MTU: 1500, 1557 Disabled: true, 1558 NoAutoStart: true, 1559 }}) 1560 1561 args := getArgs(c, interface_.Calls()) 1562 maasArgs, ok := args.(gomaasapi.LinkSubnetArgs) 1563 c.Assert(ok, jc.IsTrue) 1564 expected := gomaasapi.LinkSubnetArgs{ 1565 Mode: gomaasapi.LinkModeStatic, 1566 Subnet: subnet2, 1567 } 1568 c.Assert(maasArgs, jc.DeepEquals, expected) 1569 } 1570 func (suite *maas2EnvironSuite) TestStorageReturnsStorage(c *gc.C) { 1571 controller := newFakeController() 1572 env := suite.makeEnviron(c, controller) 1573 stor := env.Storage() 1574 c.Check(stor, gc.NotNil) 1575 1576 // The Storage object is really a maas2Storage. 1577 specificStorage := stor.(*maas2Storage) 1578 1579 // Its environment pointer refers back to its environment. 1580 c.Check(specificStorage.environ, gc.Equals, env) 1581 c.Check(specificStorage.maasController, gc.Equals, controller) 1582 } 1583 1584 func (suite *maas2EnvironSuite) TestStartInstanceEndToEnd(c *gc.C) { 1585 suite.setupFakeTools(c) 1586 machine := newFakeMachine("gus", arch.HostArch(), "Deployed") 1587 file := &fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"} 1588 controller := newFakeControllerWithFiles(file) 1589 controller.machines = []gomaasapi.Machine{machine} 1590 controller.allocateMachine = machine 1591 controller.allocateMachineMatches = gomaasapi.ConstraintMatches{ 1592 Storage: make(map[string][]gomaasapi.BlockDevice), 1593 } 1594 1595 env := suite.makeEnviron(c, controller) 1596 err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 1597 ControllerConfig: coretesting.FakeControllerConfig(), 1598 AdminSecret: jujutesting.AdminSecret, 1599 CAPrivateKey: coretesting.CAKey, 1600 }) 1601 c.Assert(err, jc.ErrorIsNil) 1602 1603 machine.Stub.CheckCallNames(c, "Start", "SetOwnerData") 1604 ownerData, ok := machine.Stub.Calls()[1].Args[0].(map[string]string) 1605 c.Assert(ok, jc.IsTrue) 1606 c.Assert(ownerData, gc.DeepEquals, map[string]string{ 1607 "claude": "rains", 1608 tags.JujuController: suite.controllerUUID, 1609 tags.JujuIsController: "true", 1610 tags.JujuModel: env.Config().UUID(), 1611 }) 1612 1613 // Test the instance id is correctly recorded for the bootstrap node. 1614 // Check that ControllerInstances returns the id of the bootstrap machine. 1615 instanceIds, err := env.ControllerInstances(suite.controllerUUID) 1616 c.Assert(err, jc.ErrorIsNil) 1617 c.Assert(instanceIds, gc.HasLen, 1) 1618 insts, err := env.AllInstances() 1619 c.Assert(err, jc.ErrorIsNil) 1620 c.Assert(insts, gc.HasLen, 1) 1621 c.Check(insts[0].Id(), gc.Equals, instanceIds[0]) 1622 1623 node1 := newFakeMachine("victor", arch.HostArch(), "Deployed") 1624 node1.hostname = "host1" 1625 node1.cpuCount = 1 1626 node1.memory = 1024 1627 node1.zoneName = "test_zone" 1628 controller.allocateMachine = node1 1629 1630 instance, hc := jujutesting.AssertStartInstance(c, env, suite.controllerUUID, "1") 1631 c.Check(instance, gc.NotNil) 1632 c.Assert(hc, gc.NotNil) 1633 c.Check(hc.String(), gc.Equals, fmt.Sprintf("arch=%s cores=1 mem=1024M availability-zone=test_zone", arch.HostArch())) 1634 1635 node1.Stub.CheckCallNames(c, "Start", "SetOwnerData") 1636 startArgs, ok := node1.Stub.Calls()[0].Args[0].(gomaasapi.StartArgs) 1637 c.Assert(ok, jc.IsTrue) 1638 1639 decodedUserData, err := decodeUserData(startArgs.UserData) 1640 c.Assert(err, jc.ErrorIsNil) 1641 info := machineInfo{"host1"} 1642 cloudcfg, err := cloudinit.New("precise") 1643 c.Assert(err, jc.ErrorIsNil) 1644 cloudinitRunCmd, err := info.cloudinitRunCmd(cloudcfg) 1645 c.Assert(err, jc.ErrorIsNil) 1646 data, err := goyaml.Marshal(cloudinitRunCmd) 1647 c.Assert(err, jc.ErrorIsNil) 1648 c.Check(string(decodedUserData), jc.Contains, string(data)) 1649 1650 // Trash the tools and try to start another instance. 1651 suite.PatchValue(&envtools.DefaultBaseURL, "") 1652 instance, _, _, err = jujutesting.StartInstance(env, suite.controllerUUID, "2") 1653 c.Check(instance, gc.IsNil) 1654 c.Check(err, jc.Satisfies, errors.IsNotFound) 1655 } 1656 1657 func (suite *maas2EnvironSuite) TestControllerInstances(c *gc.C) { 1658 controller := newFakeControllerWithErrors(gomaasapi.NewNoMatchError("state")) 1659 env := suite.makeEnviron(c, controller) 1660 _, err := env.ControllerInstances(suite.controllerUUID) 1661 c.Assert(err, gc.Equals, environs.ErrNotBootstrapped) 1662 1663 controller.machinesArgsCheck = func(args gomaasapi.MachinesArgs) { 1664 c.Assert(args, gc.DeepEquals, gomaasapi.MachinesArgs{ 1665 OwnerData: map[string]string{ 1666 tags.JujuIsController: "true", 1667 tags.JujuController: suite.controllerUUID, 1668 }, 1669 }) 1670 } 1671 1672 tests := [][]instance.Id{{"inst-0"}, {"inst-0", "inst-1"}} 1673 for _, expected := range tests { 1674 controller.machines = make([]gomaasapi.Machine, len(expected)) 1675 for i := range expected { 1676 controller.machines[i] = newFakeMachine(string(expected[i]), "", "") 1677 } 1678 controllerInstances, err := env.ControllerInstances(suite.controllerUUID) 1679 c.Assert(err, jc.ErrorIsNil) 1680 c.Assert(controllerInstances, jc.SameContents, expected) 1681 } 1682 } 1683 1684 func (suite *maas2EnvironSuite) TestDestroy(c *gc.C) { 1685 file1 := &fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"} 1686 file2 := &fakeFile{name: coretesting.ModelTag.Id() + "-horace"} 1687 controller := newFakeControllerWithFiles(file1, file2) 1688 controller.machines = []gomaasapi.Machine{&fakeMachine{systemID: "pete"}} 1689 env := suite.makeEnviron(c, controller) 1690 err := env.Destroy() 1691 c.Check(err, jc.ErrorIsNil) 1692 1693 controller.Stub.CheckCallNames(c, "ReleaseMachines", "GetFile", "Files", "GetFile", "GetFile") 1694 // Instances have been stopped. 1695 controller.Stub.CheckCall(c, 0, "ReleaseMachines", gomaasapi.ReleaseMachinesArgs{ 1696 SystemIDs: []string{"pete"}, 1697 Comment: "Released by Juju MAAS provider", 1698 }) 1699 1700 // Files have been cleaned up. 1701 c.Check(file1.deleted, jc.IsTrue) 1702 c.Check(file2.deleted, jc.IsTrue) 1703 } 1704 1705 func (suite *maas2EnvironSuite) TestBootstrapFailsIfNoTools(c *gc.C) { 1706 env := suite.makeEnviron(c, newFakeController()) 1707 vers := version.MustParse("1.2.3") 1708 err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 1709 ControllerConfig: coretesting.FakeControllerConfig(), 1710 AdminSecret: jujutesting.AdminSecret, 1711 CAPrivateKey: coretesting.CAKey, 1712 // Disable auto-uploading by setting the agent version 1713 // to something that's not the current version. 1714 AgentVersion: &vers, 1715 }) 1716 c.Check(err, gc.ErrorMatches, "Juju cannot bootstrap because no agent binaries are available for your model(.|\n)*") 1717 } 1718 1719 func (suite *maas2EnvironSuite) TestBootstrapFailsIfNoNodes(c *gc.C) { 1720 suite.setupFakeTools(c) 1721 controller := newFakeController() 1722 controller.allocateMachineError = gomaasapi.NewNoMatchError("oops") 1723 env := suite.makeEnviron(c, controller) 1724 err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 1725 ControllerConfig: coretesting.FakeControllerConfig(), 1726 AdminSecret: jujutesting.AdminSecret, 1727 CAPrivateKey: coretesting.CAKey, 1728 }) 1729 // Since there are no nodes, the attempt to allocate one returns a 1730 // 409: Conflict. 1731 c.Check(err, gc.ErrorMatches, ".*cannot run instances.*") 1732 } 1733 1734 func (suite *maas2EnvironSuite) TestGetToolsMetadataSources(c *gc.C) { 1735 // Add a dummy file to storage so we can use that to check the 1736 // obtained source later. 1737 env := suite.makeEnviron(c, newFakeControllerWithFiles( 1738 &fakeFile{name: coretesting.ModelTag.Id() + "-tools/filename", contents: makeRandomBytes(10)}, 1739 )) 1740 sources, err := envtools.GetMetadataSources(env) 1741 c.Assert(err, jc.ErrorIsNil) 1742 c.Assert(sources, gc.HasLen, 0) 1743 } 1744 1745 func (suite *maas2EnvironSuite) TestConstraintsValidator(c *gc.C) { 1746 controller := newFakeController() 1747 controller.bootResources = []gomaasapi.BootResource{&fakeBootResource{name: "trusty", architecture: "amd64"}} 1748 env := suite.makeEnviron(c, controller) 1749 validator, err := env.ConstraintsValidator() 1750 c.Assert(err, jc.ErrorIsNil) 1751 cons := constraints.MustParse("arch=amd64 cpu-power=10 instance-type=foo virt-type=kvm") 1752 unsupported, err := validator.Validate(cons) 1753 c.Assert(err, jc.ErrorIsNil) 1754 c.Assert(unsupported, jc.SameContents, []string{"cpu-power", "instance-type", "virt-type"}) 1755 } 1756 1757 func (suite *maas2EnvironSuite) TestConstraintsValidatorVocab(c *gc.C) { 1758 controller := newFakeController() 1759 controller.bootResources = []gomaasapi.BootResource{ 1760 &fakeBootResource{name: "trusty", architecture: "amd64"}, 1761 &fakeBootResource{name: "precise", architecture: "armhf"}, 1762 } 1763 env := suite.makeEnviron(c, controller) 1764 validator, err := env.ConstraintsValidator() 1765 c.Assert(err, jc.ErrorIsNil) 1766 cons := constraints.MustParse("arch=ppc64el") 1767 _, err = validator.Validate(cons) 1768 c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=ppc64el\nvalid values are: \\[amd64 armhf\\]") 1769 } 1770 1771 func (suite *maas2EnvironSuite) TestReleaseContainerAddresses(c *gc.C) { 1772 dev1 := newFakeDevice("a", "eleven") 1773 dev2 := newFakeDevice("b", "will") 1774 controller := newFakeController() 1775 controller.devices = []gomaasapi.Device{dev1, dev2} 1776 1777 env := suite.makeEnviron(c, controller) 1778 err := env.ReleaseContainerAddresses([]network.ProviderInterfaceInfo{ 1779 {MACAddress: "will"}, 1780 {MACAddress: "dustin"}, 1781 {MACAddress: "eleven"}, 1782 }) 1783 c.Assert(err, jc.ErrorIsNil) 1784 1785 args, ok := getArgs(c, controller.Calls()).(gomaasapi.DevicesArgs) 1786 c.Assert(ok, jc.IsTrue) 1787 expected := gomaasapi.DevicesArgs{MACAddresses: []string{"will", "dustin", "eleven"}} 1788 c.Assert(args, gc.DeepEquals, expected) 1789 1790 dev1.CheckCallNames(c, "Delete") 1791 dev2.CheckCallNames(c, "Delete") 1792 } 1793 1794 func (suite *maas2EnvironSuite) TestReleaseContainerAddresses_HandlesDupes(c *gc.C) { 1795 dev1 := newFakeDevice("a", "eleven") 1796 controller := newFakeController() 1797 controller.devices = []gomaasapi.Device{dev1, dev1} 1798 1799 env := suite.makeEnviron(c, controller) 1800 err := env.ReleaseContainerAddresses([]network.ProviderInterfaceInfo{ 1801 {MACAddress: "will"}, 1802 {MACAddress: "eleven"}, 1803 }) 1804 c.Assert(err, jc.ErrorIsNil) 1805 1806 args, ok := getArgs(c, controller.Calls()).(gomaasapi.DevicesArgs) 1807 c.Assert(ok, jc.IsTrue) 1808 expected := gomaasapi.DevicesArgs{MACAddresses: []string{"will", "eleven"}} 1809 c.Assert(args, gc.DeepEquals, expected) 1810 1811 dev1.CheckCallNames(c, "Delete") 1812 } 1813 1814 func (suite *maas2EnvironSuite) TestReleaseContainerAddressesErrorGettingDevices(c *gc.C) { 1815 controller := newFakeControllerWithErrors(errors.New("Everything done broke")) 1816 env := suite.makeEnviron(c, controller) 1817 err := env.ReleaseContainerAddresses([]network.ProviderInterfaceInfo{{MACAddress: "anything"}}) 1818 c.Assert(err, gc.ErrorMatches, "Everything done broke") 1819 } 1820 1821 func (suite *maas2EnvironSuite) TestReleaseContainerAddressesErrorDeletingDevice(c *gc.C) { 1822 dev1 := newFakeDevice("a", "eleven") 1823 dev1.systemID = "hopper" 1824 dev1.SetErrors(errors.New("don't delete me")) 1825 controller := newFakeController() 1826 controller.devices = []gomaasapi.Device{dev1} 1827 1828 env := suite.makeEnviron(c, controller) 1829 err := env.ReleaseContainerAddresses([]network.ProviderInterfaceInfo{ 1830 {MACAddress: "eleven"}, 1831 }) 1832 c.Assert(err, gc.ErrorMatches, "deleting device hopper: don't delete me") 1833 1834 _, ok := getArgs(c, controller.Calls()).(gomaasapi.DevicesArgs) 1835 c.Assert(ok, jc.IsTrue) 1836 1837 dev1.CheckCallNames(c, "Delete") 1838 } 1839 1840 func newFakeDevice(systemID, macAddress string) *fakeDevice { 1841 return &fakeDevice{ 1842 Stub: &testing.Stub{}, 1843 systemID: systemID, 1844 interface_: &fakeInterface{ 1845 Stub: &testing.Stub{}, 1846 macAddress: macAddress, 1847 }, 1848 } 1849 }