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  }