github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/storage/base_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package storage_test
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names/v5"
     9  	"github.com/juju/testing"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/apiserver/common"
    14  	"github.com/juju/juju/apiserver/facades/client/storage"
    15  	apiservertesting "github.com/juju/juju/apiserver/testing"
    16  	"github.com/juju/juju/environs/context"
    17  	"github.com/juju/juju/rpc/params"
    18  	"github.com/juju/juju/state"
    19  	jujustorage "github.com/juju/juju/storage"
    20  	"github.com/juju/juju/storage/poolmanager"
    21  	coretesting "github.com/juju/juju/testing"
    22  )
    23  
    24  type baseStorageSuite struct {
    25  	coretesting.BaseSuite
    26  
    27  	resources  *common.Resources
    28  	authorizer apiservertesting.FakeAuthorizer
    29  
    30  	api             *storage.StorageAPI
    31  	apiCaas         *storage.StorageAPI
    32  	storageAccessor *mockStorageAccessor
    33  	state           *mockState
    34  
    35  	storageTag      names.StorageTag
    36  	storageInstance *mockStorageInstance
    37  	unitTag         names.UnitTag
    38  	machineTag      names.MachineTag
    39  
    40  	volumeTag            names.VolumeTag
    41  	volume               *mockVolume
    42  	volumeAttachment     *mockVolumeAttachment
    43  	volumeAttachmentPlan *mockVolumeAttachmentPlan
    44  	filesystemTag        names.FilesystemTag
    45  	filesystem           *mockFilesystem
    46  	filesystemAttachment *mockFilesystemAttachment
    47  	stub                 testing.Stub
    48  
    49  	registry    jujustorage.StaticProviderRegistry
    50  	poolManager *mockPoolManager
    51  	pools       map[string]*jujustorage.Config
    52  	poolsInUse  []string
    53  
    54  	blocks      map[state.BlockType]state.Block
    55  	callContext context.ProviderCallContext
    56  }
    57  
    58  func (s *baseStorageSuite) SetUpTest(c *gc.C) {
    59  	s.BaseSuite.SetUpTest(c)
    60  	s.resources = common.NewResources()
    61  	s.authorizer = apiservertesting.FakeAuthorizer{Tag: names.NewUserTag("admin"), Controller: true}
    62  	s.stub.ResetCalls()
    63  	s.state = s.constructState()
    64  	s.storageAccessor = s.constructStorageAccessor()
    65  
    66  	s.registry = jujustorage.StaticProviderRegistry{map[jujustorage.ProviderType]jujustorage.Provider{}}
    67  	s.pools = make(map[string]*jujustorage.Config)
    68  	s.poolManager = s.constructPoolManager()
    69  	s.poolsInUse = []string{}
    70  
    71  	s.callContext = context.NewEmptyCloudCallContext()
    72  	s.api = storage.NewStorageAPIForTest(s.state, state.ModelTypeIAAS, s.storageAccessor, s.storageMetadata, s.authorizer, s.callContext)
    73  	s.apiCaas = storage.NewStorageAPIForTest(s.state, state.ModelTypeCAAS, s.storageAccessor, s.storageMetadata, s.authorizer, s.callContext)
    74  }
    75  
    76  func (s *baseStorageSuite) storageMetadata() (poolmanager.PoolManager, jujustorage.ProviderRegistry, error) {
    77  	return s.poolManager, s.registry, nil
    78  }
    79  
    80  // TODO(axw) get rid of assertCalls, use stub directly everywhere.
    81  func (s *baseStorageSuite) assertCalls(c *gc.C, expectedCalls []string) {
    82  	s.stub.CheckCallNames(c, expectedCalls...)
    83  }
    84  
    85  const (
    86  	allStorageInstancesCall                 = "allStorageInstances"
    87  	removeStoragePoolCall                   = "removeStoragePool"
    88  	storageInstanceAttachmentsCall          = "storageInstanceAttachments"
    89  	storageInstanceCall                     = "StorageInstance"
    90  	storageInstanceFilesystemCall           = "StorageInstanceFilesystem"
    91  	storageInstanceFilesystemAttachmentCall = "storageInstanceFilesystemAttachment"
    92  	storageInstanceVolumeCall               = "storageInstanceVolume"
    93  	volumeCall                              = "volumeCall"
    94  	machineVolumeAttachmentsCall            = "machineVolumeAttachments"
    95  	volumeAttachmentsCall                   = "volumeAttachments"
    96  	allVolumesCall                          = "allVolumes"
    97  	filesystemCall                          = "filesystemCall"
    98  	machineFilesystemAttachmentsCall        = "machineFilesystemAttachments"
    99  	filesystemAttachmentsCall               = "filesystemAttachments"
   100  	allFilesystemsCall                      = "allFilesystems"
   101  	addStorageForUnitCall                   = "addStorageForUnit"
   102  	getBlockForTypeCall                     = "getBlockForType"
   103  	volumeAttachmentCall                    = "volumeAttachment"
   104  	volumeAttachmentPlanCall                = "volumeAttachmentPlan"
   105  	volumeAttachmentPlansCall               = "volumeAttachmentPlans"
   106  	attachStorageCall                       = "attachStorage"
   107  	detachStorageCall                       = "detachStorage"
   108  	destroyStorageInstanceCall              = "destroyStorageInstance"
   109  	releaseStorageInstanceCall              = "releaseStorageInstance"
   110  	addExistingFilesystemCall               = "addExistingFilesystem"
   111  )
   112  
   113  func (s *baseStorageSuite) constructState() *mockState {
   114  	s.unitTag = names.NewUnitTag("mysql/0")
   115  	s.blocks = make(map[state.BlockType]state.Block)
   116  	return &mockState{
   117  		unitName:        s.unitTag.Id(),
   118  		assignedMachine: s.machineTag.Id(),
   119  		getBlockForType: func(t state.BlockType) (state.Block, bool, error) {
   120  			s.stub.AddCall(getBlockForTypeCall, t)
   121  			val, found := s.blocks[t]
   122  			return val, found, nil
   123  		},
   124  	}
   125  }
   126  
   127  func (s *baseStorageSuite) constructStorageAccessor() *mockStorageAccessor {
   128  	s.storageTag = names.NewStorageTag("data/0")
   129  
   130  	s.storageInstance = &mockStorageInstance{
   131  		kind:       state.StorageKindFilesystem,
   132  		owner:      s.unitTag,
   133  		storageTag: s.storageTag,
   134  		life:       state.Dying,
   135  	}
   136  
   137  	storageInstanceAttachment := &mockStorageAttachment{
   138  		storage: s.storageInstance,
   139  		life:    state.Alive,
   140  	}
   141  
   142  	s.machineTag = names.NewMachineTag("66")
   143  	s.filesystemTag = names.NewFilesystemTag("104")
   144  	s.volumeTag = names.NewVolumeTag("22")
   145  	s.filesystem = &mockFilesystem{
   146  		tag:     s.filesystemTag,
   147  		storage: &s.storageTag,
   148  		life:    state.Alive,
   149  	}
   150  	s.filesystemAttachment = &mockFilesystemAttachment{
   151  		filesystem: s.filesystemTag,
   152  		machine:    s.machineTag,
   153  		life:       state.Dead,
   154  	}
   155  	s.volume = &mockVolume{tag: s.volumeTag, storage: &s.storageTag}
   156  	s.volumeAttachment = &mockVolumeAttachment{
   157  		VolumeTag: s.volumeTag,
   158  		HostTag:   s.machineTag,
   159  		life:      state.Alive,
   160  	}
   161  
   162  	s.volumeAttachmentPlan = &mockVolumeAttachmentPlan{
   163  		VolumeTag: s.volumeTag,
   164  		HostTag:   s.machineTag,
   165  		life:      state.Alive,
   166  		info:      &state.VolumeAttachmentPlanInfo{},
   167  		blk:       &state.BlockDeviceInfo{},
   168  	}
   169  
   170  	return &mockStorageAccessor{
   171  		allStorageInstances: func() ([]state.StorageInstance, error) {
   172  			s.stub.AddCall(allStorageInstancesCall)
   173  			return []state.StorageInstance{s.storageInstance}, nil
   174  		},
   175  		removeStoragePool: func(poolName string) error {
   176  			s.stub.AddCall(removeStoragePoolCall)
   177  			for _, p := range s.poolsInUse {
   178  				if p == poolName {
   179  					return errors.Errorf("storage pool %q in use", poolName)
   180  				}
   181  			}
   182  			return s.poolManager.Delete(poolName)
   183  		},
   184  		storageInstance: func(sTag names.StorageTag) (state.StorageInstance, error) {
   185  			s.stub.AddCall(storageInstanceCall, sTag)
   186  			if sTag == s.storageTag {
   187  				return s.storageInstance, nil
   188  			}
   189  			return nil, errors.NotFoundf("%s", names.ReadableString(sTag))
   190  		},
   191  		storageInstanceAttachments: func(tag names.StorageTag) ([]state.StorageAttachment, error) {
   192  			s.stub.AddCall(storageInstanceAttachmentsCall, tag)
   193  			if tag == s.storageTag {
   194  				return []state.StorageAttachment{storageInstanceAttachment}, nil
   195  			}
   196  			return []state.StorageAttachment{}, nil
   197  		},
   198  		storageInstanceFilesystem: func(sTag names.StorageTag) (state.Filesystem, error) {
   199  			s.stub.AddCall(storageInstanceFilesystemCall)
   200  			if sTag == s.storageTag {
   201  				return s.filesystem, nil
   202  			}
   203  			return nil, errors.NotFoundf("%s", names.ReadableString(sTag))
   204  		},
   205  		storageInstanceFilesystemAttachment: func(m names.Tag, f names.FilesystemTag) (state.FilesystemAttachment, error) {
   206  			s.stub.AddCall(storageInstanceFilesystemAttachmentCall)
   207  			if m == s.machineTag && f == s.filesystemTag {
   208  				return s.filesystemAttachment, nil
   209  			}
   210  			return nil, errors.NotFoundf("filesystem attachment %s:%s", m, f)
   211  		},
   212  		storageInstanceVolume: func(t names.StorageTag) (state.Volume, error) {
   213  			s.stub.AddCall(storageInstanceVolumeCall)
   214  			if t == s.storageTag {
   215  				return s.volume, nil
   216  			}
   217  			return nil, errors.NotFoundf("%s", names.ReadableString(t))
   218  		},
   219  		volumeAttachment: func(names.Tag, names.VolumeTag) (state.VolumeAttachment, error) {
   220  			s.stub.AddCall(volumeAttachmentCall)
   221  			return s.volumeAttachment, nil
   222  		},
   223  		volumeAttachmentPlan: func(names.Tag, names.VolumeTag) (state.VolumeAttachmentPlan, error) {
   224  			s.stub.AddCall(volumeAttachmentPlanCall)
   225  			return s.volumeAttachmentPlan, nil
   226  		},
   227  		volumeAttachmentPlans: func(names.VolumeTag) ([]state.VolumeAttachmentPlan, error) {
   228  			s.stub.AddCall(volumeAttachmentPlansCall)
   229  			return []state.VolumeAttachmentPlan{s.volumeAttachmentPlan}, nil
   230  		},
   231  		volume: func(tag names.VolumeTag) (state.Volume, error) {
   232  			s.stub.AddCall(volumeCall)
   233  			if tag == s.volumeTag {
   234  				return s.volume, nil
   235  			}
   236  			return nil, errors.NotFoundf("%s", names.ReadableString(tag))
   237  		},
   238  		machineVolumeAttachments: func(machine names.MachineTag) ([]state.VolumeAttachment, error) {
   239  			s.stub.AddCall(machineVolumeAttachmentsCall)
   240  			if machine == s.machineTag {
   241  				return []state.VolumeAttachment{s.volumeAttachment}, nil
   242  			}
   243  			return nil, nil
   244  		},
   245  		volumeAttachments: func(volume names.VolumeTag) ([]state.VolumeAttachment, error) {
   246  			s.stub.AddCall(volumeAttachmentsCall)
   247  			if volume == s.volumeTag {
   248  				return []state.VolumeAttachment{s.volumeAttachment}, nil
   249  			}
   250  			return nil, nil
   251  		},
   252  		allVolumes: func() ([]state.Volume, error) {
   253  			s.stub.AddCall(allVolumesCall)
   254  			return []state.Volume{s.volume}, nil
   255  		},
   256  		filesystem: func(tag names.FilesystemTag) (state.Filesystem, error) {
   257  			s.stub.AddCall(filesystemCall)
   258  			if tag == s.filesystemTag {
   259  				return s.filesystem, nil
   260  			}
   261  			return nil, errors.NotFoundf("%s", names.ReadableString(tag))
   262  		},
   263  		machineFilesystemAttachments: func(machine names.MachineTag) ([]state.FilesystemAttachment, error) {
   264  			s.stub.AddCall(machineFilesystemAttachmentsCall)
   265  			if machine == s.machineTag {
   266  				return []state.FilesystemAttachment{s.filesystemAttachment}, nil
   267  			}
   268  			return nil, nil
   269  		},
   270  		filesystemAttachments: func(filesystem names.FilesystemTag) ([]state.FilesystemAttachment, error) {
   271  			s.stub.AddCall(filesystemAttachmentsCall)
   272  			if filesystem == s.filesystemTag {
   273  				return []state.FilesystemAttachment{s.filesystemAttachment}, nil
   274  			}
   275  			return nil, nil
   276  		},
   277  		allFilesystems: func() ([]state.Filesystem, error) {
   278  			s.stub.AddCall(allFilesystemsCall)
   279  			return []state.Filesystem{s.filesystem}, nil
   280  		},
   281  		addStorageForUnit: func(u names.UnitTag, name string, cons state.StorageConstraints) ([]names.StorageTag, error) {
   282  			s.stub.AddCall(addStorageForUnitCall)
   283  			return nil, nil
   284  		},
   285  		detachStorage: func(storage names.StorageTag, unit names.UnitTag, force bool) error {
   286  			s.stub.AddCall(detachStorageCall, storage, unit, force)
   287  			if storage == s.storageTag && unit == s.unitTag {
   288  				return nil
   289  			}
   290  			return errors.NotFoundf(
   291  				"attachment of %s to %s",
   292  				names.ReadableString(storage),
   293  				names.ReadableString(unit),
   294  			)
   295  		},
   296  		attachStorage: func(storage names.StorageTag, unit names.UnitTag) error {
   297  			s.stub.AddCall(attachStorageCall, storage, unit)
   298  			if storage == s.storageTag && unit == s.unitTag {
   299  				return nil
   300  			}
   301  			return errors.Errorf(
   302  				"cannot attach %s to %s",
   303  				names.ReadableString(storage),
   304  				names.ReadableString(unit),
   305  			)
   306  		},
   307  		destroyStorageInstance: func(tag names.StorageTag, destroyAttached bool, force bool) error {
   308  			s.stub.AddCall(destroyStorageInstanceCall, tag, destroyAttached, force)
   309  			return errors.New("cannae do it")
   310  		},
   311  		releaseStorageInstance: func(tag names.StorageTag, destroyAttached bool, force bool) error {
   312  			s.stub.AddCall(releaseStorageInstanceCall, tag, destroyAttached, force)
   313  			return errors.New("cannae do it")
   314  		},
   315  		addExistingFilesystem: func(f state.FilesystemInfo, v *state.VolumeInfo, storageName string) (names.StorageTag, error) {
   316  			s.stub.AddCall(addExistingFilesystemCall, f, v, storageName)
   317  			return s.storageTag, s.stub.NextErr()
   318  		},
   319  	}
   320  }
   321  
   322  func (s *baseStorageSuite) addBlock(c *gc.C, t state.BlockType, msg string) {
   323  	s.blocks[t] = mockBlock{
   324  		t:   t,
   325  		msg: msg,
   326  	}
   327  }
   328  
   329  func (s *baseStorageSuite) blockAllChanges(c *gc.C, msg string) {
   330  	s.addBlock(c, state.ChangeBlock, msg)
   331  }
   332  
   333  func (s *baseStorageSuite) blockDestroyModel(c *gc.C, msg string) {
   334  	s.addBlock(c, state.DestroyBlock, msg)
   335  }
   336  
   337  func (s *baseStorageSuite) blockRemoveObject(c *gc.C, msg string) {
   338  	s.addBlock(c, state.RemoveBlock, msg)
   339  }
   340  
   341  func (s *baseStorageSuite) assertBlocked(c *gc.C, err error, msg string) {
   342  	c.Assert(params.IsCodeOperationBlocked(err), jc.IsTrue)
   343  	c.Assert(err, gc.ErrorMatches, msg)
   344  }
   345  
   346  func (s *baseStorageSuite) constructPoolManager() *mockPoolManager {
   347  	return &mockPoolManager{
   348  		getPool: func(name string) (*jujustorage.Config, error) {
   349  			if one, ok := s.pools[name]; ok {
   350  				return one, nil
   351  			}
   352  			return nil, errors.NotFoundf("mock pool manager: get pool %v", name)
   353  		},
   354  		createPool: func(name string, providerType jujustorage.ProviderType, attrs map[string]interface{}) (*jujustorage.Config, error) {
   355  			pool, err := jujustorage.NewConfig(name, providerType, attrs)
   356  			s.pools[name] = pool
   357  			return pool, err
   358  		},
   359  		removePool: func(name string) error {
   360  			delete(s.pools, name)
   361  			return nil
   362  		},
   363  		listPools: func() ([]*jujustorage.Config, error) {
   364  			result := make([]*jujustorage.Config, len(s.pools))
   365  			i := 0
   366  			for _, v := range s.pools {
   367  				result[i] = v
   368  				i++
   369  			}
   370  			return result, nil
   371  		},
   372  		replacePool: func(name, provider string, attrs map[string]interface{}) error {
   373  			if p, ok := s.pools[name]; ok {
   374  				providerType := p.Provider()
   375  				if provider != "" {
   376  					providerType = jujustorage.ProviderType(provider)
   377  				}
   378  				newPool, err := jujustorage.NewConfig(name, providerType, attrs)
   379  				s.pools[name] = newPool
   380  				return err
   381  			}
   382  			return errors.NotFoundf("mock pool manager: get pool %v", name)
   383  		},
   384  	}
   385  }