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 }