github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/collections/set" 11 "github.com/juju/errors" 12 "github.com/juju/gomaasapi" 13 "github.com/juju/testing" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils/arch" 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/core/constraints" 24 "github.com/juju/juju/core/instance" 25 "github.com/juju/juju/environs" 26 "github.com/juju/juju/environs/bootstrap" 27 "github.com/juju/juju/environs/config" 28 "github.com/juju/juju/environs/tags" 29 envjujutesting "github.com/juju/juju/environs/testing" 30 envtools "github.com/juju/juju/environs/tools" 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 testServer.AddGetResponse("/api/2.0/domains", http.StatusOK, maas2DomainsResponse) 47 // Weirdly, rather than returning a 404 when the version is 48 // unknown, MAAS2 returns some HTML (the login page). 49 testServer.AddGetResponse("/api/1.0/version/", http.StatusOK, "<html></html>") 50 testServer.Start() 51 suite.AddCleanup(func(*gc.C) { testServer.Close() }) 52 cred := cloud.NewCredential(cloud.OAuth1AuthType, map[string]string{ 53 "maas-oauth": "a:b:c", 54 }) 55 cloud := environs.CloudSpec{ 56 Type: "maas", 57 Name: "maas", 58 Endpoint: testServer.Server.URL, 59 Credential: &cred, 60 } 61 attrs := coretesting.FakeConfig().Merge(maasEnvAttrs) 62 cfg, err := config.New(config.NoDefaults, attrs) 63 c.Assert(err, jc.ErrorIsNil) 64 return NewEnviron(cloud, cfg, nil) 65 } 66 67 func (suite *maas2EnvironSuite) TestNewEnvironWithController(c *gc.C) { 68 env, err := suite.getEnvWithServer(c) 69 c.Assert(err, jc.ErrorIsNil) 70 c.Assert(env, gc.NotNil) 71 } 72 73 func (suite *maas2EnvironSuite) injectControllerWithSpacesAndCheck(c *gc.C, spaces []gomaasapi.Space, expected gomaasapi.AllocateMachineArgs) (*maasEnviron, *fakeController) { 74 machine := newFakeMachine("Bruce Sterling", arch.HostArch(), "") 75 return suite.injectControllerWithMachine(c, machine, spaces, expected) 76 } 77 78 func (suite *maas2EnvironSuite) injectControllerWithMachine(c *gc.C, machine *fakeMachine, spaces []gomaasapi.Space, expected gomaasapi.AllocateMachineArgs) (*maasEnviron, *fakeController) { 79 var env *maasEnviron 80 check := func(args gomaasapi.AllocateMachineArgs) { 81 expected.AgentName = env.Config().UUID() 82 c.Assert(args, gc.DeepEquals, expected) 83 } 84 85 controller := &fakeController{ 86 allocateMachineArgsCheck: check, 87 allocateMachine: machine, 88 allocateMachineMatches: gomaasapi.ConstraintMatches{ 89 Storage: map[string][]gomaasapi.StorageDevice{}, 90 }, 91 spaces: spaces, 92 } 93 suite.injectController(controller) 94 suite.setupFakeTools(c) 95 env = suite.makeEnviron(c, nil) 96 return env, controller 97 } 98 99 func (suite *maas2EnvironSuite) makeEnvironWithMachines(c *gc.C, expectedSystemIDs []string, returnSystemIDs []string) *maasEnviron { 100 var env *maasEnviron 101 checkArgs := func(args gomaasapi.MachinesArgs) { 102 c.Check(args.SystemIDs, gc.DeepEquals, expectedSystemIDs) 103 c.Check(args.AgentName, gc.Equals, env.Config().UUID()) 104 } 105 machines := make([]gomaasapi.Machine, len(returnSystemIDs)) 106 for index, id := range returnSystemIDs { 107 machines[index] = &fakeMachine{systemID: id} 108 } 109 controller := &fakeController{ 110 machines: machines, 111 machinesArgsCheck: checkArgs, 112 } 113 env = suite.makeEnviron(c, controller) 114 return env 115 } 116 117 func (suite *maas2EnvironSuite) TestAllInstances(c *gc.C) { 118 env := suite.makeEnvironWithMachines( 119 c, []string{}, []string{"tuco", "tio", "gus"}, 120 ) 121 result, err := env.AllInstances(suite.callCtx) 122 c.Assert(err, jc.ErrorIsNil) 123 expectedMachines := set.NewStrings("tuco", "tio", "gus") 124 actualMachines := set.NewStrings() 125 for _, instance := range result { 126 actualMachines.Add(string(instance.Id())) 127 } 128 c.Assert(actualMachines, gc.DeepEquals, expectedMachines) 129 } 130 131 func (suite *maas2EnvironSuite) TestAllInstancesError(c *gc.C) { 132 controller := &fakeController{machinesError: errors.New("Something terrible!")} 133 env := suite.makeEnviron(c, controller) 134 _, err := env.AllInstances(suite.callCtx) 135 c.Assert(err, gc.ErrorMatches, "Something terrible!") 136 } 137 138 func (suite *maas2EnvironSuite) TestInstances(c *gc.C) { 139 env := suite.makeEnvironWithMachines( 140 c, []string{"jake", "bonnibel"}, []string{"jake", "bonnibel"}, 141 ) 142 result, err := env.Instances(suite.callCtx, []instance.Id{"jake", "bonnibel"}) 143 c.Assert(err, jc.ErrorIsNil) 144 expectedMachines := set.NewStrings("jake", "bonnibel") 145 actualMachines := set.NewStrings() 146 for _, machine := range result { 147 actualMachines.Add(string(machine.Id())) 148 } 149 c.Assert(actualMachines, gc.DeepEquals, expectedMachines) 150 } 151 152 func (suite *maas2EnvironSuite) TestInstancesInvalidCredential(c *gc.C) { 153 controller := &fakeController{ 154 machinesError: gomaasapi.NewPermissionError("fail auth here"), 155 } 156 env := suite.makeEnviron(c, controller) 157 c.Assert(suite.invalidCredential, jc.IsFalse) 158 _, err := env.Instances(suite.callCtx, []instance.Id{"jake", "bonnibel"}) 159 c.Assert(err, gc.NotNil) 160 c.Assert(suite.invalidCredential, jc.IsTrue) 161 } 162 163 func (suite *maas2EnvironSuite) TestInstancesPartialResult(c *gc.C) { 164 env := suite.makeEnvironWithMachines( 165 c, []string{"jake", "bonnibel"}, []string{"tuco", "bonnibel"}, 166 ) 167 result, err := env.Instances(suite.callCtx, []instance.Id{"jake", "bonnibel"}) 168 c.Check(err, gc.Equals, environs.ErrPartialInstances) 169 c.Assert(result, gc.HasLen, 2) 170 c.Assert(result[0], gc.IsNil) 171 c.Assert(result[1].Id(), gc.Equals, instance.Id("bonnibel")) 172 } 173 174 func (suite *maas2EnvironSuite) TestAvailabilityZones(c *gc.C) { 175 env := suite.makeEnviron(c, newFakeController()) 176 result, err := env.AvailabilityZones(suite.callCtx) 177 c.Assert(err, jc.ErrorIsNil) 178 expectedZones := set.NewStrings("mossack", "fonseca") 179 actualZones := set.NewStrings() 180 for _, zone := range result { 181 actualZones.Add(zone.Name()) 182 } 183 c.Assert(actualZones, gc.DeepEquals, expectedZones) 184 } 185 186 func (suite *maas2EnvironSuite) TestAvailabilityZonesError(c *gc.C) { 187 controller := &fakeController{ 188 zonesError: errors.New("a bad thing"), 189 } 190 env := suite.makeEnviron(c, controller) 191 _, err := env.AvailabilityZones(suite.callCtx) 192 c.Assert(err, gc.ErrorMatches, "a bad thing") 193 } 194 195 func (suite *maas2EnvironSuite) TestAvailabilityZonesInvalidCredential(c *gc.C) { 196 controller := &fakeController{ 197 zonesError: gomaasapi.NewPermissionError("fail auth here"), 198 } 199 env := suite.makeEnviron(c, controller) 200 c.Assert(suite.invalidCredential, jc.IsFalse) 201 _, err := env.AvailabilityZones(suite.callCtx) 202 c.Assert(err, gc.NotNil) 203 c.Assert(suite.invalidCredential, jc.IsTrue) 204 } 205 206 func (suite *maas2EnvironSuite) TestSpaces(c *gc.C) { 207 controller := &fakeController{ 208 spaces: []gomaasapi.Space{ 209 fakeSpace{ 210 name: "pepper", 211 id: 1234, 212 }, 213 fakeSpace{ 214 name: "freckles", 215 id: 4567, 216 subnets: []gomaasapi.Subnet{ 217 fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24"}, 218 fakeSubnet{id: 98, vlan: fakeVLAN{vid: 67}, cidr: "192.168.11.0/24"}, 219 }, 220 }, 221 }, 222 } 223 env := suite.makeEnviron(c, controller) 224 result, err := env.Spaces(suite.callCtx) 225 c.Assert(err, jc.ErrorIsNil) 226 c.Assert(result, gc.HasLen, 1) 227 c.Assert(result[0].Name, gc.Equals, "freckles") 228 c.Assert(result[0].ProviderId, gc.Equals, network.Id("4567")) 229 subnets := result[0].Subnets 230 c.Assert(subnets, gc.HasLen, 2) 231 c.Assert(subnets[0].ProviderId, gc.Equals, network.Id("99")) 232 c.Assert(subnets[0].VLANTag, gc.Equals, 66) 233 c.Assert(subnets[0].CIDR, gc.Equals, "192.168.10.0/24") 234 c.Assert(subnets[0].SpaceProviderId, gc.Equals, network.Id("4567")) 235 c.Assert(subnets[1].ProviderId, gc.Equals, network.Id("98")) 236 c.Assert(subnets[1].VLANTag, gc.Equals, 67) 237 c.Assert(subnets[1].CIDR, gc.Equals, "192.168.11.0/24") 238 c.Assert(subnets[1].SpaceProviderId, gc.Equals, network.Id("4567")) 239 } 240 241 func (suite *maas2EnvironSuite) TestSpacesError(c *gc.C) { 242 controller := &fakeController{ 243 spacesError: errors.New("Joe Manginiello"), 244 } 245 env := suite.makeEnviron(c, controller) 246 _, err := env.Spaces(suite.callCtx) 247 c.Assert(err, gc.ErrorMatches, "Joe Manginiello") 248 } 249 250 func (suite *maas2EnvironSuite) TestSpacesInvalidCredential(c *gc.C) { 251 controller := &fakeController{ 252 spacesError: gomaasapi.NewPermissionError("fail auth here"), 253 } 254 env := suite.makeEnviron(c, controller) 255 c.Assert(suite.invalidCredential, jc.IsFalse) 256 _, err := env.Spaces(suite.callCtx) 257 c.Assert(err, gc.NotNil) 258 c.Assert(suite.invalidCredential, jc.IsTrue) 259 } 260 261 func collectReleaseArgs(controller *fakeController) []gomaasapi.ReleaseMachinesArgs { 262 args := []gomaasapi.ReleaseMachinesArgs{} 263 for _, call := range controller.Stub.Calls() { 264 if call.FuncName == "ReleaseMachines" { 265 args = append(args, call.Args[0].(gomaasapi.ReleaseMachinesArgs)) 266 } 267 } 268 return args 269 } 270 271 func (suite *maas2EnvironSuite) TestStopInstancesReturnsIfParameterEmpty(c *gc.C) { 272 controller := newFakeController() 273 err := suite.makeEnviron(c, controller).StopInstances(suite.callCtx) 274 c.Check(err, jc.ErrorIsNil) 275 c.Assert(collectReleaseArgs(controller), gc.HasLen, 0) 276 } 277 278 func (suite *maas2EnvironSuite) TestStopInstancesStopsAndReleasesInstances(c *gc.C) { 279 // Return a cannot complete indicating that test1 is in the wrong state. 280 // The release operation will still release the others and succeed. 281 controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"}) 282 err := suite.makeEnviron(c, controller).StopInstances(suite.callCtx, "test1", "test2", "test3") 283 c.Check(err, jc.ErrorIsNil) 284 args := collectReleaseArgs(controller) 285 c.Assert(args, gc.HasLen, 1) 286 c.Assert(args[0].SystemIDs, gc.DeepEquals, []string{"test1", "test2", "test3"}) 287 } 288 289 func (suite *maas2EnvironSuite) TestStopInstancesIgnoresConflict(c *gc.C) { 290 // Return a cannot complete indicating that test1 is in the wrong state. 291 // The release operation will still release the others and succeed. 292 controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"}) 293 controller.SetErrors(gomaasapi.NewCannotCompleteError("test1 not allocated")) 294 err := suite.makeEnviron(c, controller).StopInstances(suite.callCtx, "test1", "test2", "test3") 295 c.Check(err, jc.ErrorIsNil) 296 297 args := collectReleaseArgs(controller) 298 c.Assert(args, gc.HasLen, 1) 299 c.Assert(args[0].SystemIDs, gc.DeepEquals, []string{"test1", "test2", "test3"}) 300 } 301 302 func (suite *maas2EnvironSuite) TestStopInstancesIgnoresMissingNodeAndRecurses(c *gc.C) { 303 controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"}) 304 controller.SetErrors( 305 gomaasapi.NewBadRequestError("no such machine: test1"), 306 gomaasapi.NewBadRequestError("no such machine: test1"), 307 ) 308 err := suite.makeEnviron(c, controller).StopInstances(suite.callCtx, "test1", "test2", "test3") 309 c.Check(err, jc.ErrorIsNil) 310 args := collectReleaseArgs(controller) 311 c.Assert(args, gc.HasLen, 4) 312 c.Assert(args[0].SystemIDs, gc.DeepEquals, []string{"test1", "test2", "test3"}) 313 c.Assert(args[1].SystemIDs, gc.DeepEquals, []string{"test1"}) 314 c.Assert(args[2].SystemIDs, gc.DeepEquals, []string{"test2"}) 315 c.Assert(args[3].SystemIDs, gc.DeepEquals, []string{"test3"}) 316 } 317 318 func (suite *maas2EnvironSuite) checkStopInstancesFails(c *gc.C, withError error) { 319 controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"}) 320 controller.SetErrors(withError) 321 err := suite.makeEnviron(c, controller).StopInstances(suite.callCtx, "test1", "test2", "test3") 322 c.Check(err, gc.ErrorMatches, fmt.Sprintf("cannot release nodes: %s", withError)) 323 // Only tries once. 324 c.Assert(collectReleaseArgs(controller), gc.HasLen, 1) 325 } 326 327 func (suite *maas2EnvironSuite) TestStopInstancesReturnsUnexpectedMAASError(c *gc.C) { 328 suite.checkStopInstancesFails(c, gomaasapi.NewNoMatchError("Something else bad!")) 329 } 330 331 func (suite *maas2EnvironSuite) TestStopInstancesReturnsUnexpectedError(c *gc.C) { 332 suite.checkStopInstancesFails(c, errors.New("Something completely unexpected!")) 333 } 334 335 func (suite *maas2EnvironSuite) TestStartInstanceError(c *gc.C) { 336 suite.injectController(&fakeController{ 337 allocateMachineError: errors.New("Charles Babbage"), 338 }) 339 env := suite.makeEnviron(c, nil) 340 _, err := env.StartInstance(suite.callCtx, environs.StartInstanceParams{}) 341 c.Assert(err, gc.ErrorMatches, "failed to acquire node: Charles Babbage") 342 } 343 344 func (suite *maas2EnvironSuite) TestStartInstance(c *gc.C) { 345 env, _ := suite.injectControllerWithSpacesAndCheck(c, nil, gomaasapi.AllocateMachineArgs{}) 346 347 params := environs.StartInstanceParams{ControllerUUID: suite.controllerUUID} 348 result, err := jujutesting.StartInstanceWithParams(env, suite.callCtx, "1", params) 349 c.Assert(err, jc.ErrorIsNil) 350 c.Assert(result.Instance.Id(), gc.Equals, instance.Id("Bruce Sterling")) 351 c.Assert(result.DisplayName, gc.Equals, "") 352 } 353 354 func (suite *maas2EnvironSuite) TestStartInstanceReturnsHostnameAsDisplayName(c *gc.C) { 355 machine := &fakeMachine{ 356 systemID: "Bruce Sterling", 357 architecture: arch.HostArch(), 358 hostname: "mirrorshades.author", 359 Stub: &testing.Stub{}, 360 statusName: "", 361 } 362 env, _ := suite.injectControllerWithMachine(c, machine, nil, gomaasapi.AllocateMachineArgs{}) 363 params := environs.StartInstanceParams{ControllerUUID: suite.controllerUUID} 364 result, err := jujutesting.StartInstanceWithParams(env, suite.callCtx, "0", params) 365 c.Assert(err, jc.ErrorIsNil) 366 c.Assert(result.Instance.Id(), gc.Equals, instance.Id("Bruce Sterling")) 367 c.Assert(result.DisplayName, gc.Equals, machine.Hostname()) 368 } 369 370 func (suite *maas2EnvironSuite) TestStartInstanceAppliesResourceTags(c *gc.C) { 371 env, controller := suite.injectControllerWithSpacesAndCheck(c, nil, gomaasapi.AllocateMachineArgs{}) 372 config := env.Config() 373 _, ok := config.ResourceTags() 374 c.Assert(ok, jc.IsTrue) 375 params := environs.StartInstanceParams{ControllerUUID: suite.controllerUUID} 376 _, err := jujutesting.StartInstanceWithParams(env, suite.callCtx, "1", params) 377 c.Assert(err, jc.ErrorIsNil) 378 379 machine := controller.allocateMachine.(*fakeMachine) 380 machine.CheckCallNames(c, "Start", "SetOwnerData") 381 c.Assert(machine.Calls()[1].Args[0], gc.DeepEquals, map[string]string{ 382 "claude": "rains", 383 tags.JujuController: suite.controllerUUID, 384 tags.JujuModel: config.UUID(), 385 }) 386 } 387 388 func (suite *maas2EnvironSuite) TestStartInstanceParams(c *gc.C) { 389 var env *maasEnviron 390 suite.injectController(&fakeController{ 391 allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) { 392 c.Assert(args, gc.DeepEquals, gomaasapi.AllocateMachineArgs{ 393 AgentName: env.Config().UUID(), 394 Zone: "foo", 395 MinMemory: 8192, 396 }) 397 }, 398 allocateMachine: newFakeMachine("Bruce Sterling", arch.HostArch(), ""), 399 allocateMachineMatches: gomaasapi.ConstraintMatches{ 400 Storage: map[string][]gomaasapi.StorageDevice{}, 401 }, 402 zones: []gomaasapi.Zone{&fakeZone{name: "foo"}}, 403 }) 404 suite.setupFakeTools(c) 405 env = suite.makeEnviron(c, nil) 406 params := environs.StartInstanceParams{ 407 ControllerUUID: suite.controllerUUID, 408 AvailabilityZone: "foo", 409 Constraints: constraints.MustParse("mem=8G"), 410 } 411 result, err := jujutesting.StartInstanceWithParams(env, suite.callCtx, "1", params) 412 c.Assert(err, jc.ErrorIsNil) 413 c.Assert(result.Instance.Id(), gc.Equals, instance.Id("Bruce Sterling")) 414 } 415 416 func (suite *maas2EnvironSuite) TestAcquireNodePassedAgentName(c *gc.C) { 417 var env *maasEnviron 418 suite.injectController(&fakeController{ 419 allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) { 420 c.Assert(args, gc.DeepEquals, gomaasapi.AllocateMachineArgs{ 421 AgentName: env.Config().UUID()}) 422 }, 423 allocateMachine: &fakeMachine{ 424 systemID: "Bruce Sterling", 425 architecture: arch.HostArch(), 426 }, 427 }) 428 suite.setupFakeTools(c) 429 env = suite.makeEnviron(c, nil) 430 431 _, err := env.acquireNode2(suite.callCtx, "", "", "", constraints.Value{}, nil, nil) 432 433 c.Check(err, jc.ErrorIsNil) 434 } 435 436 func (suite *maas2EnvironSuite) TestAcquireNodePassesPositiveAndNegativeTags(c *gc.C) { 437 var env *maasEnviron 438 expected := gomaasapi.AllocateMachineArgs{ 439 Tags: []string{"tag1", "tag3"}, 440 NotTags: []string{"tag2", "tag4"}, 441 } 442 env, _ = suite.injectControllerWithSpacesAndCheck(c, nil, expected) 443 _, err := env.acquireNode2(suite.callCtx, 444 "", "", "", 445 constraints.Value{Tags: stringslicep("tag1", "^tag2", "tag3", "^tag4")}, 446 nil, nil, 447 ) 448 c.Check(err, jc.ErrorIsNil) 449 } 450 451 func getFourSpaces() []gomaasapi.Space { 452 return []gomaasapi.Space{ 453 fakeSpace{ 454 name: "space-1", 455 subnets: []gomaasapi.Subnet{fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24"}}, 456 id: 5, 457 }, 458 fakeSpace{ 459 name: "space-2", 460 subnets: []gomaasapi.Subnet{fakeSubnet{id: 100, vlan: fakeVLAN{vid: 66}, cidr: "192.168.11.0/24"}}, 461 id: 6, 462 }, 463 fakeSpace{ 464 name: "space-3", 465 subnets: []gomaasapi.Subnet{fakeSubnet{id: 101, vlan: fakeVLAN{vid: 66}, cidr: "192.168.12.0/24"}}, 466 id: 7, 467 }, 468 fakeSpace{ 469 name: "space-4", 470 subnets: []gomaasapi.Subnet{fakeSubnet{id: 102, vlan: fakeVLAN{vid: 66}, cidr: "192.168.13.0/24"}}, 471 id: 8, 472 }, 473 } 474 } 475 476 func (suite *maas2EnvironSuite) TestAcquireNodePassesPositiveAndNegativeSpaces(c *gc.C) { 477 expected := gomaasapi.AllocateMachineArgs{ 478 NotSpace: []string{"6", "8"}, 479 Interfaces: []gomaasapi.InterfaceSpec{ 480 {Label: "0", Space: "5"}, 481 {Label: "1", Space: "7"}, 482 }, 483 } 484 env, _ := suite.injectControllerWithSpacesAndCheck(c, getFourSpaces(), expected) 485 486 _, err := env.acquireNode2(suite.callCtx, 487 "", "", "", 488 constraints.Value{Spaces: stringslicep("space-1", "^space-2", "space-3", "^space-4")}, 489 nil, nil, 490 ) 491 c.Check(err, jc.ErrorIsNil) 492 } 493 494 func (suite *maas2EnvironSuite) TestAcquireNodeDisambiguatesNamedLabelsFromIndexedUpToALimit(c *gc.C) { 495 env, _ := suite.injectControllerWithSpacesAndCheck(c, getFourSpaces(), gomaasapi.AllocateMachineArgs{}) 496 var shortLimit uint = 0 497 suite.PatchValue(&numericLabelLimit, shortLimit) 498 499 _, err := env.acquireNode2(suite.callCtx, 500 "", "", "", 501 constraints.Value{Spaces: stringslicep("space-1", "^space-2", "space-3", "^space-4")}, 502 []interfaceBinding{{"0", "first-clash"}, {"1", "final-clash"}}, 503 nil, 504 ) 505 c.Assert(err, gc.ErrorMatches, `too many conflicting numeric labels, giving up.`) 506 } 507 508 func (suite *maas2EnvironSuite) TestAcquireNodeStorage(c *gc.C) { 509 var env *maasEnviron 510 var getStorage func() []gomaasapi.StorageSpec 511 suite.injectController(&fakeController{ 512 allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) { 513 c.Assert(args, jc.DeepEquals, gomaasapi.AllocateMachineArgs{ 514 AgentName: env.Config().UUID(), 515 Storage: getStorage(), 516 }) 517 }, 518 allocateMachine: &fakeMachine{ 519 systemID: "Bruce Sterling", 520 architecture: arch.HostArch(), 521 }, 522 }) 523 suite.setupFakeTools(c) 524 for i, test := range []struct { 525 volumes []volumeInfo 526 expected []gomaasapi.StorageSpec 527 }{{ 528 volumes: nil, 529 expected: []gomaasapi.StorageSpec{}, 530 }, { 531 volumes: []volumeInfo{{"volume-1", 1234, nil}}, 532 expected: []gomaasapi.StorageSpec{{"volume-1", 1234, nil}}, 533 }, { 534 volumes: []volumeInfo{{"", 1234, []string{"tag1", "tag2"}}}, 535 expected: []gomaasapi.StorageSpec{{"", 1234, []string{"tag1", "tag2"}}}, 536 }, { 537 volumes: []volumeInfo{{"volume-1", 1234, []string{"tag1", "tag2"}}}, 538 expected: []gomaasapi.StorageSpec{{"volume-1", 1234, []string{"tag1", "tag2"}}}, 539 }, { 540 volumes: []volumeInfo{ 541 {"volume-1", 1234, []string{"tag1", "tag2"}}, 542 {"volume-2", 4567, []string{"tag1", "tag3"}}, 543 }, 544 expected: []gomaasapi.StorageSpec{ 545 {"volume-1", 1234, []string{"tag1", "tag2"}}, 546 {"volume-2", 4567, []string{"tag1", "tag3"}}, 547 }, 548 }} { 549 c.Logf("test #%d: volumes=%v", i, test.volumes) 550 getStorage = func() []gomaasapi.StorageSpec { 551 return test.expected 552 } 553 env = suite.makeEnviron(c, nil) 554 _, err := env.acquireNode2(suite.callCtx, "", "", "", constraints.Value{}, nil, test.volumes) 555 c.Check(err, jc.ErrorIsNil) 556 } 557 } 558 559 func (suite *maas2EnvironSuite) TestAcquireNodeInterfaces(c *gc.C) { 560 var env *maasEnviron 561 var getNegatives func() []string 562 var getPositives func() []gomaasapi.InterfaceSpec 563 suite.injectController(&fakeController{ 564 allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) { 565 c.Assert(args, gc.DeepEquals, gomaasapi.AllocateMachineArgs{ 566 AgentName: env.Config().UUID(), 567 Interfaces: getPositives(), 568 NotSpace: getNegatives(), 569 }) 570 }, 571 allocateMachine: &fakeMachine{ 572 systemID: "Bruce Sterling", 573 architecture: arch.HostArch(), 574 }, 575 spaces: getTwoSpaces(), 576 }) 577 suite.setupFakeTools(c) 578 // Add some constraints, including spaces to verify specified bindings 579 // always override any spaces constraints. 580 cons := constraints.Value{ 581 Spaces: stringslicep("foo", "^bar"), 582 } 583 // In the tests below Space 2 means foo, Space 3 means bar. 584 for i, test := range []struct { 585 interfaces []interfaceBinding 586 expectedPositives []gomaasapi.InterfaceSpec 587 expectedNegatives []string 588 expectedError string 589 }{{ // without specified bindings, spaces constraints are used instead. 590 interfaces: nil, 591 expectedPositives: []gomaasapi.InterfaceSpec{{"0", "2"}}, 592 expectedNegatives: []string{"3"}, 593 expectedError: "", 594 }, { 595 interfaces: []interfaceBinding{{"name-1", "space-1"}}, 596 expectedPositives: []gomaasapi.InterfaceSpec{{"name-1", "space-1"}, {"0", "2"}}, 597 expectedNegatives: []string{"3"}, 598 }, { 599 interfaces: []interfaceBinding{ 600 {"name-1", "7"}, 601 {"name-2", "8"}, 602 {"name-3", "9"}, 603 }, 604 expectedPositives: []gomaasapi.InterfaceSpec{{"name-1", "7"}, {"name-2", "8"}, {"name-3", "9"}, {"0", "2"}}, 605 expectedNegatives: []string{"3"}, 606 }, { 607 interfaces: []interfaceBinding{{"", "anything"}}, 608 expectedPositives: []gomaasapi.InterfaceSpec{{"0", "anything"}, {"1", "2"}}, 609 expectedNegatives: []string{"3"}, 610 }, { 611 interfaces: []interfaceBinding{{"shared-db", "3"}}, 612 expectedError: `negative space "bar" from constraints clashes with interface bindings`, 613 }, { 614 interfaces: []interfaceBinding{ 615 {"shared-db", "1"}, 616 {"db", "1"}, 617 }, 618 expectedPositives: []gomaasapi.InterfaceSpec{{"shared-db", "1"}, {"db", "1"}, {"0", "2"}}, 619 expectedNegatives: []string{"3"}, 620 }, { 621 interfaces: []interfaceBinding{{"", ""}}, 622 expectedError: `invalid interface binding "": space provider ID is required`, 623 }, { 624 interfaces: []interfaceBinding{ 625 {"valid", "ok"}, 626 {"", "valid-but-ignored-space"}, 627 {"valid-name-empty-space", ""}, 628 {"", ""}, 629 }, 630 expectedError: `invalid interface binding "valid-name-empty-space": space provider ID is required`, 631 }, { 632 interfaces: []interfaceBinding{{"foo", ""}}, 633 expectedError: `invalid interface binding "foo": space provider ID is required`, 634 }, { 635 interfaces: []interfaceBinding{ 636 {"bar", ""}, 637 {"valid", "ok"}, 638 {"", "valid-but-ignored-space"}, 639 {"", ""}, 640 }, 641 expectedError: `invalid interface binding "bar": space provider ID is required`, 642 }, { 643 interfaces: []interfaceBinding{ 644 {"dup-name", "1"}, 645 {"dup-name", "2"}, 646 }, 647 expectedError: `duplicated interface binding "dup-name"`, 648 }, { 649 interfaces: []interfaceBinding{ 650 {"valid-1", "0"}, 651 {"dup-name", "1"}, 652 {"dup-name", "2"}, 653 {"valid-2", "3"}, 654 }, 655 expectedError: `duplicated interface binding "dup-name"`, 656 }} { 657 c.Logf("test #%d: interfaces=%v", i, test.interfaces) 658 env = suite.makeEnviron(c, nil) 659 getNegatives = func() []string { 660 return test.expectedNegatives 661 } 662 getPositives = func() []gomaasapi.InterfaceSpec { 663 return test.expectedPositives 664 } 665 _, err := env.acquireNode2(suite.callCtx, "", "", "", cons, test.interfaces, nil) 666 if test.expectedError != "" { 667 c.Check(err, gc.ErrorMatches, test.expectedError) 668 c.Check(err, jc.Satisfies, errors.IsNotValid) 669 continue 670 } 671 c.Check(err, jc.ErrorIsNil) 672 } 673 } 674 675 func getTwoSpaces() []gomaasapi.Space { 676 return []gomaasapi.Space{ 677 fakeSpace{ 678 name: "foo", 679 subnets: []gomaasapi.Subnet{fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24"}}, 680 id: 2, 681 }, 682 fakeSpace{ 683 name: "bar", 684 subnets: []gomaasapi.Subnet{fakeSubnet{id: 100, vlan: fakeVLAN{vid: 66}, cidr: "192.168.11.0/24"}}, 685 id: 3, 686 }, 687 } 688 } 689 690 func (suite *maas2EnvironSuite) TestAcquireNodeConvertsSpaceNames(c *gc.C) { 691 expected := gomaasapi.AllocateMachineArgs{ 692 NotSpace: []string{"3"}, 693 Interfaces: []gomaasapi.InterfaceSpec{{Label: "0", Space: "2"}}, 694 } 695 env, _ := suite.injectControllerWithSpacesAndCheck(c, getTwoSpaces(), expected) 696 cons := constraints.Value{ 697 Spaces: stringslicep("foo", "^bar"), 698 } 699 _, err := env.acquireNode2(suite.callCtx, "", "", "", cons, nil, nil) 700 c.Assert(err, jc.ErrorIsNil) 701 } 702 703 func (suite *maas2EnvironSuite) TestAcquireNodeTranslatesSpaceNames(c *gc.C) { 704 expected := gomaasapi.AllocateMachineArgs{ 705 NotSpace: []string{"3"}, 706 Interfaces: []gomaasapi.InterfaceSpec{{Label: "0", Space: "2"}}, 707 } 708 env, _ := suite.injectControllerWithSpacesAndCheck(c, getTwoSpaces(), expected) 709 cons := constraints.Value{ 710 Spaces: stringslicep("foo-1", "^bar-3"), 711 } 712 _, err := env.acquireNode2(suite.callCtx, "", "", "", cons, nil, nil) 713 c.Assert(err, jc.ErrorIsNil) 714 } 715 716 func (suite *maas2EnvironSuite) TestAcquireNodeUnrecognisedSpace(c *gc.C) { 717 suite.injectController(&fakeController{}) 718 env := suite.makeEnviron(c, nil) 719 cons := constraints.Value{ 720 Spaces: stringslicep("baz"), 721 } 722 _, err := env.acquireNode2(suite.callCtx, "", "", "", cons, nil, nil) 723 c.Assert(err, gc.ErrorMatches, `unrecognised space in constraint "baz"`) 724 } 725 726 func (suite *maas2EnvironSuite) TestWaitForNodeDeploymentError(c *gc.C) { 727 machine := newFakeMachine("Bruce Sterling", arch.HostArch(), "") 728 controller := newFakeController() 729 controller.allocateMachine = machine 730 controller.allocateMachineMatches = gomaasapi.ConstraintMatches{ 731 Storage: map[string][]gomaasapi.StorageDevice{}, 732 } 733 controller.machines = []gomaasapi.Machine{machine} 734 suite.injectController(controller) 735 suite.setupFakeTools(c) 736 env := suite.makeEnviron(c, nil) 737 err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, 738 suite.callCtx, bootstrap.BootstrapParams{ 739 ControllerConfig: coretesting.FakeControllerConfig(), 740 AdminSecret: jujutesting.AdminSecret, 741 CAPrivateKey: coretesting.CAKey, 742 }) 743 c.Assert(err, gc.ErrorMatches, "bootstrap instance started but did not change to Deployed state.*") 744 } 745 746 func (suite *maas2EnvironSuite) TestWaitForNodeDeploymentRetry(c *gc.C) { 747 machine := newFakeMachine("Inaccessible machine", arch.HostArch(), "") 748 controller := newFakeController() 749 controller.allocateMachine = machine 750 controller.allocateMachineMatches = gomaasapi.ConstraintMatches{ 751 Storage: map[string][]gomaasapi.StorageDevice{}, 752 } 753 controller.machines = []gomaasapi.Machine{} 754 suite.injectController(controller) 755 suite.setupFakeTools(c) 756 env := suite.makeEnviron(c, nil) 757 bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, 758 suite.callCtx, bootstrap.BootstrapParams{ 759 ControllerConfig: coretesting.FakeControllerConfig(), 760 AdminSecret: jujutesting.AdminSecret, 761 CAPrivateKey: coretesting.CAKey, 762 }) 763 c.Check(c.GetTestLog(), jc.Contains, "WARNING juju.provider.maas failed to get instance from provider attempt") 764 } 765 766 func (suite *maas2EnvironSuite) TestWaitForNodeDeploymentSucceeds(c *gc.C) { 767 machine := newFakeMachine("Bruce Sterling", arch.HostArch(), "Deployed") 768 controller := newFakeController() 769 controller.allocateMachine = machine 770 controller.allocateMachineMatches = gomaasapi.ConstraintMatches{ 771 Storage: map[string][]gomaasapi.StorageDevice{}, 772 } 773 controller.machines = []gomaasapi.Machine{machine} 774 suite.injectController(controller) 775 suite.setupFakeTools(c) 776 env := suite.makeEnviron(c, nil) 777 err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, 778 suite.callCtx, bootstrap.BootstrapParams{ 779 ControllerConfig: coretesting.FakeControllerConfig(), 780 AdminSecret: jujutesting.AdminSecret, 781 CAPrivateKey: coretesting.CAKey, 782 }) 783 c.Assert(err, jc.ErrorIsNil) 784 } 785 786 func (suite *maas2EnvironSuite) TestSubnetsNoFilters(c *gc.C) { 787 suite.injectController(&fakeController{ 788 spaces: getFourSpaces(), 789 }) 790 env := suite.makeEnviron(c, nil) 791 subnets, err := env.Subnets(suite.callCtx, "", nil) 792 c.Assert(err, jc.ErrorIsNil) 793 expected := []network.SubnetInfo{ 794 {CIDR: "192.168.10.0/24", ProviderId: "99", VLANTag: 66, SpaceProviderId: "5"}, 795 {CIDR: "192.168.11.0/24", ProviderId: "100", VLANTag: 66, SpaceProviderId: "6"}, 796 {CIDR: "192.168.12.0/24", ProviderId: "101", VLANTag: 66, SpaceProviderId: "7"}, 797 {CIDR: "192.168.13.0/24", ProviderId: "102", VLANTag: 66, SpaceProviderId: "8"}, 798 } 799 c.Assert(subnets, jc.DeepEquals, expected) 800 } 801 802 func (suite *maas2EnvironSuite) TestSubnetsNoFiltersError(c *gc.C) { 803 suite.injectController(&fakeController{ 804 spacesError: errors.New("bang"), 805 }) 806 env := suite.makeEnviron(c, nil) 807 _, err := env.Subnets(suite.callCtx, "", nil) 808 c.Assert(err, gc.ErrorMatches, "bang") 809 } 810 811 func (suite *maas2EnvironSuite) TestSubnetsSubnetIds(c *gc.C) { 812 suite.injectController(&fakeController{ 813 spaces: getFourSpaces(), 814 }) 815 env := suite.makeEnviron(c, nil) 816 subnets, err := env.Subnets(suite.callCtx, "", []network.Id{"99", "100"}) 817 c.Assert(err, jc.ErrorIsNil) 818 expected := []network.SubnetInfo{ 819 {CIDR: "192.168.10.0/24", ProviderId: "99", VLANTag: 66, SpaceProviderId: "5"}, 820 {CIDR: "192.168.11.0/24", ProviderId: "100", VLANTag: 66, SpaceProviderId: "6"}, 821 } 822 c.Assert(subnets, jc.DeepEquals, expected) 823 } 824 825 func (suite *maas2EnvironSuite) TestSubnetsSubnetIdsMissing(c *gc.C) { 826 suite.injectController(&fakeController{ 827 spaces: getFourSpaces(), 828 }) 829 env := suite.makeEnviron(c, nil) 830 _, err := env.Subnets(suite.callCtx, "", []network.Id{"99", "missing"}) 831 msg := "failed to find the following subnets: missing" 832 c.Assert(err, gc.ErrorMatches, msg) 833 } 834 835 func (suite *maas2EnvironSuite) TestSubnetsInstIdNotFound(c *gc.C) { 836 suite.injectController(&fakeController{}) 837 env := suite.makeEnviron(c, nil) 838 _, err := env.Subnets(suite.callCtx, "foo", nil) 839 c.Assert(err, jc.Satisfies, errors.IsNotFound) 840 } 841 842 func (suite *maas2EnvironSuite) TestSubnetsInstId(c *gc.C) { 843 interfaces := []gomaasapi.Interface{ 844 &fakeInterface{ 845 links: []gomaasapi.Link{ 846 &fakeLink{subnet: fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24", space: "space-1"}}, 847 &fakeLink{subnet: fakeSubnet{id: 100, vlan: fakeVLAN{vid: 0}, cidr: "192.168.11.0/24", space: "space-2"}}, 848 }, 849 }, 850 &fakeInterface{ 851 links: []gomaasapi.Link{ 852 &fakeLink{subnet: fakeSubnet{id: 101, vlan: fakeVLAN{vid: 2}, cidr: "192.168.12.0/24", space: "space-3"}}, 853 }, 854 }, 855 } 856 machine := &fakeMachine{ 857 systemID: "William Gibson", 858 interfaceSet: interfaces, 859 } 860 machine2 := &fakeMachine{systemID: "Bruce Sterling"} 861 suite.injectController(&fakeController{ 862 machines: []gomaasapi.Machine{machine, machine2}, 863 spaces: getFourSpaces(), 864 }) 865 env := suite.makeEnviron(c, nil) 866 subnets, err := env.Subnets(suite.callCtx, "William Gibson", nil) 867 c.Assert(err, jc.ErrorIsNil) 868 expected := []network.SubnetInfo{ 869 {CIDR: "192.168.10.0/24", ProviderId: "99", VLANTag: 66, SpaceProviderId: "5"}, 870 {CIDR: "192.168.11.0/24", ProviderId: "100", VLANTag: 0, SpaceProviderId: "6"}, 871 {CIDR: "192.168.12.0/24", ProviderId: "101", VLANTag: 2, SpaceProviderId: "7"}, 872 } 873 c.Assert(subnets, jc.DeepEquals, expected) 874 } 875 876 func (suite *maas2EnvironSuite) TestStartInstanceNetworkInterfaces(c *gc.C) { 877 vlan0 := fakeVLAN{ 878 id: 5001, 879 vid: 0, 880 mtu: 1500, 881 } 882 883 vlan50 := fakeVLAN{ 884 id: 5004, 885 vid: 50, 886 mtu: 1500, 887 } 888 889 subnetPXE := fakeSubnet{ 890 id: 3, 891 space: "default", 892 vlan: vlan0, 893 gateway: "10.20.19.2", 894 cidr: "10.20.19.0/24", 895 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 896 } 897 898 exampleInterfaces := []gomaasapi.Interface{ 899 &fakeInterface{ 900 id: 91, 901 name: "eth0", 902 type_: "physical", 903 enabled: true, 904 macAddress: "52:54:00:70:9b:fe", 905 vlan: vlan0, 906 links: []gomaasapi.Link{ 907 &fakeLink{ 908 id: 436, 909 subnet: &subnetPXE, 910 ipAddress: "10.20.19.103", 911 mode: "static", 912 }, 913 &fakeLink{ 914 id: 437, 915 subnet: &subnetPXE, 916 ipAddress: "10.20.19.104", 917 mode: "static", 918 }, 919 }, 920 parents: []string{}, 921 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 922 }, 923 &fakeInterface{ 924 id: 150, 925 name: "eth0.50", 926 type_: "vlan", 927 enabled: true, 928 macAddress: "52:54:00:70:9b:fe", 929 vlan: vlan50, 930 links: []gomaasapi.Link{ 931 &fakeLink{ 932 id: 517, 933 subnet: &fakeSubnet{ 934 id: 5, 935 space: "admin", 936 vlan: vlan50, 937 gateway: "10.50.19.2", 938 cidr: "10.50.19.0/24", 939 dnsServers: []string{}, 940 }, 941 ipAddress: "10.50.19.103", 942 mode: "static", 943 }, 944 }, 945 parents: []string{"eth0"}, 946 children: []string{}, 947 }, 948 } 949 var env *maasEnviron 950 machine := newFakeMachine("Bruce Sterling", arch.HostArch(), "") 951 machine.interfaceSet = exampleInterfaces 952 controller := &fakeController{ 953 allocateMachine: machine, 954 allocateMachineMatches: gomaasapi.ConstraintMatches{ 955 Storage: map[string][]gomaasapi.StorageDevice{}, 956 }, 957 } 958 suite.injectController(controller) 959 suite.setupFakeTools(c) 960 env = suite.makeEnviron(c, nil) 961 962 params := environs.StartInstanceParams{ControllerUUID: suite.controllerUUID} 963 result, err := jujutesting.StartInstanceWithParams(env, suite.callCtx, "1", params) 964 c.Assert(err, jc.ErrorIsNil) 965 expected := []network.InterfaceInfo{{ 966 DeviceIndex: 0, 967 MACAddress: "52:54:00:70:9b:fe", 968 CIDR: "10.20.19.0/24", 969 ProviderId: "91", 970 ProviderSubnetId: "3", 971 AvailabilityZones: nil, 972 VLANTag: 0, 973 ProviderVLANId: "5001", 974 ProviderAddressId: "436", 975 InterfaceName: "eth0", 976 InterfaceType: "ethernet", 977 Disabled: false, 978 NoAutoStart: false, 979 ConfigType: "static", 980 Address: network.NewAddressOnSpace("default", "10.20.19.103"), 981 DNSServers: network.NewAddressesOnSpace("default", "10.20.19.2", "10.20.19.3"), 982 DNSSearchDomains: nil, 983 MTU: 1500, 984 GatewayAddress: network.NewAddressOnSpace("default", "10.20.19.2"), 985 }, { 986 DeviceIndex: 0, 987 MACAddress: "52:54:00:70:9b:fe", 988 CIDR: "10.20.19.0/24", 989 ProviderId: "91", 990 ProviderSubnetId: "3", 991 AvailabilityZones: nil, 992 VLANTag: 0, 993 ProviderVLANId: "5001", 994 ProviderAddressId: "437", 995 InterfaceName: "eth0", 996 InterfaceType: "ethernet", 997 Disabled: false, 998 NoAutoStart: false, 999 ConfigType: "static", 1000 Address: network.NewAddressOnSpace("default", "10.20.19.104"), 1001 DNSServers: network.NewAddressesOnSpace("default", "10.20.19.2", "10.20.19.3"), 1002 DNSSearchDomains: nil, 1003 MTU: 1500, 1004 GatewayAddress: network.NewAddressOnSpace("default", "10.20.19.2"), 1005 }, { 1006 DeviceIndex: 1, 1007 MACAddress: "52:54:00:70:9b:fe", 1008 CIDR: "10.50.19.0/24", 1009 ProviderId: "150", 1010 ProviderSubnetId: "5", 1011 AvailabilityZones: nil, 1012 VLANTag: 50, 1013 ProviderVLANId: "5004", 1014 ProviderAddressId: "517", 1015 InterfaceName: "eth0.50", 1016 ParentInterfaceName: "eth0", 1017 InterfaceType: "802.1q", 1018 Disabled: false, 1019 NoAutoStart: false, 1020 ConfigType: "static", 1021 Address: network.NewAddressOnSpace("admin", "10.50.19.103"), 1022 DNSServers: nil, 1023 DNSSearchDomains: nil, 1024 MTU: 1500, 1025 GatewayAddress: network.NewAddressOnSpace("admin", "10.50.19.2"), 1026 }, 1027 } 1028 c.Assert(result.NetworkInfo, jc.DeepEquals, expected) 1029 } 1030 1031 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSingleNic(c *gc.C) { 1032 vlan1 := fakeVLAN{ 1033 id: 5001, 1034 mtu: 1500, 1035 } 1036 vlan2 := fakeVLAN{ 1037 id: 5002, 1038 mtu: 1500, 1039 } 1040 subnet1 := fakeSubnet{ 1041 id: 3, 1042 space: "default", 1043 vlan: vlan1, 1044 gateway: "10.20.19.2", 1045 cidr: "10.20.19.0/24", 1046 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 1047 } 1048 subnet2 := fakeSubnet{ 1049 id: 4, 1050 space: "freckles", 1051 vlan: vlan2, 1052 gateway: "192.168.1.1", 1053 cidr: "192.168.1.0/24", 1054 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 1055 } 1056 staticRoute2to1 := fakeStaticRoute{ 1057 id: 1, 1058 source: subnet2, 1059 destination: subnet1, 1060 gatewayIP: "192.168.1.1", 1061 metric: 100, 1062 } 1063 1064 interfaces := []gomaasapi.Interface{ 1065 &fakeInterface{ 1066 id: 91, 1067 name: "eth0", 1068 type_: "physical", 1069 enabled: true, 1070 macAddress: "52:54:00:70:9b:fe", 1071 vlan: vlan1, 1072 links: []gomaasapi.Link{ 1073 &fakeLink{ 1074 id: 436, 1075 subnet: &subnet1, 1076 ipAddress: "10.20.19.103", 1077 mode: "static", 1078 }, 1079 }, 1080 parents: []string{}, 1081 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 1082 }, 1083 } 1084 deviceInterfaces := []gomaasapi.Interface{ 1085 &fakeInterface{ 1086 id: 93, 1087 name: "eth1", 1088 type_: "physical", 1089 enabled: true, 1090 macAddress: "53:54:00:70:9b:ff", 1091 vlan: vlan2, 1092 links: []gomaasapi.Link{ 1093 &fakeLink{ 1094 id: 480, 1095 subnet: &subnet2, 1096 ipAddress: "192.168.1.127", 1097 mode: "static", 1098 }, 1099 }, 1100 parents: []string{}, 1101 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 1102 }, 1103 } 1104 var env *maasEnviron 1105 device := &fakeDevice{ 1106 interfaceSet: deviceInterfaces, 1107 systemID: "foo", 1108 } 1109 controller := &fakeController{ 1110 Stub: &testing.Stub{}, 1111 machines: []gomaasapi.Machine{&fakeMachine{ 1112 Stub: &testing.Stub{}, 1113 systemID: "1", 1114 architecture: arch.HostArch(), 1115 interfaceSet: interfaces, 1116 createDevice: device, 1117 }}, 1118 spaces: []gomaasapi.Space{ 1119 fakeSpace{ 1120 name: "freckles", 1121 id: 4567, 1122 subnets: []gomaasapi.Subnet{subnet1, subnet2}, 1123 }, 1124 }, 1125 devices: []gomaasapi.Device{device}, 1126 staticRoutes: []gomaasapi.StaticRoute{staticRoute2to1}, 1127 } 1128 suite.injectController(controller) 1129 suite.setupFakeTools(c) 1130 env = suite.makeEnviron(c, nil) 1131 1132 prepared := []network.InterfaceInfo{{ 1133 MACAddress: "52:54:00:70:9b:fe", 1134 CIDR: "10.20.19.0/24", 1135 InterfaceName: "eth0", 1136 }} 1137 ignored := names.NewMachineTag("1/lxd/0") 1138 result, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) 1139 c.Assert(err, jc.ErrorIsNil) 1140 expected := []network.InterfaceInfo{{ 1141 DeviceIndex: 0, 1142 MACAddress: "53:54:00:70:9b:ff", 1143 CIDR: "192.168.1.0/24", 1144 ProviderId: "93", 1145 ProviderSubnetId: "4", 1146 VLANTag: 0, 1147 ProviderVLANId: "5002", 1148 ProviderAddressId: "480", 1149 InterfaceName: "eth1", 1150 InterfaceType: "ethernet", 1151 ConfigType: "static", 1152 Address: network.NewAddressOnSpace("freckles", "192.168.1.127"), 1153 DNSServers: network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"), 1154 MTU: 1500, 1155 GatewayAddress: network.NewAddressOnSpace("freckles", "192.168.1.1"), 1156 Routes: []network.Route{{ 1157 DestinationCIDR: subnet1.CIDR(), 1158 GatewayIP: "192.168.1.1", 1159 Metric: 100, 1160 }}, 1161 }} 1162 c.Assert(result, jc.DeepEquals, expected) 1163 } 1164 1165 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesNoStaticRoutesAPI(c *gc.C) { 1166 // MAAS 2.0 doesn't have support for static routes, and generates an Error 1167 vlan1 := fakeVLAN{ 1168 id: 5001, 1169 mtu: 1500, 1170 } 1171 subnet1 := fakeSubnet{ 1172 id: 3, 1173 space: "freckles", 1174 vlan: vlan1, 1175 gateway: "10.20.19.2", 1176 cidr: "10.20.19.0/24", 1177 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 1178 } 1179 1180 interfaces := []gomaasapi.Interface{ 1181 &fakeInterface{ 1182 id: 91, 1183 name: "eth0", 1184 type_: "physical", 1185 enabled: true, 1186 macAddress: "52:54:00:70:9b:fe", 1187 vlan: vlan1, 1188 links: []gomaasapi.Link{ 1189 &fakeLink{ 1190 id: 436, 1191 subnet: &subnet1, 1192 ipAddress: "10.20.19.103", 1193 mode: "static", 1194 }, 1195 }, 1196 parents: []string{}, 1197 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 1198 }, 1199 } 1200 // This will be returned by the fakeController when we call CreateDevice 1201 deviceInterfaces := []gomaasapi.Interface{ 1202 &fakeInterface{ 1203 id: 93, 1204 name: "eth0", 1205 type_: "physical", 1206 enabled: true, 1207 macAddress: "53:54:00:70:9b:ff", 1208 vlan: vlan1, 1209 links: []gomaasapi.Link{ 1210 &fakeLink{ 1211 id: 480, 1212 subnet: &subnet1, 1213 ipAddress: "10.20.19.104", 1214 mode: "static", 1215 }, 1216 }, 1217 parents: []string{}, 1218 children: []string{}, 1219 }, 1220 } 1221 stub := &testing.Stub{} 1222 device := &fakeDevice{ 1223 Stub: stub, 1224 interfaceSet: deviceInterfaces, 1225 systemID: "foo", 1226 } 1227 // MAAS 2.0 gives us this kind of error back, I'm not sure of the conten of 1228 // the Headers or BodyMessage, but it is a 404 with a particular error 1229 // string that we've seen. 1230 body := "Unknown API endpoint: /MAAS/api/2.0/static-routes/." 1231 notFound := gomaasapi.ServerError{ 1232 StatusCode: http.StatusNotFound, 1233 BodyMessage: body, 1234 } 1235 wrap1 := errors.Annotatef(notFound, "ServerError: 404 NOT FOUND (%s)", body) 1236 staticRoutesErr := gomaasapi.NewUnexpectedError(wrap1) 1237 var env *maasEnviron 1238 controller := &fakeController{ 1239 Stub: stub, 1240 machines: []gomaasapi.Machine{&fakeMachine{ 1241 Stub: stub, 1242 systemID: "1", 1243 architecture: arch.HostArch(), 1244 interfaceSet: interfaces, 1245 createDevice: device, 1246 }}, 1247 spaces: []gomaasapi.Space{ 1248 fakeSpace{ 1249 name: "freckles", 1250 id: 4567, 1251 subnets: []gomaasapi.Subnet{subnet1}, 1252 }, 1253 }, 1254 devices: []gomaasapi.Device{device}, 1255 staticRoutesError: staticRoutesErr, 1256 } 1257 suite.injectController(controller) 1258 suite.setupFakeTools(c) 1259 env = suite.makeEnviron(c, nil) 1260 1261 prepared := []network.InterfaceInfo{{ 1262 MACAddress: "52:54:00:70:9b:fe", 1263 CIDR: "10.20.19.0/24", 1264 InterfaceName: "eth0", 1265 }} 1266 ignored := names.NewMachineTag("1/lxd/0") 1267 result, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) 1268 c.Assert(err, jc.ErrorIsNil) 1269 expected := []network.InterfaceInfo{{ 1270 DeviceIndex: 0, 1271 MACAddress: "53:54:00:70:9b:ff", 1272 CIDR: "10.20.19.0/24", 1273 ProviderId: "93", 1274 ProviderSubnetId: "3", 1275 VLANTag: 0, 1276 ProviderVLANId: "5001", 1277 ProviderAddressId: "480", 1278 InterfaceName: "eth0", 1279 InterfaceType: "ethernet", 1280 ConfigType: "static", 1281 Address: network.NewAddressOnSpace("freckles", "10.20.19.104"), 1282 DNSServers: network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"), 1283 MTU: 1500, 1284 GatewayAddress: network.NewAddressOnSpace("freckles", "10.20.19.2"), 1285 Routes: []network.Route{}, 1286 }} 1287 c.Assert(result, jc.DeepEquals, expected) 1288 } 1289 1290 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesStaticRoutesDenied(c *gc.C) { 1291 // I don't have a specific error that we've triggered, but we want to make 1292 // sure that we don't suppress all error responses from MAAS just because 1293 // we know we want to skip 404 1294 vlan1 := fakeVLAN{ 1295 id: 5001, 1296 mtu: 1500, 1297 } 1298 subnet1 := fakeSubnet{ 1299 id: 3, 1300 space: "freckles", 1301 vlan: vlan1, 1302 gateway: "10.20.19.2", 1303 cidr: "10.20.19.0/24", 1304 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 1305 } 1306 1307 interfaces := []gomaasapi.Interface{ 1308 &fakeInterface{ 1309 id: 91, 1310 name: "eth0", 1311 type_: "physical", 1312 enabled: true, 1313 macAddress: "52:54:00:70:9b:fe", 1314 vlan: vlan1, 1315 links: []gomaasapi.Link{ 1316 &fakeLink{ 1317 id: 436, 1318 subnet: &subnet1, 1319 ipAddress: "10.20.19.103", 1320 mode: "static", 1321 }, 1322 }, 1323 parents: []string{}, 1324 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 1325 }, 1326 } 1327 body := "I have failed you" 1328 internalError := gomaasapi.ServerError{ 1329 StatusCode: http.StatusInternalServerError, 1330 BodyMessage: body, 1331 } 1332 staticRoutesErr := errors.Annotatef(internalError, "ServerError: %v (%s)", http.StatusInternalServerError, body) 1333 var env *maasEnviron 1334 controller := &fakeController{ 1335 Stub: &testing.Stub{}, 1336 machines: []gomaasapi.Machine{&fakeMachine{ 1337 Stub: &testing.Stub{}, 1338 systemID: "1", 1339 architecture: arch.HostArch(), 1340 interfaceSet: interfaces, 1341 }}, 1342 spaces: []gomaasapi.Space{ 1343 fakeSpace{ 1344 name: "freckles", 1345 id: 4567, 1346 subnets: []gomaasapi.Subnet{subnet1}, 1347 }, 1348 }, 1349 staticRoutesError: staticRoutesErr, 1350 } 1351 suite.injectController(controller) 1352 suite.setupFakeTools(c) 1353 env = suite.makeEnviron(c, nil) 1354 1355 prepared := []network.InterfaceInfo{{ 1356 MACAddress: "52:54:00:70:9b:fe", 1357 CIDR: "10.20.19.0/24", 1358 InterfaceName: "eth0", 1359 }} 1360 ignored := names.NewMachineTag("1/lxd/0") 1361 _, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) 1362 c.Assert(err, gc.NotNil) 1363 c.Assert(err, gc.ErrorMatches, ".*ServerError: 500 \\(I have failed you\\).*") 1364 } 1365 1366 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesDualNic(c *gc.C) { 1367 vlan1 := fakeVLAN{ 1368 id: 5001, 1369 mtu: 1500, 1370 } 1371 vlan2 := fakeVLAN{ 1372 id: 5002, 1373 mtu: 1500, 1374 } 1375 subnet1 := fakeSubnet{ 1376 id: 3, 1377 space: "freckles", 1378 vlan: vlan1, 1379 gateway: "10.20.19.2", 1380 cidr: "10.20.19.0/24", 1381 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 1382 } 1383 subnet2 := fakeSubnet{ 1384 id: 4, 1385 space: "freckles", 1386 vlan: vlan2, 1387 gateway: "192.168.1.1", 1388 cidr: "192.168.1.0/24", 1389 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 1390 } 1391 staticRoute2to1 := fakeStaticRoute{ 1392 id: 1, 1393 source: subnet2, 1394 destination: subnet1, 1395 gatewayIP: "192.168.1.1", 1396 metric: 100, 1397 } 1398 1399 interfaces := []gomaasapi.Interface{ 1400 &fakeInterface{ 1401 id: 91, 1402 name: "eth0", 1403 type_: "physical", 1404 enabled: true, 1405 macAddress: "52:54:00:70:9b:fe", 1406 vlan: vlan1, 1407 links: []gomaasapi.Link{ 1408 &fakeLink{ 1409 id: 436, 1410 subnet: &subnet1, 1411 ipAddress: "10.20.19.103", 1412 mode: "static", 1413 }, 1414 }, 1415 parents: []string{}, 1416 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 1417 }, 1418 &fakeInterface{ 1419 id: 92, 1420 name: "eth1", 1421 type_: "physical", 1422 enabled: true, 1423 macAddress: "52:54:00:70:9b:ff", 1424 vlan: vlan2, 1425 links: []gomaasapi.Link{ 1426 &fakeLink{ 1427 id: 437, 1428 subnet: &subnet2, 1429 ipAddress: "192.168.1.4", 1430 mode: "static", 1431 }, 1432 }, 1433 }, 1434 } 1435 deviceInterfaces := []gomaasapi.Interface{ 1436 &fakeInterface{ 1437 id: 93, 1438 name: "eth0", 1439 type_: "physical", 1440 enabled: true, 1441 macAddress: "53:54:00:70:9b:ff", 1442 vlan: vlan1, 1443 links: []gomaasapi.Link{ 1444 &fakeLink{ 1445 id: 480, 1446 subnet: &subnet1, 1447 ipAddress: "10.20.19.127", 1448 mode: "static", 1449 }, 1450 }, 1451 parents: []string{}, 1452 children: []string{"eth0.100", "eth0.250", "eth0.50"}, 1453 Stub: &testing.Stub{}, 1454 }, 1455 } 1456 newInterface := &fakeInterface{ 1457 id: 94, 1458 name: "eth1", 1459 type_: "physical", 1460 enabled: true, 1461 macAddress: "52:54:00:70:9b:f4", 1462 vlan: vlan2, 1463 links: []gomaasapi.Link{ 1464 &fakeLink{ 1465 id: 481, 1466 subnet: &subnet2, 1467 ipAddress: "192.168.1.127", 1468 mode: "static", 1469 }, 1470 }, 1471 Stub: &testing.Stub{}, 1472 } 1473 device := &fakeDevice{ 1474 interfaceSet: deviceInterfaces, 1475 systemID: "foo", 1476 interface_: newInterface, 1477 Stub: &testing.Stub{}, 1478 } 1479 controller := &fakeController{ 1480 Stub: &testing.Stub{}, 1481 machines: []gomaasapi.Machine{&fakeMachine{ 1482 Stub: &testing.Stub{}, 1483 systemID: "1", 1484 architecture: arch.HostArch(), 1485 interfaceSet: interfaces, 1486 createDevice: device, 1487 }}, 1488 spaces: []gomaasapi.Space{ 1489 fakeSpace{ 1490 name: "freckles", 1491 id: 4567, 1492 subnets: []gomaasapi.Subnet{subnet1, subnet2}, 1493 }, 1494 }, 1495 devices: []gomaasapi.Device{device}, 1496 staticRoutes: []gomaasapi.StaticRoute{staticRoute2to1}, 1497 } 1498 suite.injectController(controller) 1499 env := suite.makeEnviron(c, nil) 1500 1501 prepared := []network.InterfaceInfo{{ 1502 MACAddress: "53:54:00:70:9b:ff", 1503 CIDR: "10.20.19.0/24", 1504 InterfaceName: "eth0", 1505 }, { 1506 MACAddress: "52:54:00:70:9b:f4", 1507 CIDR: "192.168.1.0/24", 1508 InterfaceName: "eth1", 1509 }} 1510 expected := []network.InterfaceInfo{{ 1511 DeviceIndex: 0, 1512 MACAddress: "53:54:00:70:9b:ff", 1513 CIDR: "10.20.19.0/24", 1514 ProviderId: "93", 1515 ProviderSubnetId: "3", 1516 ProviderVLANId: "5001", 1517 ProviderAddressId: "480", 1518 InterfaceName: "eth0", 1519 InterfaceType: "ethernet", 1520 ConfigType: "static", 1521 Address: network.NewAddressOnSpace("freckles", "10.20.19.127"), 1522 DNSServers: network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"), 1523 MTU: 1500, 1524 GatewayAddress: network.NewAddressOnSpace("freckles", "10.20.19.2"), 1525 }, { 1526 DeviceIndex: 1, 1527 MACAddress: "52:54:00:70:9b:f4", 1528 CIDR: "192.168.1.0/24", 1529 ProviderId: "94", 1530 ProviderSubnetId: "4", 1531 ProviderVLANId: "5002", 1532 ProviderAddressId: "481", 1533 InterfaceName: "eth1", 1534 InterfaceType: "ethernet", 1535 ConfigType: "static", 1536 Address: network.NewAddressOnSpace("freckles", "192.168.1.127"), 1537 DNSServers: network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"), 1538 MTU: 1500, 1539 GatewayAddress: network.NewAddressOnSpace("freckles", "192.168.1.1"), 1540 Routes: []network.Route{{ 1541 DestinationCIDR: "10.20.19.0/24", 1542 GatewayIP: "192.168.1.1", 1543 Metric: 100, 1544 }}, 1545 }} 1546 ignored := names.NewMachineTag("1/lxd/0") 1547 result, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) 1548 c.Assert(err, jc.ErrorIsNil) 1549 c.Assert(result, jc.DeepEquals, expected) 1550 } 1551 1552 func (suite *maas2EnvironSuite) assertAllocateContainerAddressesFails(c *gc.C, controller *fakeController, prepared []network.InterfaceInfo, errorMatches string) { 1553 if prepared == nil { 1554 prepared = []network.InterfaceInfo{{}} 1555 } 1556 suite.injectController(controller) 1557 env := suite.makeEnviron(c, nil) 1558 ignored := names.NewMachineTag("1/lxd/0") 1559 _, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) 1560 c.Assert(err, gc.ErrorMatches, errorMatches) 1561 } 1562 1563 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSpacesError(c *gc.C) { 1564 machine := &fakeMachine{ 1565 Stub: &testing.Stub{}, 1566 systemID: "1", 1567 } 1568 controller := &fakeController{ 1569 machines: []gomaasapi.Machine{machine}, 1570 spacesError: errors.New("boom"), 1571 } 1572 suite.assertAllocateContainerAddressesFails(c, controller, nil, "boom") 1573 } 1574 1575 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesPrimaryInterfaceMissing(c *gc.C) { 1576 machine := &fakeMachine{ 1577 Stub: &testing.Stub{}, 1578 systemID: "1", 1579 } 1580 controller := &fakeController{ 1581 machines: []gomaasapi.Machine{machine}, 1582 } 1583 suite.assertAllocateContainerAddressesFails(c, controller, nil, "cannot find primary interface for container") 1584 } 1585 1586 func makeFakeSubnet(id int) fakeSubnet { 1587 return fakeSubnet{ 1588 id: id, 1589 space: "freckles", 1590 gateway: fmt.Sprintf("10.20.%d.2", 16+id), 1591 cidr: fmt.Sprintf("10.20.%d.0/24", 16+id), 1592 } 1593 } 1594 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesMachinesError(c *gc.C) { 1595 var env *maasEnviron 1596 subnet := makeFakeSubnet(3) 1597 checkMachinesArgs := func(args gomaasapi.MachinesArgs) { 1598 expected := gomaasapi.MachinesArgs{ 1599 AgentName: env.Config().UUID(), 1600 SystemIDs: []string{"1"}, 1601 } 1602 c.Assert(args, jc.DeepEquals, expected) 1603 } 1604 controller := &fakeController{ 1605 machinesError: errors.New("boom"), 1606 machinesArgsCheck: checkMachinesArgs, 1607 spaces: []gomaasapi.Space{ 1608 fakeSpace{ 1609 name: "freckles", 1610 id: 4567, 1611 subnets: []gomaasapi.Subnet{subnet}, 1612 }, 1613 }, 1614 } 1615 suite.injectController(controller) 1616 env = suite.makeEnviron(c, nil) 1617 prepared := []network.InterfaceInfo{ 1618 {InterfaceName: "eth0", CIDR: "10.20.19.0/24"}, 1619 } 1620 ignored := names.NewMachineTag("1/lxd/0") 1621 _, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) 1622 c.Assert(err, gc.ErrorMatches, "boom") 1623 } 1624 1625 func getArgs(c *gc.C, calls []testing.StubCall, callNum, argNum int) interface{} { 1626 c.Assert(len(calls), gc.Not(jc.LessThan), callNum) 1627 args := calls[callNum].Args 1628 c.Assert(len(args), gc.Not(jc.LessThan), argNum) 1629 return args[argNum] 1630 } 1631 1632 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesCreateDeviceError(c *gc.C) { 1633 subnet := makeFakeSubnet(3) 1634 var env *maasEnviron 1635 machine := &fakeMachine{ 1636 Stub: &testing.Stub{}, 1637 systemID: "1", 1638 } 1639 machine.SetErrors(nil, errors.New("bad device call")) 1640 controller := &fakeController{ 1641 machines: []gomaasapi.Machine{machine}, 1642 spaces: []gomaasapi.Space{ 1643 fakeSpace{ 1644 name: "freckles", 1645 id: 4567, 1646 subnets: []gomaasapi.Subnet{subnet}, 1647 }, 1648 }, 1649 } 1650 suite.injectController(controller) 1651 env = suite.makeEnviron(c, nil) 1652 prepared := []network.InterfaceInfo{ 1653 {InterfaceName: "eth0", CIDR: "10.20.19.0/24", MACAddress: "DEADBEEF"}, 1654 } 1655 ignored := names.NewMachineTag("1/lxd/0") 1656 _, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) 1657 c.Assert(err, gc.ErrorMatches, `failed to create MAAS device for "juju-06f00d-1-lxd-0": bad device call`) 1658 machine.CheckCall(c, 0, "Devices", gomaasapi.DevicesArgs{ 1659 Hostname: []string{"juju-06f00d-1-lxd-0"}, 1660 }) 1661 machine.CheckCall(c, 1, "CreateDevice", gomaasapi.CreateMachineDeviceArgs{ 1662 Hostname: "juju-06f00d-1-lxd-0", 1663 Subnet: subnet, 1664 MACAddress: "DEADBEEF", 1665 InterfaceName: "eth0", 1666 }) 1667 } 1668 1669 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSubnetMissing(c *gc.C) { 1670 subnet := makeFakeSubnet(3) 1671 var env *maasEnviron 1672 device := &fakeDevice{ 1673 Stub: &testing.Stub{}, 1674 interfaceSet: []gomaasapi.Interface{ 1675 &fakeInterface{ 1676 id: 93, 1677 name: "eth0", 1678 type_: "physical", 1679 enabled: true, 1680 macAddress: "53:54:00:70:9b:ff", 1681 vlan: &fakeVLAN{vid: 0}, 1682 links: []gomaasapi.Link{ 1683 &fakeLink{ 1684 id: 480, 1685 mode: "link_up", 1686 }, 1687 }, 1688 parents: []string{}, 1689 children: []string{}, 1690 Stub: &testing.Stub{}, 1691 }, 1692 }, 1693 interface_: &fakeInterface{ 1694 id: 94, 1695 name: "eth1", 1696 type_: "physical", 1697 enabled: true, 1698 macAddress: "53:54:00:70:9b:f1", 1699 vlan: &fakeVLAN{vid: 0}, 1700 links: []gomaasapi.Link{ 1701 &fakeLink{ 1702 id: 481, 1703 mode: "link_up", 1704 }, 1705 }, 1706 parents: []string{}, 1707 children: []string{}, 1708 Stub: &testing.Stub{}, 1709 }, 1710 systemID: "foo", 1711 } 1712 machine := &fakeMachine{ 1713 Stub: &testing.Stub{}, 1714 systemID: "1", 1715 createDevice: device, 1716 } 1717 controller := &fakeController{ 1718 Stub: &testing.Stub{}, 1719 machines: []gomaasapi.Machine{machine}, 1720 spaces: []gomaasapi.Space{ 1721 fakeSpace{ 1722 name: "freckles", 1723 id: 4567, 1724 subnets: []gomaasapi.Subnet{subnet}, 1725 }, 1726 }, 1727 devices: []gomaasapi.Device{device}, 1728 } 1729 suite.injectController(controller) 1730 env = suite.makeEnviron(c, nil) 1731 prepared := []network.InterfaceInfo{ 1732 {InterfaceName: "eth0", CIDR: "", MACAddress: "DEADBEEF"}, 1733 {InterfaceName: "eth1", CIDR: "", MACAddress: "DEADBEEE"}, 1734 } 1735 ignored := names.NewMachineTag("1/lxd/0") 1736 allocated, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) 1737 c.Assert(err, jc.ErrorIsNil) 1738 c.Assert(allocated, jc.DeepEquals, []network.InterfaceInfo{{ 1739 DeviceIndex: 0, 1740 MACAddress: "53:54:00:70:9b:ff", 1741 ProviderId: "93", 1742 ProviderVLANId: "0", 1743 VLANTag: 0, 1744 InterfaceName: "eth0", 1745 InterfaceType: "ethernet", 1746 Disabled: false, 1747 NoAutoStart: false, 1748 ConfigType: "manual", 1749 MTU: 1500, 1750 }, { 1751 DeviceIndex: 1, 1752 MACAddress: "53:54:00:70:9b:f1", 1753 ProviderId: "94", 1754 ProviderVLANId: "0", 1755 VLANTag: 0, 1756 InterfaceName: "eth1", 1757 InterfaceType: "ethernet", 1758 Disabled: false, 1759 NoAutoStart: false, 1760 ConfigType: "manual", 1761 MTU: 1500, 1762 }}) 1763 } 1764 1765 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesCreateInterfaceError(c *gc.C) { 1766 subnet := makeFakeSubnet(3) 1767 subnet2 := makeFakeSubnet(4) 1768 subnet2.vlan = fakeVLAN{vid: 66} 1769 var env *maasEnviron 1770 device := &fakeDevice{ 1771 Stub: &testing.Stub{}, 1772 interfaceSet: []gomaasapi.Interface{&fakeInterface{}}, 1773 systemID: "foo", 1774 } 1775 device.SetErrors(errors.New("boom")) 1776 machine := &fakeMachine{ 1777 Stub: &testing.Stub{}, 1778 systemID: "1", 1779 createDevice: device, 1780 } 1781 controller := &fakeController{ 1782 machines: []gomaasapi.Machine{machine}, 1783 spaces: []gomaasapi.Space{ 1784 fakeSpace{ 1785 name: "freckles", 1786 id: 4567, 1787 subnets: []gomaasapi.Subnet{subnet, subnet2}, 1788 }, 1789 }, 1790 } 1791 suite.injectController(controller) 1792 env = suite.makeEnviron(c, nil) 1793 prepared := []network.InterfaceInfo{ 1794 {InterfaceName: "eth0", CIDR: "10.20.19.0/24", MACAddress: "DEADBEEF"}, 1795 {InterfaceName: "eth1", CIDR: "10.20.20.0/24", MACAddress: "DEADBEEE"}, 1796 } 1797 ignored := names.NewMachineTag("1/lxd/0") 1798 _, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) 1799 c.Assert(err, gc.ErrorMatches, `failed to create MAAS device for "juju-06f00d-1-lxd-0": creating device interface: boom`) 1800 args := getArgs(c, device.Calls(), 0, 0) 1801 maasArgs, ok := args.(gomaasapi.CreateInterfaceArgs) 1802 c.Assert(ok, jc.IsTrue) 1803 expected := gomaasapi.CreateInterfaceArgs{ 1804 MACAddress: "DEADBEEE", 1805 Name: "eth1", 1806 VLAN: subnet2.VLAN(), 1807 } 1808 c.Assert(maasArgs, jc.DeepEquals, expected) 1809 } 1810 1811 func (suite *maas2EnvironSuite) TestAllocateContainerAddressesLinkSubnetError(c *gc.C) { 1812 subnet := makeFakeSubnet(3) 1813 subnet2 := makeFakeSubnet(4) 1814 subnet2.vlan = fakeVLAN{vid: 66} 1815 var env *maasEnviron 1816 interface_ := &fakeInterface{Stub: &testing.Stub{}} 1817 interface_.SetErrors(errors.New("boom")) 1818 device := &fakeDevice{ 1819 Stub: &testing.Stub{}, 1820 interfaceSet: []gomaasapi.Interface{&fakeInterface{}}, 1821 interface_: interface_, 1822 systemID: "foo", 1823 } 1824 machine := &fakeMachine{ 1825 Stub: &testing.Stub{}, 1826 systemID: "1", 1827 createDevice: device, 1828 } 1829 controller := &fakeController{ 1830 Stub: &testing.Stub{}, 1831 machines: []gomaasapi.Machine{machine}, 1832 spaces: []gomaasapi.Space{ 1833 fakeSpace{ 1834 name: "freckles", 1835 id: 4567, 1836 subnets: []gomaasapi.Subnet{subnet, subnet2}, 1837 }, 1838 }, 1839 devices: []gomaasapi.Device{device}, 1840 } 1841 suite.injectController(controller) 1842 env = suite.makeEnviron(c, nil) 1843 prepared := []network.InterfaceInfo{ 1844 {InterfaceName: "eth0", CIDR: "10.20.19.0/24", MACAddress: "DEADBEEF"}, 1845 {InterfaceName: "eth1", CIDR: "10.20.20.0/24", MACAddress: "DEADBEEE"}, 1846 } 1847 ignored := names.NewMachineTag("1/lxd/0") 1848 _, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), ignored, prepared) 1849 c.Assert(err, gc.ErrorMatches, "failed to create MAAS device.*boom") 1850 args := getArgs(c, interface_.Calls(), 0, 0) 1851 maasArgs, ok := args.(gomaasapi.LinkSubnetArgs) 1852 c.Assert(ok, jc.IsTrue) 1853 expected := gomaasapi.LinkSubnetArgs{ 1854 Mode: gomaasapi.LinkModeStatic, 1855 Subnet: subnet2, 1856 } 1857 c.Assert(maasArgs, jc.DeepEquals, expected) 1858 } 1859 1860 func (suite *maas2EnvironSuite) TestStorageReturnsStorage(c *gc.C) { 1861 controller := newFakeController() 1862 env := suite.makeEnviron(c, controller) 1863 stor := env.Storage() 1864 c.Check(stor, gc.NotNil) 1865 1866 // The Storage object is really a maas2Storage. 1867 specificStorage := stor.(*maas2Storage) 1868 1869 // Its environment pointer refers back to its environment. 1870 c.Check(specificStorage.environ, gc.Equals, env) 1871 c.Check(specificStorage.maasController, gc.Equals, controller) 1872 } 1873 1874 func (suite *maas2EnvironSuite) TestAllocateContainerReuseExistingDevice(c *gc.C) { 1875 stub := &testing.Stub{} 1876 vlan1 := fakeVLAN{ 1877 id: 5001, 1878 mtu: 1500, 1879 } 1880 subnet1 := fakeSubnet{ 1881 id: 3, 1882 space: "space-1", 1883 vlan: vlan1, 1884 gateway: "10.20.19.2", 1885 cidr: "10.20.19.0/24", 1886 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 1887 } 1888 interfaces := []gomaasapi.Interface{ 1889 &fakeInterface{ 1890 id: 91, 1891 name: "eth0", 1892 type_: "physical", 1893 enabled: true, 1894 macAddress: "52:54:00:70:9b:fe", 1895 vlan: vlan1, 1896 links: []gomaasapi.Link{ 1897 &fakeLink{ 1898 id: 436, 1899 subnet: &subnet1, 1900 ipAddress: "10.20.19.103", 1901 mode: "static", 1902 }, 1903 }, 1904 parents: []string{}, 1905 }, 1906 } 1907 deviceInterfaces := []gomaasapi.Interface{ 1908 &fakeInterface{ 1909 id: 93, 1910 name: "eth0", 1911 type_: "physical", 1912 enabled: true, 1913 macAddress: "53:54:00:70:9b:ff", 1914 vlan: vlan1, 1915 links: []gomaasapi.Link{ 1916 &fakeLink{ 1917 id: 480, 1918 subnet: &subnet1, 1919 ipAddress: "10.20.19.105", 1920 mode: "static", 1921 }, 1922 }, 1923 parents: []string{}, 1924 }, 1925 } 1926 var env *maasEnviron 1927 device := &fakeDevice{ 1928 Stub: stub, 1929 interfaceSet: deviceInterfaces, 1930 systemID: "foo", 1931 } 1932 controller := &fakeController{ 1933 Stub: stub, 1934 machines: []gomaasapi.Machine{&fakeMachine{ 1935 Stub: stub, 1936 systemID: "1", 1937 architecture: arch.HostArch(), 1938 interfaceSet: interfaces, 1939 // Instead of having createDevice return it, Devices() 1940 // returns it from the beginning 1941 createDevice: nil, 1942 devices: []gomaasapi.Device{device}, 1943 }}, 1944 spaces: []gomaasapi.Space{ 1945 fakeSpace{ 1946 name: "space-1", 1947 id: 4567, 1948 subnets: []gomaasapi.Subnet{subnet1}, 1949 }, 1950 }, 1951 devices: []gomaasapi.Device{device}, 1952 } 1953 suite.injectController(controller) 1954 suite.setupFakeTools(c) 1955 env = suite.makeEnviron(c, nil) 1956 1957 prepared := []network.InterfaceInfo{{ 1958 MACAddress: "53:54:00:70:9b:ff", 1959 CIDR: "10.20.19.0/24", 1960 InterfaceName: "eth0", 1961 }} 1962 containerTag := names.NewMachineTag("1/lxd/0") 1963 result, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), containerTag, prepared) 1964 c.Assert(err, jc.ErrorIsNil) 1965 expected := []network.InterfaceInfo{{ 1966 DeviceIndex: 0, 1967 MACAddress: "53:54:00:70:9b:ff", 1968 CIDR: "10.20.19.0/24", 1969 ProviderId: "93", 1970 ProviderSubnetId: "3", 1971 VLANTag: 0, 1972 ProviderVLANId: "5001", 1973 ProviderAddressId: "480", 1974 InterfaceName: "eth0", 1975 InterfaceType: "ethernet", 1976 ConfigType: "static", 1977 Address: network.NewAddressOnSpace("space-1", "10.20.19.105"), 1978 DNSServers: network.NewAddressesOnSpace("space-1", "10.20.19.2", "10.20.19.3"), 1979 MTU: 1500, 1980 GatewayAddress: network.NewAddressOnSpace("space-1", "10.20.19.2"), 1981 Routes: []network.Route{}, 1982 }} 1983 c.Assert(result, jc.DeepEquals, expected) 1984 } 1985 1986 func (suite *maas2EnvironSuite) TestAllocateContainerRefusesReuseInvalidNIC(c *gc.C) { 1987 vlan1 := fakeVLAN{ 1988 id: 5001, 1989 mtu: 1500, 1990 } 1991 vlan2 := fakeVLAN{ 1992 id: 5002, 1993 mtu: 1500, 1994 } 1995 subnet1 := fakeSubnet{ 1996 id: 3, 1997 space: "freckles", 1998 vlan: vlan1, 1999 gateway: "10.20.19.2", 2000 cidr: "10.20.19.0/24", 2001 dnsServers: []string{"10.20.19.2", "10.20.19.3"}, 2002 } 2003 subnet2 := fakeSubnet{ 2004 id: 4, 2005 space: "freckles", 2006 vlan: vlan2, 2007 gateway: "192.168.1.1", 2008 cidr: "192.168.1.0/24", 2009 dnsServers: []string{"192.168.1.2"}, 2010 } 2011 subnet3 := fakeSubnet{ 2012 id: 5, 2013 space: "freckles", 2014 vlan: vlan2, 2015 gateway: "192.168.1.1", 2016 cidr: "192.168.2.0/24", 2017 dnsServers: []string{"192.168.1.2"}, 2018 } 2019 interfaces := []gomaasapi.Interface{ 2020 &fakeInterface{ 2021 id: 91, 2022 name: "eth0", 2023 type_: "physical", 2024 enabled: true, 2025 macAddress: "52:54:00:70:9b:fe", 2026 vlan: vlan1, 2027 links: []gomaasapi.Link{ 2028 &fakeLink{ 2029 id: 436, 2030 subnet: &subnet1, 2031 ipAddress: "10.20.19.103", 2032 mode: "static", 2033 }, 2034 }, 2035 parents: []string{}, 2036 }, 2037 &fakeInterface{ 2038 id: 92, 2039 name: "eth1", 2040 type_: "physical", 2041 enabled: true, 2042 macAddress: "52:54:00:70:9b:ff", 2043 vlan: vlan2, 2044 links: []gomaasapi.Link{ 2045 &fakeLink{ 2046 id: 437, 2047 subnet: &subnet2, 2048 ipAddress: "192.168.1.100", 2049 mode: "static", 2050 }, 2051 }, 2052 parents: []string{}, 2053 }, 2054 } 2055 badDeviceInterfaces := []gomaasapi.Interface{ 2056 &fakeInterface{ 2057 id: 93, 2058 name: "eth0", 2059 type_: "physical", 2060 enabled: true, 2061 macAddress: "53:54:00:70:88:aa", 2062 vlan: vlan1, 2063 links: []gomaasapi.Link{ 2064 &fakeLink{ 2065 id: 480, 2066 subnet: &subnet1, 2067 ipAddress: "10.20.19.105", 2068 mode: "static", 2069 }, 2070 }, 2071 parents: []string{}, 2072 }, 2073 // This interface is linked to the wrong subnet 2074 &fakeInterface{ 2075 id: 94, 2076 name: "eth1", 2077 type_: "physical", 2078 enabled: true, 2079 macAddress: "53:54:00:70:88:bb", 2080 vlan: vlan1, 2081 links: []gomaasapi.Link{ 2082 &fakeLink{ 2083 id: 481, 2084 subnet: &subnet3, 2085 ipAddress: "192.168.2.100", 2086 mode: "static", 2087 }, 2088 }, 2089 parents: []string{}, 2090 }, 2091 } 2092 goodSecondInterface := &fakeInterface{ 2093 id: 94, 2094 name: "eth1", 2095 type_: "physical", 2096 enabled: true, 2097 macAddress: "53:54:00:70:88:bb", 2098 vlan: vlan2, 2099 links: []gomaasapi.Link{ 2100 &fakeLink{ 2101 id: 481, 2102 subnet: &subnet2, 2103 ipAddress: "192.168.1.101", 2104 mode: "static", 2105 }, 2106 }, 2107 parents: []string{}, 2108 } 2109 goodDeviceInterfaces := []gomaasapi.Interface{ 2110 badDeviceInterfaces[0], 2111 } 2112 var env *maasEnviron 2113 stub := &testing.Stub{} 2114 badDevice := &fakeDevice{ 2115 Stub: stub, 2116 interfaceSet: badDeviceInterfaces, 2117 systemID: "foo", 2118 } 2119 goodDevice := &fakeDevice{ 2120 Stub: stub, 2121 interfaceSet: goodDeviceInterfaces, 2122 systemID: "foo", 2123 interface_: goodSecondInterface, 2124 } 2125 machine := &fakeMachine{ 2126 Stub: stub, 2127 systemID: "1", 2128 architecture: arch.HostArch(), 2129 interfaceSet: interfaces, 2130 createDevice: goodDevice, 2131 // Devices will first list the bad device, and then 2132 // createDevice will create the right one 2133 devices: []gomaasapi.Device{badDevice}, 2134 } 2135 badDevice.deleteCB = func() { machine.devices = machine.devices[:0] } 2136 controller := &fakeController{ 2137 Stub: stub, 2138 machines: []gomaasapi.Machine{machine}, 2139 spaces: []gomaasapi.Space{ 2140 fakeSpace{ 2141 name: "space-1", 2142 id: 4567, 2143 subnets: []gomaasapi.Subnet{subnet1}, 2144 }, 2145 }, 2146 devices: []gomaasapi.Device{goodDevice}, 2147 } 2148 suite.injectController(controller) 2149 suite.setupFakeTools(c) 2150 env = suite.makeEnviron(c, nil) 2151 2152 prepared := []network.InterfaceInfo{{ 2153 MACAddress: "53:54:00:70:88:aa", 2154 CIDR: "10.20.19.0/24", 2155 InterfaceName: "eth0", 2156 }, { 2157 MACAddress: "53:54:00:70:88:bb", 2158 CIDR: "192.168.1.0/24", 2159 InterfaceName: "eth1", 2160 }} 2161 containerTag := names.NewMachineTag("1/lxd/0") 2162 result, err := env.AllocateContainerAddresses(suite.callCtx, instance.Id("1"), containerTag, prepared) 2163 c.Assert(err, jc.ErrorIsNil) 2164 expected := []network.InterfaceInfo{{ 2165 DeviceIndex: 0, 2166 MACAddress: "53:54:00:70:88:aa", 2167 CIDR: "10.20.19.0/24", 2168 ProviderId: "93", 2169 ProviderSubnetId: "3", 2170 VLANTag: 0, 2171 ProviderVLANId: "5001", 2172 ProviderAddressId: "480", 2173 InterfaceName: "eth0", 2174 InterfaceType: "ethernet", 2175 ConfigType: "static", 2176 Address: network.NewAddressOnSpace("freckles", "10.20.19.105"), 2177 DNSServers: network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"), 2178 MTU: 1500, 2179 GatewayAddress: network.NewAddressOnSpace("freckles", "10.20.19.2"), 2180 Routes: []network.Route{}, 2181 }, { 2182 DeviceIndex: 1, 2183 MACAddress: "53:54:00:70:88:bb", 2184 CIDR: "192.168.1.0/24", 2185 ProviderId: "94", 2186 ProviderSubnetId: "4", 2187 VLANTag: 0, 2188 ProviderVLANId: "5002", 2189 ProviderAddressId: "481", 2190 InterfaceName: "eth1", 2191 InterfaceType: "ethernet", 2192 ConfigType: "static", 2193 Address: network.NewAddressOnSpace("freckles", "192.168.1.101"), 2194 DNSServers: network.NewAddressesOnSpace("freckles", "192.168.1.2"), 2195 MTU: 1500, 2196 GatewayAddress: network.NewAddressOnSpace("freckles", "192.168.1.1"), 2197 Routes: []network.Route{}, 2198 }} 2199 c.Assert(result, jc.DeepEquals, expected) 2200 } 2201 2202 func (suite *maas2EnvironSuite) TestStartInstanceEndToEnd(c *gc.C) { 2203 suite.setupFakeTools(c) 2204 machine := newFakeMachine("gus", arch.HostArch(), "Deployed") 2205 file := &fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"} 2206 controller := newFakeControllerWithFiles(file) 2207 controller.machines = []gomaasapi.Machine{machine} 2208 controller.allocateMachine = machine 2209 controller.allocateMachineMatches = gomaasapi.ConstraintMatches{ 2210 Storage: make(map[string][]gomaasapi.StorageDevice), 2211 } 2212 2213 env := suite.makeEnviron(c, controller) 2214 err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, 2215 suite.callCtx, bootstrap.BootstrapParams{ 2216 ControllerConfig: coretesting.FakeControllerConfig(), 2217 AdminSecret: jujutesting.AdminSecret, 2218 CAPrivateKey: coretesting.CAKey, 2219 }) 2220 c.Assert(err, jc.ErrorIsNil) 2221 2222 machine.Stub.CheckCallNames(c, "Start", "SetOwnerData") 2223 ownerData, ok := machine.Stub.Calls()[1].Args[0].(map[string]string) 2224 c.Assert(ok, jc.IsTrue) 2225 c.Assert(ownerData, gc.DeepEquals, map[string]string{ 2226 "claude": "rains", 2227 tags.JujuController: suite.controllerUUID, 2228 tags.JujuIsController: "true", 2229 tags.JujuModel: env.Config().UUID(), 2230 }) 2231 2232 // Test the instance id is correctly recorded for the bootstrap node. 2233 // Check that ControllerInstances returns the id of the bootstrap machine. 2234 instanceIds, err := env.ControllerInstances(suite.callCtx, suite.controllerUUID) 2235 c.Assert(err, jc.ErrorIsNil) 2236 c.Assert(instanceIds, gc.HasLen, 1) 2237 insts, err := env.AllInstances(suite.callCtx) 2238 c.Assert(err, jc.ErrorIsNil) 2239 c.Assert(insts, gc.HasLen, 1) 2240 c.Check(insts[0].Id(), gc.Equals, instanceIds[0]) 2241 2242 node1 := newFakeMachine("victor", arch.HostArch(), "Deployed") 2243 node1.hostname = "host1" 2244 node1.cpuCount = 1 2245 node1.memory = 1024 2246 node1.zoneName = "test_zone" 2247 controller.allocateMachine = node1 2248 2249 instance, hc := jujutesting.AssertStartInstance(c, env, suite.callCtx, suite.controllerUUID, "1") 2250 c.Check(instance, gc.NotNil) 2251 c.Assert(hc, gc.NotNil) 2252 c.Check(hc.String(), gc.Equals, fmt.Sprintf("arch=%s cores=1 mem=1024M availability-zone=test_zone", arch.HostArch())) 2253 2254 node1.Stub.CheckCallNames(c, "Start", "SetOwnerData") 2255 startArgs, ok := node1.Stub.Calls()[0].Args[0].(gomaasapi.StartArgs) 2256 c.Assert(ok, jc.IsTrue) 2257 2258 decodedUserData, err := decodeUserData(startArgs.UserData) 2259 c.Assert(err, jc.ErrorIsNil) 2260 info := machineInfo{"host1"} 2261 cloudcfg, err := cloudinit.New("precise") 2262 c.Assert(err, jc.ErrorIsNil) 2263 cloudinitRunCmd, err := info.cloudinitRunCmd(cloudcfg) 2264 c.Assert(err, jc.ErrorIsNil) 2265 data, err := goyaml.Marshal(cloudinitRunCmd) 2266 c.Assert(err, jc.ErrorIsNil) 2267 c.Check(string(decodedUserData), jc.Contains, string(data)) 2268 2269 // Trash the tools and try to start another instance. 2270 suite.PatchValue(&envtools.DefaultBaseURL, "") 2271 instance, _, _, err = jujutesting.StartInstance(env, suite.callCtx, suite.controllerUUID, "2") 2272 c.Check(instance, gc.IsNil) 2273 c.Check(err, jc.Satisfies, errors.IsNotFound) 2274 } 2275 2276 func (suite *maas2EnvironSuite) TestControllerInstances(c *gc.C) { 2277 controller := newFakeControllerWithErrors(gomaasapi.NewNoMatchError("state")) 2278 env := suite.makeEnviron(c, controller) 2279 _, err := env.ControllerInstances(suite.callCtx, suite.controllerUUID) 2280 c.Assert(err, gc.Equals, environs.ErrNotBootstrapped) 2281 2282 controller.machinesArgsCheck = func(args gomaasapi.MachinesArgs) { 2283 c.Assert(args, gc.DeepEquals, gomaasapi.MachinesArgs{ 2284 OwnerData: map[string]string{ 2285 tags.JujuIsController: "true", 2286 tags.JujuController: suite.controllerUUID, 2287 }, 2288 }) 2289 } 2290 2291 tests := [][]instance.Id{{"inst-0"}, {"inst-0", "inst-1"}} 2292 for _, expected := range tests { 2293 controller.machines = make([]gomaasapi.Machine, len(expected)) 2294 for i := range expected { 2295 controller.machines[i] = newFakeMachine(string(expected[i]), "", "") 2296 } 2297 controllerInstances, err := env.ControllerInstances(suite.callCtx, suite.controllerUUID) 2298 c.Assert(err, jc.ErrorIsNil) 2299 c.Assert(controllerInstances, jc.SameContents, expected) 2300 } 2301 } 2302 2303 func (suite *maas2EnvironSuite) TestControllerInstancesInvalidCredential(c *gc.C) { 2304 controller := &fakeController{ 2305 machinesError: gomaasapi.NewPermissionError("fail auth here"), 2306 } 2307 env := suite.makeEnviron(c, controller) 2308 2309 c.Assert(suite.invalidCredential, jc.IsFalse) 2310 _, err := env.ControllerInstances(suite.callCtx, suite.controllerUUID) 2311 c.Assert(err, gc.NotNil) 2312 c.Assert(suite.invalidCredential, jc.IsTrue) 2313 } 2314 2315 func (suite *maas2EnvironSuite) TestDestroy(c *gc.C) { 2316 file1 := &fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"} 2317 file2 := &fakeFile{name: coretesting.ModelTag.Id() + "-horace"} 2318 controller := newFakeControllerWithFiles(file1, file2) 2319 controller.machines = []gomaasapi.Machine{&fakeMachine{systemID: "pete"}} 2320 env := suite.makeEnviron(c, controller) 2321 err := env.Destroy(suite.callCtx) 2322 c.Check(err, jc.ErrorIsNil) 2323 2324 controller.Stub.CheckCallNames(c, "ReleaseMachines", "GetFile", "Files", "GetFile", "GetFile") 2325 // Instances have been stopped. 2326 controller.Stub.CheckCall(c, 0, "ReleaseMachines", gomaasapi.ReleaseMachinesArgs{ 2327 SystemIDs: []string{"pete"}, 2328 Comment: "Released by Juju MAAS provider", 2329 }) 2330 2331 // Files have been cleaned up. 2332 c.Check(file1.deleted, jc.IsTrue) 2333 c.Check(file2.deleted, jc.IsTrue) 2334 } 2335 2336 func (suite *maas2EnvironSuite) TestBootstrapFailsIfNoTools(c *gc.C) { 2337 env := suite.makeEnviron(c, newFakeController()) 2338 vers := version.MustParse("1.2.3") 2339 err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, 2340 suite.callCtx, bootstrap.BootstrapParams{ 2341 ControllerConfig: coretesting.FakeControllerConfig(), 2342 AdminSecret: jujutesting.AdminSecret, 2343 CAPrivateKey: coretesting.CAKey, 2344 // Disable auto-uploading by setting the agent version 2345 // to something that's not the current version. 2346 AgentVersion: &vers, 2347 }) 2348 c.Check(err, gc.ErrorMatches, "Juju cannot bootstrap because no agent binaries are available for your model(.|\n)*") 2349 } 2350 2351 func (suite *maas2EnvironSuite) TestBootstrapFailsIfNoNodes(c *gc.C) { 2352 suite.setupFakeTools(c) 2353 controller := newFakeController() 2354 controller.allocateMachineError = gomaasapi.NewNoMatchError("oops") 2355 env := suite.makeEnviron(c, controller) 2356 err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, 2357 suite.callCtx, bootstrap.BootstrapParams{ 2358 ControllerConfig: coretesting.FakeControllerConfig(), 2359 AdminSecret: jujutesting.AdminSecret, 2360 CAPrivateKey: coretesting.CAKey, 2361 }) 2362 // Since there are no nodes, the attempt to allocate one returns a 2363 // 409: Conflict. 2364 c.Check(err, gc.ErrorMatches, "cannot start bootstrap instance in any availability zone \\(mossack, fonseca\\)") 2365 } 2366 2367 func (suite *maas2EnvironSuite) TestGetToolsMetadataSources(c *gc.C) { 2368 // Add a dummy file to storage so we can use that to check the 2369 // obtained source later. 2370 env := suite.makeEnviron(c, newFakeControllerWithFiles( 2371 &fakeFile{name: coretesting.ModelTag.Id() + "-tools/filename", contents: makeRandomBytes(10)}, 2372 )) 2373 sources, err := envtools.GetMetadataSources(env) 2374 c.Assert(err, jc.ErrorIsNil) 2375 c.Assert(sources, gc.HasLen, 0) 2376 } 2377 2378 func (suite *maas2EnvironSuite) TestConstraintsValidator(c *gc.C) { 2379 controller := newFakeController() 2380 controller.bootResources = []gomaasapi.BootResource{&fakeBootResource{name: "trusty", architecture: "amd64"}} 2381 env := suite.makeEnviron(c, controller) 2382 validator, err := env.ConstraintsValidator(suite.callCtx) 2383 c.Assert(err, jc.ErrorIsNil) 2384 cons := constraints.MustParse("arch=amd64 cpu-power=10 instance-type=foo virt-type=kvm") 2385 unsupported, err := validator.Validate(cons) 2386 c.Assert(err, jc.ErrorIsNil) 2387 c.Assert(unsupported, jc.SameContents, []string{"cpu-power", "instance-type", "virt-type"}) 2388 } 2389 2390 func (suite *maas2EnvironSuite) TestConstraintsValidatorInvalidCredential(c *gc.C) { 2391 controller := &fakeController{ 2392 bootResources: []gomaasapi.BootResource{&fakeBootResource{name: "trusty", architecture: "amd64"}}, 2393 bootResourcesError: gomaasapi.NewPermissionError("fail auth here"), 2394 } 2395 env := suite.makeEnviron(c, controller) 2396 c.Assert(suite.invalidCredential, jc.IsFalse) 2397 _, err := env.ConstraintsValidator(suite.callCtx) 2398 c.Assert(err, gc.NotNil) 2399 c.Assert(suite.invalidCredential, jc.IsTrue) 2400 } 2401 2402 func (suite *maas2EnvironSuite) TestDomainsInvalidCredential(c *gc.C) { 2403 controller := &fakeController{ 2404 domainsError: gomaasapi.NewPermissionError("fail auth here"), 2405 } 2406 env := suite.makeEnviron(c, controller) 2407 c.Assert(suite.invalidCredential, jc.IsFalse) 2408 _, err := env.Domains(suite.callCtx) 2409 c.Assert(err, gc.NotNil) 2410 c.Assert(suite.invalidCredential, jc.IsTrue) 2411 } 2412 2413 func (suite *maas2EnvironSuite) TestConstraintsValidatorVocab(c *gc.C) { 2414 controller := newFakeController() 2415 controller.bootResources = []gomaasapi.BootResource{ 2416 &fakeBootResource{name: "trusty", architecture: "amd64"}, 2417 &fakeBootResource{name: "precise", architecture: "armhf"}, 2418 } 2419 env := suite.makeEnviron(c, controller) 2420 validator, err := env.ConstraintsValidator(suite.callCtx) 2421 c.Assert(err, jc.ErrorIsNil) 2422 cons := constraints.MustParse("arch=ppc64el") 2423 _, err = validator.Validate(cons) 2424 c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=ppc64el\nvalid values are: \\[amd64 armhf\\]") 2425 } 2426 2427 func (suite *maas2EnvironSuite) TestReleaseContainerAddresses(c *gc.C) { 2428 dev1 := newFakeDevice("a", "eleven") 2429 dev2 := newFakeDevice("b", "will") 2430 controller := newFakeController() 2431 controller.devices = []gomaasapi.Device{dev1, dev2} 2432 2433 env := suite.makeEnviron(c, controller) 2434 err := env.ReleaseContainerAddresses(suite.callCtx, []network.ProviderInterfaceInfo{ 2435 {MACAddress: "will"}, 2436 {MACAddress: "dustin"}, 2437 {MACAddress: "eleven"}, 2438 }) 2439 c.Assert(err, jc.ErrorIsNil) 2440 2441 args, ok := getArgs(c, controller.Calls(), 0, 0).(gomaasapi.DevicesArgs) 2442 c.Assert(ok, jc.IsTrue) 2443 expected := gomaasapi.DevicesArgs{MACAddresses: []string{"will", "dustin", "eleven"}} 2444 c.Assert(args, gc.DeepEquals, expected) 2445 2446 dev1.CheckCallNames(c, "Delete") 2447 dev2.CheckCallNames(c, "Delete") 2448 } 2449 2450 func (suite *maas2EnvironSuite) TestReleaseContainerAddresses_HandlesDupes(c *gc.C) { 2451 dev1 := newFakeDevice("a", "eleven") 2452 controller := newFakeController() 2453 controller.devices = []gomaasapi.Device{dev1, dev1} 2454 2455 env := suite.makeEnviron(c, controller) 2456 err := env.ReleaseContainerAddresses(suite.callCtx, []network.ProviderInterfaceInfo{ 2457 {MACAddress: "will"}, 2458 {MACAddress: "eleven"}, 2459 }) 2460 c.Assert(err, jc.ErrorIsNil) 2461 2462 args, ok := getArgs(c, controller.Calls(), 0, 0).(gomaasapi.DevicesArgs) 2463 c.Assert(ok, jc.IsTrue) 2464 expected := gomaasapi.DevicesArgs{MACAddresses: []string{"will", "eleven"}} 2465 c.Assert(args, gc.DeepEquals, expected) 2466 2467 dev1.CheckCallNames(c, "Delete") 2468 } 2469 2470 func (suite *maas2EnvironSuite) TestReleaseContainerAddressesErrorGettingDevices(c *gc.C) { 2471 controller := newFakeControllerWithErrors(errors.New("Everything done broke")) 2472 env := suite.makeEnviron(c, controller) 2473 err := env.ReleaseContainerAddresses(suite.callCtx, []network.ProviderInterfaceInfo{{MACAddress: "anything"}}) 2474 c.Assert(err, gc.ErrorMatches, "Everything done broke") 2475 } 2476 2477 func (suite *maas2EnvironSuite) TestReleaseContainerAddressesErrorDeletingDevice(c *gc.C) { 2478 dev1 := newFakeDevice("a", "eleven") 2479 dev1.systemID = "hopper" 2480 dev1.SetErrors(errors.New("don't delete me")) 2481 controller := newFakeController() 2482 controller.devices = []gomaasapi.Device{dev1} 2483 2484 env := suite.makeEnviron(c, controller) 2485 err := env.ReleaseContainerAddresses(suite.callCtx, []network.ProviderInterfaceInfo{ 2486 {MACAddress: "eleven"}, 2487 }) 2488 c.Assert(err, gc.ErrorMatches, "deleting device hopper: don't delete me") 2489 2490 _, ok := getArgs(c, controller.Calls(), 0, 0).(gomaasapi.DevicesArgs) 2491 c.Assert(ok, jc.IsTrue) 2492 2493 dev1.CheckCallNames(c, "Delete") 2494 } 2495 2496 func (suite *maas2EnvironSuite) TestAdoptResources(c *gc.C) { 2497 machine1 := newFakeMachine("big-fig-wasp", "gaudi", "good") 2498 machine2 := newFakeMachine("robot-stop", "hundertwasser", "fine") 2499 machine3 := newFakeMachine("gamma-knife", "von-neumann", "acceptable") 2500 controller := newFakeController() 2501 controller.machines = append(controller.machines, machine1, machine3) 2502 env := suite.makeEnviron(c, controller) 2503 2504 err := env.AdoptResources(suite.callCtx, "some-other-controller", version.MustParse("1.2.3")) 2505 c.Assert(err, jc.ErrorIsNil) 2506 2507 machine1.CheckCallNames(c, "SetOwnerData") 2508 c.Assert(machine1.Calls()[0].Args[0], gc.DeepEquals, map[string]string{ 2509 tags.JujuController: "some-other-controller", 2510 }) 2511 machine2.CheckCallNames(c) 2512 machine3.CheckCallNames(c, "SetOwnerData") 2513 c.Assert(machine3.Calls()[0].Args[0], gc.DeepEquals, map[string]string{ 2514 tags.JujuController: "some-other-controller", 2515 }) 2516 } 2517 2518 func (suite *maas2EnvironSuite) TestAdoptResourcesError(c *gc.C) { 2519 machine1 := newFakeMachine("evil-death-roll", "frank-lloyd-wright", "ok") 2520 machine2 := newFakeMachine("people-vultures", "gehry", "adequate") 2521 controller := newFakeController() 2522 controller.machines = append(controller.machines, machine1, machine2) 2523 env := suite.makeEnviron(c, controller) 2524 2525 machine1.SetErrors(errors.New("blorp")) 2526 2527 err := env.AdoptResources(suite.callCtx, "some-other-controller", version.MustParse("3.2.1")) 2528 c.Assert(err, gc.ErrorMatches, `failed to update controller for some instances: \[evil-death-roll\]`) 2529 2530 machine1.CheckCallNames(c, "SetOwnerData") 2531 c.Assert(machine1.Calls()[0].Args[0], gc.DeepEquals, map[string]string{ 2532 tags.JujuController: "some-other-controller", 2533 }) 2534 machine2.CheckCallNames(c, "SetOwnerData") 2535 c.Assert(machine2.Calls()[0].Args[0], gc.DeepEquals, map[string]string{ 2536 tags.JujuController: "some-other-controller", 2537 }) 2538 } 2539 2540 func newFakeDevice(systemID, macAddress string) *fakeDevice { 2541 return &fakeDevice{ 2542 Stub: &testing.Stub{}, 2543 systemID: systemID, 2544 interface_: &fakeInterface{ 2545 Stub: &testing.Stub{}, 2546 macAddress: macAddress, 2547 }, 2548 } 2549 }