github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/maas/maas_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 "github.com/juju/collections/set" 8 "github.com/juju/errors" 9 "github.com/juju/gomaasapi/v2" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/cloud" 15 environscloudspec "github.com/juju/juju/environs/cloudspec" 16 "github.com/juju/juju/environs/config" 17 coretesting "github.com/juju/juju/testing" 18 "github.com/juju/juju/version" 19 ) 20 21 var maasEnvAttrs = coretesting.Attrs{ 22 "name": "test-env", 23 "type": "maas", 24 config.ResourceTagsKey: map[string]string{ 25 "claude": "rains", 26 }, 27 } 28 29 type maasSuite struct { 30 baseProviderSuite 31 } 32 33 func (suite *maasSuite) injectController(controller gomaasapi.Controller) { 34 mockGetController := func(maasServer, apiKey string) (gomaasapi.Controller, error) { 35 return controller, nil 36 } 37 suite.PatchValue(&GetMAASController, mockGetController) 38 } 39 40 func (suite *maasSuite) makeEnviron(c *gc.C, controller gomaasapi.Controller) *maasEnviron { 41 if controller != nil { 42 suite.injectController(controller) 43 } 44 testAttrs := coretesting.Attrs{} 45 for k, v := range maasEnvAttrs { 46 testAttrs[k] = v 47 } 48 testAttrs["agent-version"] = version.Current.String() 49 50 cred := cloud.NewCredential(cloud.OAuth1AuthType, map[string]string{ 51 "maas-oauth": "a:b:c", 52 }) 53 cloud := environscloudspec.CloudSpec{ 54 Type: "maas", 55 Name: "maas", 56 Endpoint: "http://any-old-junk.invalid/", 57 Credential: &cred, 58 } 59 60 attrs := coretesting.FakeConfig().Merge(testAttrs) 61 suite.controllerUUID = coretesting.FakeControllerConfig().ControllerUUID() 62 cfg, err := config.New(config.NoDefaults, attrs) 63 c.Assert(err, jc.ErrorIsNil) 64 env, err := NewEnviron(cloud, cfg, nil) 65 c.Assert(err, jc.ErrorIsNil) 66 c.Assert(env, gc.NotNil) 67 return env 68 } 69 70 type fakeController struct { 71 gomaasapi.Controller 72 *testing.Stub 73 74 domains []gomaasapi.Domain 75 bootResources []gomaasapi.BootResource 76 bootResourcesError error 77 machines []gomaasapi.Machine 78 machinesError error 79 machinesArgsCheck func(gomaasapi.MachinesArgs) 80 zones []gomaasapi.Zone 81 zonesError error 82 spaces []gomaasapi.Space 83 spacesError error 84 staticRoutes []gomaasapi.StaticRoute 85 staticRoutesError error 86 87 allocateMachine gomaasapi.Machine 88 allocateMachineMatches gomaasapi.ConstraintMatches 89 allocateMachineError error 90 allocateMachineArgsCheck func(gomaasapi.AllocateMachineArgs) 91 92 files []gomaasapi.File 93 94 devices []gomaasapi.Device 95 domainsError error 96 } 97 98 func newFakeController() *fakeController { 99 return &fakeController{ 100 Stub: &testing.Stub{}, 101 zones: []gomaasapi.Zone{ 102 &fakeZone{name: "mossack"}, 103 &fakeZone{name: "fonseca"}, 104 }, 105 domains: []gomaasapi.Domain{ 106 &fakeDomain{}, 107 }, 108 } 109 } 110 111 func newFakeControllerWithErrors(errors ...error) *fakeController { 112 controller := newFakeController() 113 controller.SetErrors(errors...) 114 return controller 115 } 116 117 func newFakeControllerWithFiles(files ...gomaasapi.File) *fakeController { 118 controller := newFakeController() 119 controller.files = files 120 return controller 121 } 122 123 func (c *fakeController) Devices(args gomaasapi.DevicesArgs) ([]gomaasapi.Device, error) { 124 c.MethodCall(c, "Devices", args) 125 return c.devices, c.NextErr() 126 } 127 128 func (c *fakeController) Machines(args gomaasapi.MachinesArgs) ([]gomaasapi.Machine, error) { 129 if c.machinesArgsCheck != nil { 130 c.machinesArgsCheck(args) 131 } 132 if c.machinesError != nil { 133 return nil, c.machinesError 134 } 135 if len(args.SystemIDs) > 0 { 136 result := []gomaasapi.Machine{} 137 systemIds := set.NewStrings(args.SystemIDs...) 138 for _, machine := range c.machines { 139 if systemIds.Contains(machine.SystemID()) { 140 result = append(result, machine) 141 } 142 } 143 return result, nil 144 } 145 return c.machines, nil 146 } 147 148 func (c *fakeController) Domains() ([]gomaasapi.Domain, error) { 149 return c.domains, c.domainsError 150 } 151 152 func (c *fakeController) AllocateMachine(args gomaasapi.AllocateMachineArgs) (gomaasapi.Machine, gomaasapi.ConstraintMatches, error) { 153 if c.allocateMachineArgsCheck != nil { 154 c.allocateMachineArgsCheck(args) 155 } 156 if c.allocateMachineError != nil { 157 return nil, c.allocateMachineMatches, c.allocateMachineError 158 } 159 return c.allocateMachine, c.allocateMachineMatches, nil 160 } 161 162 func (c *fakeController) BootResources() ([]gomaasapi.BootResource, error) { 163 if c.bootResourcesError != nil { 164 return nil, c.bootResourcesError 165 } 166 return c.bootResources, nil 167 } 168 169 func (c *fakeController) Zones() ([]gomaasapi.Zone, error) { 170 if c.zonesError != nil { 171 return nil, c.zonesError 172 } 173 return c.zones, nil 174 } 175 176 func (c *fakeController) Spaces() ([]gomaasapi.Space, error) { 177 if c.spacesError != nil { 178 return nil, c.spacesError 179 } 180 return c.spaces, nil 181 } 182 183 func (c *fakeController) StaticRoutes() ([]gomaasapi.StaticRoute, error) { 184 if c.staticRoutesError != nil { 185 return nil, c.staticRoutesError 186 } 187 return c.staticRoutes, nil 188 } 189 func (c *fakeController) Files(prefix string) ([]gomaasapi.File, error) { 190 c.MethodCall(c, "Files", prefix) 191 return c.files, c.NextErr() 192 } 193 194 func (c *fakeController) GetFile(filename string) (gomaasapi.File, error) { 195 c.MethodCall(c, "GetFile", filename) 196 err := c.NextErr() 197 if err != nil { 198 return nil, err 199 } 200 // Try to find the file by name (needed for testing RemoveAll) 201 for _, file := range c.files { 202 if file.Filename() == filename { 203 return file, nil 204 } 205 } 206 // The test forgot to set up matching files! 207 return nil, errors.Errorf("no file named %v found - did you set up your test correctly?", filename) 208 } 209 210 func (c *fakeController) AddFile(args gomaasapi.AddFileArgs) error { 211 c.MethodCall(c, "AddFile", args) 212 return c.NextErr() 213 } 214 215 func (c *fakeController) ReleaseMachines(args gomaasapi.ReleaseMachinesArgs) error { 216 c.MethodCall(c, "ReleaseMachines", args) 217 return c.NextErr() 218 } 219 220 type fakeBootResource struct { 221 gomaasapi.BootResource 222 name string 223 architecture string 224 } 225 226 func (r *fakeBootResource) Name() string { 227 return r.name 228 } 229 230 func (r *fakeBootResource) Architecture() string { 231 return r.architecture 232 } 233 234 type fakeMachine struct { 235 gomaasapi.Machine 236 *testing.Stub 237 238 zoneName string 239 hostname string 240 systemID string 241 ipAddresses []string 242 statusName string 243 statusMessage string 244 cpuCount int 245 memory int 246 architecture string 247 interfaceSet []gomaasapi.Interface 248 tags []string 249 createDevice gomaasapi.Device 250 devices []gomaasapi.Device 251 } 252 253 func newFakeMachine(systemID, architecture, statusName string) *fakeMachine { 254 return &fakeMachine{ 255 Stub: &testing.Stub{}, 256 systemID: systemID, 257 architecture: architecture, 258 statusName: statusName, 259 } 260 } 261 262 func (m *fakeMachine) Tags() []string { 263 return m.tags 264 } 265 266 func (m *fakeMachine) SetOwnerData(data map[string]string) error { 267 m.MethodCall(m, "SetOwnerData", data) 268 return m.NextErr() 269 } 270 271 func (m *fakeMachine) CPUCount() int { 272 return m.cpuCount 273 } 274 275 func (m *fakeMachine) Memory() int { 276 return m.memory 277 } 278 279 func (m *fakeMachine) Architecture() string { 280 return m.architecture 281 } 282 283 func (m *fakeMachine) SystemID() string { 284 return m.systemID 285 } 286 287 func (m *fakeMachine) Hostname() string { 288 return m.hostname 289 } 290 291 func (m *fakeMachine) FQDN() string { 292 domain := "example.com." 293 host := m.Hostname() 294 if host == "" { 295 return domain 296 } 297 return host + "." + domain 298 } 299 300 func (m *fakeMachine) IPAddresses() []string { 301 return m.ipAddresses 302 } 303 304 func (m *fakeMachine) StatusName() string { 305 return m.statusName 306 } 307 308 func (m *fakeMachine) StatusMessage() string { 309 return m.statusMessage 310 } 311 312 func (m *fakeMachine) Zone() gomaasapi.Zone { 313 return fakeZone{name: m.zoneName} 314 } 315 316 func (m *fakeMachine) InterfaceSet() []gomaasapi.Interface { 317 return m.interfaceSet 318 } 319 320 func (m *fakeMachine) Start(args gomaasapi.StartArgs) error { 321 m.MethodCall(m, "Start", args) 322 return m.NextErr() 323 } 324 325 func (m *fakeMachine) CreateDevice(args gomaasapi.CreateMachineDeviceArgs) (gomaasapi.Device, error) { 326 m.MethodCall(m, "CreateDevice", args) 327 err := m.NextErr() 328 if err != nil { 329 return nil, err 330 } 331 m.devices = append(m.devices, m.createDevice) 332 return m.createDevice, nil 333 } 334 335 func (m *fakeMachine) Devices(args gomaasapi.DevicesArgs) ([]gomaasapi.Device, error) { 336 m.MethodCall(m, "Devices", args) 337 err := m.NextErr() 338 if err != nil { 339 return nil, err 340 } 341 return m.devices, nil 342 } 343 344 type fakeZone struct { 345 gomaasapi.Zone 346 name string 347 } 348 349 func (z fakeZone) Name() string { 350 return z.name 351 } 352 353 type fakeSpace struct { 354 gomaasapi.Space 355 name string 356 id int 357 subnets []gomaasapi.Subnet 358 } 359 360 func (s fakeSpace) Name() string { 361 return s.name 362 } 363 364 func (s fakeSpace) ID() int { 365 return s.id 366 } 367 368 func (s fakeSpace) Subnets() []gomaasapi.Subnet { 369 return s.subnets 370 } 371 372 type fakeSubnet struct { 373 gomaasapi.Subnet 374 id int 375 space string 376 vlan gomaasapi.VLAN 377 gateway string 378 cidr string 379 dnsServers []string 380 } 381 382 func (s fakeSubnet) ID() int { 383 return s.id 384 } 385 386 func (s fakeSubnet) Space() string { 387 return s.space 388 } 389 390 func (s fakeSubnet) VLAN() gomaasapi.VLAN { 391 return s.vlan 392 } 393 394 func (s fakeSubnet) Gateway() string { 395 return s.gateway 396 } 397 398 func (s fakeSubnet) CIDR() string { 399 return s.cidr 400 } 401 402 func (s fakeSubnet) DNSServers() []string { 403 return s.dnsServers 404 } 405 406 type fakeStaticRoute struct { 407 id int 408 source fakeSubnet 409 destination fakeSubnet 410 gatewayIP string 411 metric int 412 } 413 414 var _ gomaasapi.StaticRoute = (*fakeStaticRoute)(nil) 415 416 func (r fakeStaticRoute) ID() int { 417 return r.id 418 } 419 420 func (r fakeStaticRoute) Source() gomaasapi.Subnet { 421 return r.source 422 } 423 424 func (r fakeStaticRoute) Destination() gomaasapi.Subnet { 425 return r.destination 426 } 427 428 func (r fakeStaticRoute) GatewayIP() string { 429 return r.gatewayIP 430 } 431 432 func (r fakeStaticRoute) Metric() int { 433 return r.metric 434 } 435 436 type fakeVLAN struct { 437 gomaasapi.VLAN 438 id int 439 vid int 440 mtu int 441 } 442 443 func (v fakeVLAN) ID() int { 444 return v.id 445 } 446 447 func (v fakeVLAN) VID() int { 448 return v.vid 449 } 450 451 func (v fakeVLAN) MTU() int { 452 return v.mtu 453 } 454 455 type fakeInterface struct { 456 *testing.Stub 457 458 id int 459 name string 460 parents []string 461 children []string 462 type_ string 463 enabled bool 464 vlan gomaasapi.VLAN 465 links []gomaasapi.Link 466 macAddress string 467 } 468 469 var _ gomaasapi.Interface = (*fakeInterface)(nil) 470 471 func (v *fakeInterface) ID() int { 472 return v.id 473 } 474 475 func (v *fakeInterface) Name() string { 476 return v.name 477 } 478 479 func (v *fakeInterface) Parents() []string { 480 return v.parents 481 } 482 483 func (v *fakeInterface) Children() []string { 484 return v.children 485 } 486 487 func (v *fakeInterface) Type() string { 488 return v.type_ 489 } 490 491 func (v *fakeInterface) EffectiveMTU() int { 492 return 1500 493 } 494 495 func (v *fakeInterface) Enabled() bool { 496 return v.enabled 497 } 498 499 func (v *fakeInterface) VLAN() gomaasapi.VLAN { 500 return v.vlan 501 } 502 503 func (v *fakeInterface) Links() []gomaasapi.Link { 504 return v.links 505 } 506 507 func (v *fakeInterface) MACAddress() string { 508 return v.macAddress 509 } 510 511 func (v *fakeInterface) LinkSubnet(args gomaasapi.LinkSubnetArgs) error { 512 v.MethodCall(v, "LinkSubnet", args) 513 return v.NextErr() 514 } 515 516 func (v *fakeInterface) Delete() error { 517 v.MethodCall(v, "Delete") 518 return v.NextErr() 519 } 520 521 func (v *fakeInterface) Tags() []string { 522 return nil 523 } 524 525 func (v *fakeInterface) UnlinkSubnet(gomaasapi.Subnet) error { 526 return errors.NotImplementedf("UnlinkSubnet") 527 } 528 529 func (v *fakeInterface) Update(gomaasapi.UpdateInterfaceArgs) error { 530 return errors.NotImplementedf("Update") 531 } 532 533 type fakeLink struct { 534 gomaasapi.Link 535 id int 536 mode string 537 subnet gomaasapi.Subnet 538 ipAddress string 539 } 540 541 func (l *fakeLink) ID() int { 542 return l.id 543 } 544 545 func (l *fakeLink) Mode() string { 546 return l.mode 547 } 548 549 func (l *fakeLink) Subnet() gomaasapi.Subnet { 550 return l.subnet 551 } 552 553 func (l *fakeLink) IPAddress() string { 554 return l.ipAddress 555 } 556 557 type fakeFile struct { 558 gomaasapi.File 559 name string 560 url string 561 contents []byte 562 deleted bool 563 error error 564 } 565 566 func (f *fakeFile) Filename() string { 567 return f.name 568 } 569 570 func (f *fakeFile) AnonymousURL() string { 571 return f.url 572 } 573 574 func (f *fakeFile) Delete() error { 575 f.deleted = true 576 return f.error 577 } 578 579 func (f *fakeFile) ReadAll() ([]byte, error) { 580 if f.error != nil { 581 return nil, f.error 582 } 583 return f.contents, nil 584 } 585 586 type fakeBlockDevice struct { 587 gomaasapi.BlockDevice 588 589 name string 590 idPath string 591 size uint64 592 } 593 594 func (bd fakeBlockDevice) Name() string { 595 return bd.name 596 } 597 598 func (bd fakeBlockDevice) IDPath() string { 599 return bd.idPath 600 } 601 602 func (bd fakeBlockDevice) Size() uint64 { 603 return bd.size 604 } 605 606 type fakePartition struct { 607 gomaasapi.Partition 608 609 name string 610 path string 611 size uint64 612 } 613 614 func (part fakePartition) Name() string { 615 return part.name 616 } 617 618 func (part fakePartition) Path() string { 619 return part.path 620 } 621 622 func (part fakePartition) Size() uint64 { 623 return part.size 624 } 625 626 type fakeDevice struct { 627 *testing.Stub 628 629 interfaceSet []gomaasapi.Interface 630 systemID string 631 interface_ gomaasapi.Interface 632 deleteCB func() 633 } 634 635 var _ gomaasapi.Device = (*fakeDevice)(nil) 636 637 func (d *fakeDevice) FQDN() string { 638 return "" 639 } 640 641 func (d *fakeDevice) Hostname() string { 642 return "" 643 } 644 645 func (d *fakeDevice) Pool() gomaasapi.Pool { 646 return nil 647 } 648 649 func (d *fakeDevice) IPAddresses() []string { 650 addrs := make([]string, 0, len(d.interfaceSet)) 651 for _, iface := range d.interfaceSet { 652 for _, link := range iface.Links() { 653 addrs = append(addrs, link.IPAddress()) 654 } 655 } 656 return addrs 657 } 658 659 func (d *fakeDevice) Owner() string { 660 return "" 661 } 662 663 func (d *fakeDevice) Parent() string { 664 return "" 665 } 666 667 func (d *fakeDevice) Zone() gomaasapi.Zone { 668 return &fakeZone{} 669 } 670 671 func (d *fakeDevice) InterfaceSet() []gomaasapi.Interface { 672 return d.interfaceSet 673 } 674 675 func (d *fakeDevice) SystemID() string { 676 return d.systemID 677 } 678 679 func (d *fakeDevice) CreateInterface(args gomaasapi.CreateInterfaceArgs) (gomaasapi.Interface, error) { 680 d.MethodCall(d, "CreateInterface", args) 681 d.interfaceSet = append(d.interfaceSet, d.interface_) 682 return d.interface_, d.NextErr() 683 } 684 685 func (d *fakeDevice) Delete() error { 686 d.MethodCall(d, "Delete") 687 if d.deleteCB != nil { 688 d.deleteCB() 689 } 690 return d.NextErr() 691 } 692 693 type fakeDomain struct{} 694 695 func (*fakeDomain) Name() string { 696 return "maas" 697 }