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