github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/storage/storage_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 "fmt" 8 9 "github.com/juju/errors" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/core/status" 17 "github.com/juju/juju/environs/context" 18 "github.com/juju/juju/state" 19 "github.com/juju/juju/storage" 20 "github.com/juju/juju/storage/provider/dummy" 21 coretesting "github.com/juju/juju/testing" 22 ) 23 24 type storageSuite struct { 25 baseStorageSuite 26 } 27 28 var _ = gc.Suite(&storageSuite{}) 29 30 func (s *storageSuite) TestStorageListEmpty(c *gc.C) { 31 s.storageAccessor.allStorageInstances = func() ([]state.StorageInstance, error) { 32 s.stub.AddCall(allStorageInstancesCall) 33 return []state.StorageInstance{}, nil 34 } 35 36 found, err := s.api.ListStorageDetails( 37 params.StorageFilters{[]params.StorageFilter{{}}}, 38 ) 39 c.Assert(err, jc.ErrorIsNil) 40 c.Assert(found.Results, gc.HasLen, 1) 41 c.Assert(found.Results[0].Error, gc.IsNil) 42 c.Assert(found.Results[0].Result, gc.HasLen, 0) 43 s.assertCalls(c, []string{allStorageInstancesCall}) 44 } 45 46 func (s *storageSuite) TestStorageListFilesystem(c *gc.C) { 47 found, err := s.api.ListStorageDetails( 48 params.StorageFilters{[]params.StorageFilter{{}}}, 49 ) 50 c.Assert(err, jc.ErrorIsNil) 51 52 expectedCalls := []string{ 53 allStorageInstancesCall, 54 storageInstanceFilesystemCall, 55 storageInstanceAttachmentsCall, 56 storageInstanceCall, 57 storageInstanceFilesystemCall, 58 storageInstanceFilesystemAttachmentCall, 59 } 60 s.assertCalls(c, expectedCalls) 61 62 c.Assert(found.Results, gc.HasLen, 1) 63 c.Assert(found.Results[0].Error, gc.IsNil) 64 c.Assert(found.Results[0].Result, gc.HasLen, 1) 65 wantedDetails := s.createTestStorageDetails() 66 c.Assert(found.Results[0].Result[0], jc.DeepEquals, wantedDetails) 67 } 68 69 func (s *storageSuite) TestStorageListVolume(c *gc.C) { 70 s.storageInstance.kind = state.StorageKindBlock 71 found, err := s.api.ListStorageDetails( 72 params.StorageFilters{[]params.StorageFilter{{}}}, 73 ) 74 c.Assert(err, jc.ErrorIsNil) 75 76 expectedCalls := []string{ 77 allStorageInstancesCall, 78 storageInstanceVolumeCall, 79 storageInstanceAttachmentsCall, 80 storageInstanceCall, 81 storageInstanceVolumeCall, 82 } 83 s.assertCalls(c, expectedCalls) 84 85 c.Assert(found.Results, gc.HasLen, 1) 86 c.Assert(found.Results[0].Error, gc.IsNil) 87 c.Assert(found.Results[0].Result, gc.HasLen, 1) 88 wantedDetails := s.createTestStorageDetails() 89 wantedDetails.Kind = params.StorageKindBlock 90 wantedDetails.Status.Status = status.Attached 91 c.Assert(found.Results[0].Result[0], jc.DeepEquals, wantedDetails) 92 } 93 94 func (s *storageSuite) TestStorageListError(c *gc.C) { 95 msg := "list test error" 96 s.storageAccessor.allStorageInstances = func() ([]state.StorageInstance, error) { 97 s.stub.AddCall(allStorageInstancesCall) 98 return []state.StorageInstance{}, errors.Errorf(msg) 99 } 100 101 found, err := s.api.ListStorageDetails( 102 params.StorageFilters{[]params.StorageFilter{{}}}, 103 ) 104 c.Assert(err, jc.ErrorIsNil) 105 c.Assert(found.Results, gc.HasLen, 1) 106 c.Assert(found.Results[0].Error, gc.ErrorMatches, msg) 107 108 expectedCalls := []string{allStorageInstancesCall} 109 s.assertCalls(c, expectedCalls) 110 } 111 112 func (s *storageSuite) TestStorageListInstanceError(c *gc.C) { 113 msg := "list test error" 114 s.storageAccessor.storageInstance = func(sTag names.StorageTag) (state.StorageInstance, error) { 115 s.stub.AddCall(storageInstanceCall) 116 c.Assert(sTag, jc.DeepEquals, s.storageTag) 117 return nil, errors.Errorf(msg) 118 } 119 120 found, err := s.api.ListStorageDetails( 121 params.StorageFilters{[]params.StorageFilter{{}}}, 122 ) 123 c.Assert(err, jc.ErrorIsNil) 124 125 expectedCalls := []string{ 126 allStorageInstancesCall, 127 storageInstanceFilesystemCall, 128 storageInstanceAttachmentsCall, 129 storageInstanceCall, 130 } 131 s.assertCalls(c, expectedCalls) 132 c.Assert(found.Results, gc.HasLen, 1) 133 c.Assert(found.Results[0].Error, gc.ErrorMatches, 134 fmt.Sprintf("getting details for storage data/0: getting storage instance: %v", msg), 135 ) 136 } 137 138 func (s *storageSuite) TestStorageListAttachmentError(c *gc.C) { 139 s.storageAccessor.storageInstanceAttachments = func(tag names.StorageTag) ([]state.StorageAttachment, error) { 140 s.stub.AddCall(storageInstanceAttachmentsCall) 141 c.Assert(tag, jc.DeepEquals, s.storageTag) 142 return []state.StorageAttachment{}, errors.Errorf("list test error") 143 } 144 145 found, err := s.api.ListStorageDetails( 146 params.StorageFilters{[]params.StorageFilter{{}}}, 147 ) 148 c.Assert(err, jc.ErrorIsNil) 149 150 expectedCalls := []string{ 151 allStorageInstancesCall, 152 storageInstanceFilesystemCall, 153 storageInstanceAttachmentsCall, 154 } 155 s.assertCalls(c, expectedCalls) 156 c.Assert(found.Results, gc.HasLen, 1) 157 c.Assert(found.Results[0].Error, gc.ErrorMatches, 158 "getting details for storage data/0: list test error") 159 } 160 161 func (s *storageSuite) TestStorageListMachineError(c *gc.C) { 162 msg := "list test error" 163 s.state.unitErr = msg 164 found, err := s.api.ListStorageDetails( 165 params.StorageFilters{[]params.StorageFilter{{}}}, 166 ) 167 c.Assert(err, jc.ErrorIsNil) 168 169 expectedCalls := []string{ 170 allStorageInstancesCall, 171 storageInstanceFilesystemCall, 172 storageInstanceAttachmentsCall, 173 } 174 s.assertCalls(c, expectedCalls) 175 c.Assert(found.Results, gc.HasLen, 1) 176 c.Assert(found.Results[0].Error, gc.ErrorMatches, 177 fmt.Sprintf("getting details for storage data/0: %v", msg), 178 ) 179 } 180 181 func (s *storageSuite) TestStorageListFilesystemError(c *gc.C) { 182 msg := "list test error" 183 s.storageAccessor.storageInstanceFilesystem = func(sTag names.StorageTag) (state.Filesystem, error) { 184 s.stub.AddCall(storageInstanceFilesystemCall) 185 c.Assert(sTag, jc.DeepEquals, s.storageTag) 186 return nil, errors.Errorf(msg) 187 } 188 189 found, err := s.api.ListStorageDetails( 190 params.StorageFilters{[]params.StorageFilter{{}}}, 191 ) 192 c.Assert(err, jc.ErrorIsNil) 193 194 expectedCalls := []string{ 195 allStorageInstancesCall, 196 storageInstanceFilesystemCall, 197 } 198 s.assertCalls(c, expectedCalls) 199 c.Assert(found.Results, gc.HasLen, 1) 200 c.Assert(found.Results[0].Error, gc.ErrorMatches, 201 fmt.Sprintf("getting details for storage data/0: %v", msg), 202 ) 203 } 204 205 func (s *storageSuite) TestStorageListFilesystemAttachmentError(c *gc.C) { 206 msg := "list test error" 207 s.state.unitErr = msg 208 209 found, err := s.api.ListStorageDetails( 210 params.StorageFilters{[]params.StorageFilter{{}}}, 211 ) 212 c.Assert(err, jc.ErrorIsNil) 213 214 expectedCalls := []string{ 215 allStorageInstancesCall, 216 storageInstanceFilesystemCall, 217 storageInstanceAttachmentsCall, 218 } 219 s.assertCalls(c, expectedCalls) 220 c.Assert(found.Results, gc.HasLen, 1) 221 c.Assert(found.Results[0].Error, gc.ErrorMatches, 222 fmt.Sprintf("getting details for storage data/0: %v", msg), 223 ) 224 } 225 226 func (s *storageSuite) createTestStorageDetails() params.StorageDetails { 227 return params.StorageDetails{ 228 StorageTag: s.storageTag.String(), 229 OwnerTag: s.unitTag.String(), 230 Kind: params.StorageKindFilesystem, 231 Life: "dying", 232 Status: params.EntityStatus{ 233 Status: "attached", 234 }, 235 Attachments: map[string]params.StorageAttachmentDetails{ 236 s.unitTag.String(): { 237 s.storageTag.String(), 238 s.unitTag.String(), 239 s.machineTag.String(), 240 "", // location 241 "alive", 242 }, 243 }, 244 } 245 } 246 247 func (s *storageSuite) assertInstanceInfoError(c *gc.C, obtained params.StorageDetailsResult, wanted params.StorageDetailsResult, expected string) { 248 if expected != "" { 249 c.Assert(errors.Cause(obtained.Error), gc.ErrorMatches, fmt.Sprintf(".*%v.*", expected)) 250 c.Assert(obtained.Result, gc.IsNil) 251 } else { 252 c.Assert(obtained.Error, gc.IsNil) 253 c.Assert(obtained, jc.DeepEquals, wanted) 254 } 255 } 256 257 func (s *storageSuite) TestShowStorageEmpty(c *gc.C) { 258 found, err := s.api.StorageDetails(params.Entities{}) 259 c.Assert(err, jc.ErrorIsNil) 260 c.Assert(found.Results, gc.HasLen, 0) 261 } 262 263 func (s *storageSuite) TestShowStorageInvalidTag(c *gc.C) { 264 // Only storage tags are permitted 265 found, err := s.api.StorageDetails(params.Entities{ 266 Entities: []params.Entity{{Tag: "machine-1"}}, 267 }) 268 c.Assert(err, jc.ErrorIsNil) 269 c.Assert(found.Results, gc.HasLen, 1) 270 c.Assert(found.Results[0].Error, gc.ErrorMatches, `"machine-1" is not a valid storage tag`) 271 } 272 273 func (s *storageSuite) TestShowStorage(c *gc.C) { 274 entity := params.Entity{Tag: s.storageTag.String()} 275 276 found, err := s.api.StorageDetails( 277 params.Entities{Entities: []params.Entity{entity}}, 278 ) 279 c.Assert(err, jc.ErrorIsNil) 280 c.Assert(found.Results, gc.HasLen, 1) 281 282 one := found.Results[0] 283 c.Assert(one.Error, gc.IsNil) 284 285 expected := params.StorageDetails{ 286 StorageTag: s.storageTag.String(), 287 OwnerTag: s.unitTag.String(), 288 Kind: params.StorageKindFilesystem, 289 Life: "dying", 290 Status: params.EntityStatus{ 291 Status: "attached", 292 }, 293 Attachments: map[string]params.StorageAttachmentDetails{ 294 s.unitTag.String(): { 295 s.storageTag.String(), 296 s.unitTag.String(), 297 s.machineTag.String(), 298 "", 299 "alive", 300 }, 301 }, 302 } 303 c.Assert(one.Result, jc.DeepEquals, &expected) 304 } 305 306 func (s *storageSuite) TestShowStorageInvalidId(c *gc.C) { 307 storageTag := "foo" 308 entity := params.Entity{Tag: storageTag} 309 310 found, err := s.api.StorageDetails(params.Entities{Entities: []params.Entity{entity}}) 311 c.Assert(err, jc.ErrorIsNil) 312 c.Assert(found.Results, gc.HasLen, 1) 313 s.assertInstanceInfoError(c, found.Results[0], params.StorageDetailsResult{}, `"foo" is not a valid tag`) 314 } 315 316 func (s *storageSuite) TestRemove(c *gc.C) { 317 results, err := s.api.Remove(params.RemoveStorage{[]params.RemoveStorageInstance{ 318 {Tag: "storage-foo-0", DestroyStorage: true}, 319 {Tag: "storage-foo-1", DestroyAttachments: true, DestroyStorage: true}, 320 {Tag: "storage-foo-1", DestroyAttachments: true, DestroyStorage: false}, 321 {Tag: "volume-0"}, 322 {Tag: "filesystem-1-2"}, 323 {Tag: "machine-0"}, 324 }}) 325 c.Assert(err, jc.ErrorIsNil) 326 c.Assert(results.Results, jc.DeepEquals, []params.ErrorResult{ 327 {Error: ¶ms.Error{Message: "cannae do it"}}, 328 {Error: ¶ms.Error{Message: "cannae do it"}}, 329 {Error: ¶ms.Error{Message: "cannae do it"}}, 330 {Error: ¶ms.Error{Message: `"volume-0" is not a valid storage tag`}}, 331 {Error: ¶ms.Error{Message: `"filesystem-1-2" is not a valid storage tag`}}, 332 {Error: ¶ms.Error{Message: `"machine-0" is not a valid storage tag`}}, 333 }) 334 s.stub.CheckCallNames(c, 335 getBlockForTypeCall, // Remove 336 getBlockForTypeCall, // Change 337 destroyStorageInstanceCall, 338 destroyStorageInstanceCall, 339 releaseStorageInstanceCall, 340 ) 341 s.stub.CheckCall(c, 2, destroyStorageInstanceCall, names.NewStorageTag("foo/0"), false) 342 s.stub.CheckCall(c, 3, destroyStorageInstanceCall, names.NewStorageTag("foo/1"), true) 343 s.stub.CheckCall(c, 4, releaseStorageInstanceCall, names.NewStorageTag("foo/1"), true) 344 } 345 346 func (s *storageSuite) TestDestroyV3(c *gc.C) { 347 results, err := s.apiv3.Destroy(params.Entities{[]params.Entity{ 348 {Tag: "storage-foo-0"}, 349 {Tag: "volume-0"}, 350 {Tag: "filesystem-1-2"}, 351 {Tag: "machine-0"}, 352 }}) 353 c.Assert(err, jc.ErrorIsNil) 354 c.Assert(results.Results, jc.DeepEquals, []params.ErrorResult{ 355 {Error: ¶ms.Error{Message: "cannae do it"}}, 356 {Error: ¶ms.Error{Message: `"volume-0" is not a valid storage tag`}}, 357 {Error: ¶ms.Error{Message: `"filesystem-1-2" is not a valid storage tag`}}, 358 {Error: ¶ms.Error{Message: `"machine-0" is not a valid storage tag`}}, 359 }) 360 s.stub.CheckCallNames(c, 361 getBlockForTypeCall, // Remove 362 getBlockForTypeCall, // Change 363 destroyStorageInstanceCall, 364 ) 365 s.stub.CheckCall(c, 2, destroyStorageInstanceCall, names.NewStorageTag("foo/0"), true) 366 } 367 368 func (s *storageSuite) TestDetach(c *gc.C) { 369 results, err := s.api.Detach(params.StorageAttachmentIds{[]params.StorageAttachmentId{ 370 {StorageTag: "storage-data-0", UnitTag: "unit-mysql-0"}, 371 {StorageTag: "storage-data-0", UnitTag: ""}, 372 {StorageTag: "volume-0", UnitTag: "unit-bar-0"}, 373 {StorageTag: "filesystem-1-2", UnitTag: "unit-bar-0"}, 374 {StorageTag: "machine-0", UnitTag: "unit-bar-0"}, 375 {StorageTag: "storage-foo-0", UnitTag: "application-bar"}, 376 }}) 377 c.Assert(err, jc.ErrorIsNil) 378 c.Assert(results.Results, gc.HasLen, 6) 379 c.Assert(results.Results, jc.DeepEquals, []params.ErrorResult{ 380 {Error: nil}, 381 {Error: nil}, 382 {Error: ¶ms.Error{Message: `"volume-0" is not a valid storage tag`}}, 383 {Error: ¶ms.Error{Message: `"filesystem-1-2" is not a valid storage tag`}}, 384 {Error: ¶ms.Error{Message: `"machine-0" is not a valid storage tag`}}, 385 {Error: ¶ms.Error{Message: `"application-bar" is not a valid unit tag`}}, 386 }) 387 s.assertCalls(c, []string{ 388 getBlockForTypeCall, // Change 389 detachStorageCall, 390 storageInstanceAttachmentsCall, 391 detachStorageCall, 392 }) 393 s.stub.CheckCalls(c, []testing.StubCall{ 394 {getBlockForTypeCall, []interface{}{state.ChangeBlock}}, 395 {detachStorageCall, []interface{}{s.storageTag, s.unitTag}}, 396 {storageInstanceAttachmentsCall, []interface{}{s.storageTag}}, 397 {detachStorageCall, []interface{}{s.storageTag, s.unitTag}}, 398 }) 399 } 400 401 func (s *storageSuite) TestDetachSpecifiedNotFound(c *gc.C) { 402 results, err := s.api.Detach(params.StorageAttachmentIds{[]params.StorageAttachmentId{ 403 {StorageTag: "storage-data-0", UnitTag: "unit-foo-42"}, 404 }}) 405 c.Assert(err, jc.ErrorIsNil) 406 c.Assert(results.Results, gc.HasLen, 1) 407 c.Assert(results.Results, jc.DeepEquals, []params.ErrorResult{ 408 {Error: ¶ms.Error{ 409 Code: params.CodeNotFound, 410 Message: "attachment of storage data/0 to unit foo/42 not found", 411 }}, 412 }) 413 s.assertCalls(c, []string{ 414 getBlockForTypeCall, // Change 415 detachStorageCall, 416 }) 417 s.stub.CheckCalls(c, []testing.StubCall{ 418 {getBlockForTypeCall, []interface{}{state.ChangeBlock}}, 419 {detachStorageCall, []interface{}{ 420 s.storageTag, 421 names.NewUnitTag("foo/42"), 422 }}, 423 }) 424 } 425 426 func (s *storageSuite) TestDetachAttachmentNotFoundConcurrent(c *gc.C) { 427 // Simulate: 428 // 1. call StorageAttachments, and receive 429 // a list of alive attachments 430 // 2. attachment is concurrently destroyed 431 // and removed by another process 432 s.storageAccessor.detachStorage = func(sTag names.StorageTag, uTag names.UnitTag) error { 433 s.stub.AddCall(detachStorageCall, sTag, uTag) 434 return errors.NotFoundf( 435 "attachment of %s to %s", 436 names.ReadableString(sTag), 437 names.ReadableString(uTag), 438 ) 439 } 440 results, err := s.api.Detach(params.StorageAttachmentIds{[]params.StorageAttachmentId{ 441 {StorageTag: "storage-data-0"}, 442 }}) 443 c.Assert(err, jc.ErrorIsNil) 444 c.Assert(results.Results, gc.HasLen, 1) 445 c.Assert(results.Results, jc.DeepEquals, []params.ErrorResult{{}}) 446 s.assertCalls(c, []string{ 447 getBlockForTypeCall, // Change 448 storageInstanceAttachmentsCall, 449 detachStorageCall, 450 }) 451 s.stub.CheckCalls(c, []testing.StubCall{ 452 {getBlockForTypeCall, []interface{}{state.ChangeBlock}}, 453 {storageInstanceAttachmentsCall, []interface{}{s.storageTag}}, 454 {detachStorageCall, []interface{}{s.storageTag, s.unitTag}}, 455 }) 456 } 457 458 func (s *storageSuite) TestDetachNoAttachmentsStorageNotFound(c *gc.C) { 459 results, err := s.api.Detach(params.StorageAttachmentIds{[]params.StorageAttachmentId{ 460 {StorageTag: "storage-foo-42"}, 461 }}) 462 c.Assert(err, jc.ErrorIsNil) 463 c.Assert(results.Results, gc.HasLen, 1) 464 c.Assert(results.Results, jc.DeepEquals, []params.ErrorResult{ 465 {Error: ¶ms.Error{ 466 Code: params.CodeNotFound, 467 Message: "storage foo/42 not found", 468 }}, 469 }) 470 s.stub.CheckCalls(c, []testing.StubCall{ 471 {getBlockForTypeCall, []interface{}{state.ChangeBlock}}, 472 {storageInstanceAttachmentsCall, []interface{}{names.NewStorageTag("foo/42")}}, 473 {storageInstanceCall, []interface{}{names.NewStorageTag("foo/42")}}, 474 }) 475 } 476 477 func (s *storageSuite) TestAttach(c *gc.C) { 478 results, err := s.api.Attach(params.StorageAttachmentIds{[]params.StorageAttachmentId{ 479 {StorageTag: "storage-data-0", UnitTag: "unit-mysql-0"}, 480 {StorageTag: "storage-data-0", UnitTag: "machine-0"}, 481 {StorageTag: "volume-0", UnitTag: "unit-mysql-0"}, 482 }}) 483 c.Assert(err, jc.ErrorIsNil) 484 c.Assert(results.Results, gc.HasLen, 3) 485 c.Assert(results.Results, jc.DeepEquals, []params.ErrorResult{ 486 {Error: nil}, 487 {Error: ¶ms.Error{Message: `"machine-0" is not a valid unit tag`}}, 488 {Error: ¶ms.Error{Message: `"volume-0" is not a valid storage tag`}}, 489 }) 490 s.stub.CheckCalls(c, []testing.StubCall{ 491 {getBlockForTypeCall, []interface{}{state.ChangeBlock}}, 492 {attachStorageCall, []interface{}{s.storageTag, s.unitTag}}, 493 }) 494 } 495 496 func (s *storageSuite) TestImportFilesystem(c *gc.C) { 497 s.state.modelTag = coretesting.ModelTag 498 filesystemSource := filesystemImporter{&dummy.FilesystemSource{}} 499 dummyStorageProvider := &dummy.StorageProvider{ 500 StorageScope: storage.ScopeEnviron, 501 IsDynamic: true, 502 FilesystemSourceFunc: func(*storage.Config) (storage.FilesystemSource, error) { 503 return filesystemSource, nil 504 }, 505 } 506 s.registry.Providers["radiance"] = dummyStorageProvider 507 508 results, err := s.api.Import(params.BulkImportStorageParams{[]params.ImportStorageParams{{ 509 Kind: params.StorageKindFilesystem, 510 Pool: "radiance", 511 ProviderId: "foo", 512 StorageName: "pgdata", 513 }}}) 514 c.Assert(err, jc.ErrorIsNil) 515 c.Assert(results.Results, jc.DeepEquals, []params.ImportStorageResult{{ 516 Result: ¶ms.ImportStorageDetails{ 517 StorageTag: "storage-data-0", 518 }, 519 }}) 520 filesystemSource.CheckCalls(c, []testing.StubCall{ 521 {"ImportFilesystem", []interface{}{ 522 s.callContext, 523 "foo", map[string]string{ 524 "juju-model-uuid": "deadbeef-0bad-400d-8000-4b1d0d06f00d", 525 "juju-controller-uuid": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 526 }, 527 }}, 528 }) 529 s.stub.CheckCalls(c, []testing.StubCall{ 530 {getBlockForTypeCall, []interface{}{state.ChangeBlock}}, 531 {addExistingFilesystemCall, []interface{}{ 532 state.FilesystemInfo{ 533 FilesystemId: "foo", 534 Pool: "radiance", 535 Size: 123, 536 }, 537 (*state.VolumeInfo)(nil), 538 "pgdata", 539 }}, 540 }) 541 } 542 543 func (s *storageSuite) TestImportFilesystemVolumeBacked(c *gc.C) { 544 s.state.modelTag = coretesting.ModelTag 545 volumeSource := volumeImporter{&dummy.VolumeSource{}} 546 dummyStorageProvider := &dummy.StorageProvider{ 547 StorageScope: storage.ScopeEnviron, 548 IsDynamic: true, 549 SupportsFunc: func(kind storage.StorageKind) bool { 550 return kind == storage.StorageKindBlock 551 }, 552 VolumeSourceFunc: func(*storage.Config) (storage.VolumeSource, error) { 553 return volumeSource, nil 554 }, 555 } 556 s.registry.Providers["radiance"] = dummyStorageProvider 557 558 results, err := s.api.Import(params.BulkImportStorageParams{[]params.ImportStorageParams{{ 559 Kind: params.StorageKindFilesystem, 560 Pool: "radiance", 561 ProviderId: "foo", 562 StorageName: "pgdata", 563 }}}) 564 c.Assert(err, jc.ErrorIsNil) 565 c.Assert(results.Results, jc.DeepEquals, []params.ImportStorageResult{{ 566 Result: ¶ms.ImportStorageDetails{ 567 StorageTag: "storage-data-0", 568 }, 569 }}) 570 volumeSource.CheckCalls(c, []testing.StubCall{ 571 {"ImportVolume", []interface{}{ 572 s.callContext, 573 "foo", map[string]string{ 574 "juju-model-uuid": "deadbeef-0bad-400d-8000-4b1d0d06f00d", 575 "juju-controller-uuid": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 576 }, 577 }}, 578 }) 579 s.stub.CheckCalls(c, []testing.StubCall{ 580 {getBlockForTypeCall, []interface{}{state.ChangeBlock}}, 581 {addExistingFilesystemCall, []interface{}{ 582 state.FilesystemInfo{ 583 Pool: "radiance", 584 Size: 123, 585 }, 586 &state.VolumeInfo{ 587 VolumeId: "foo", 588 Pool: "radiance", 589 Size: 123, 590 HardwareId: "hw", 591 }, 592 "pgdata", 593 }}, 594 }) 595 } 596 597 func (s *storageSuite) TestImportFilesystemError(c *gc.C) { 598 filesystemSource := filesystemImporter{&dummy.FilesystemSource{}} 599 dummyStorageProvider := &dummy.StorageProvider{ 600 StorageScope: storage.ScopeEnviron, 601 IsDynamic: true, 602 FilesystemSourceFunc: func(*storage.Config) (storage.FilesystemSource, error) { 603 return filesystemSource, nil 604 }, 605 } 606 s.registry.Providers["radiance"] = dummyStorageProvider 607 608 filesystemSource.SetErrors(errors.New("nope")) 609 results, err := s.api.Import(params.BulkImportStorageParams{[]params.ImportStorageParams{{ 610 Kind: params.StorageKindFilesystem, 611 Pool: "radiance", 612 ProviderId: "foo", 613 StorageName: "pgdata", 614 }}}) 615 c.Assert(err, jc.ErrorIsNil) 616 c.Assert(results.Results, jc.DeepEquals, []params.ImportStorageResult{ 617 {Error: ¶ms.Error{Message: `importing filesystem: nope`}}, 618 }) 619 filesystemSource.CheckCallNames(c, "ImportFilesystem") 620 s.stub.CheckCallNames(c, getBlockForTypeCall) 621 } 622 623 func (s *storageSuite) TestImportFilesystemNotSupported(c *gc.C) { 624 filesystemSource := &dummy.FilesystemSource{} 625 dummyStorageProvider := &dummy.StorageProvider{ 626 StorageScope: storage.ScopeEnviron, 627 IsDynamic: true, 628 FilesystemSourceFunc: func(*storage.Config) (storage.FilesystemSource, error) { 629 return filesystemSource, nil 630 }, 631 } 632 s.registry.Providers["radiance"] = dummyStorageProvider 633 634 results, err := s.api.Import(params.BulkImportStorageParams{[]params.ImportStorageParams{{ 635 Kind: params.StorageKindFilesystem, 636 Pool: "radiance", 637 ProviderId: "foo", 638 StorageName: "pgdata", 639 }}}) 640 c.Assert(err, jc.ErrorIsNil) 641 c.Assert(results.Results, jc.DeepEquals, []params.ImportStorageResult{ 642 {Error: ¶ms.Error{ 643 Message: `importing filesystem with storage provider "radiance" not supported`, 644 Code: "not supported", 645 }}, 646 }) 647 filesystemSource.CheckNoCalls(c) 648 s.stub.CheckCallNames(c, getBlockForTypeCall) 649 } 650 651 func (s *storageSuite) TestImportFilesystemVolumeBackedNotSupported(c *gc.C) { 652 volumeSource := &dummy.VolumeSource{} 653 dummyStorageProvider := &dummy.StorageProvider{ 654 StorageScope: storage.ScopeEnviron, 655 IsDynamic: true, 656 SupportsFunc: func(kind storage.StorageKind) bool { 657 return kind == storage.StorageKindBlock 658 }, 659 VolumeSourceFunc: func(*storage.Config) (storage.VolumeSource, error) { 660 return volumeSource, nil 661 }, 662 } 663 s.registry.Providers["radiance"] = dummyStorageProvider 664 665 results, err := s.api.Import(params.BulkImportStorageParams{[]params.ImportStorageParams{{ 666 Kind: params.StorageKindFilesystem, 667 Pool: "radiance", 668 ProviderId: "foo", 669 StorageName: "pgdata", 670 }}}) 671 c.Assert(err, jc.ErrorIsNil) 672 c.Assert(results.Results, jc.DeepEquals, []params.ImportStorageResult{ 673 {Error: ¶ms.Error{ 674 Message: `importing volume with storage provider "radiance" not supported`, 675 Code: "not supported", 676 }}, 677 }) 678 volumeSource.CheckNoCalls(c) 679 s.stub.CheckCallNames(c, getBlockForTypeCall) 680 } 681 682 func (s *storageSuite) TestImportValidationErrors(c *gc.C) { 683 results, err := s.api.Import(params.BulkImportStorageParams{[]params.ImportStorageParams{{ 684 Kind: params.StorageKindBlock, 685 Pool: "radiance", 686 ProviderId: "foo", 687 StorageName: "pgdata", 688 }, { 689 Kind: params.StorageKindFilesystem, 690 Pool: "123", 691 ProviderId: "foo", 692 StorageName: "pgdata", 693 }}}) 694 c.Assert(err, jc.ErrorIsNil) 695 c.Assert(results.Results, jc.DeepEquals, []params.ImportStorageResult{ 696 {Error: ¶ms.Error{Message: `storage kind "block" not supported`, Code: "not supported"}}, 697 {Error: ¶ms.Error{Message: `pool name "123" not valid`}}, 698 }) 699 } 700 701 type filesystemImporter struct { 702 *dummy.FilesystemSource 703 } 704 705 // ImportFilesystem is part of the storage.FilesystemImporter interface. 706 func (f filesystemImporter) ImportFilesystem(ctx context.ProviderCallContext, providerId string, tags map[string]string) (storage.FilesystemInfo, error) { 707 f.MethodCall(f, "ImportFilesystem", ctx, providerId, tags) 708 return storage.FilesystemInfo{ 709 FilesystemId: providerId, 710 Size: 123, 711 }, f.NextErr() 712 } 713 714 type volumeImporter struct { 715 *dummy.VolumeSource 716 } 717 718 // ImportVolume is part of the storage.VolumeImporter interface. 719 func (v volumeImporter) ImportVolume(ctx context.ProviderCallContext, providerId string, tags map[string]string) (storage.VolumeInfo, error) { 720 v.MethodCall(v, "ImportVolume", ctx, providerId, tags) 721 return storage.VolumeInfo{ 722 VolumeId: providerId, 723 Size: 123, 724 HardwareId: "hw", 725 }, v.NextErr() 726 }