github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/storageprovisioner/mock_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storageprovisioner_test 5 6 import ( 7 "time" 8 9 "github.com/juju/clock" 10 "github.com/juju/errors" 11 "github.com/juju/testing" 12 gitjujutesting "github.com/juju/testing" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/names.v2" 15 16 "github.com/juju/juju/apiserver/common" 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/core/instance" 19 "github.com/juju/juju/core/watcher" 20 "github.com/juju/juju/core/watcher/watchertest" 21 "github.com/juju/juju/environs/context" 22 "github.com/juju/juju/storage" 23 ) 24 25 const needsInstanceVolumeId = "23" 26 const noAttachmentVolumeId = "66" 27 28 var ( 29 releasingVolumeId = "2" 30 releasingFilesystemId = "2" 31 ) 32 33 var dyingVolumeAttachmentId = params.MachineStorageId{ 34 MachineTag: "machine-0", 35 AttachmentTag: "volume-0", 36 } 37 38 var dyingUnitVolumeAttachmentId = params.MachineStorageId{ 39 MachineTag: "unit-mariadb-0", 40 AttachmentTag: "volume-0", 41 } 42 43 var dyingFilesystemAttachmentId = params.MachineStorageId{ 44 MachineTag: "machine-0", 45 AttachmentTag: "filesystem-0", 46 } 47 48 var dyingUnitFilesystemAttachmentId = params.MachineStorageId{ 49 MachineTag: "unit-mariadb-0", 50 AttachmentTag: "filesystem-0", 51 } 52 53 var missingVolumeAttachmentId = params.MachineStorageId{ 54 MachineTag: "machine-3", 55 AttachmentTag: "volume-1", 56 } 57 58 type mockWatcher struct{} 59 60 func (mockWatcher) Kill() {} 61 func (mockWatcher) Wait() error { return nil } 62 63 func newMockNotifyWatcher() *mockNotifyWatcher { 64 return &mockNotifyWatcher{ 65 changes: make(chan struct{}, 1), 66 } 67 } 68 69 type mockNotifyWatcher struct { 70 mockWatcher 71 changes chan struct{} 72 } 73 74 func (w *mockNotifyWatcher) Changes() watcher.NotifyChannel { 75 return w.changes 76 } 77 78 func newMockStringsWatcher() *mockStringsWatcher { 79 return &mockStringsWatcher{ 80 changes: make(chan []string, 1), 81 } 82 } 83 84 type mockStringsWatcher struct { 85 mockWatcher 86 changes chan []string 87 } 88 89 func (w *mockStringsWatcher) Changes() watcher.StringsChannel { 90 return w.changes 91 } 92 93 type mockApplicationsWatcher struct { 94 watcher *watchertest.MockStringsWatcher 95 } 96 97 func (w *mockApplicationsWatcher) WatchApplications() (watcher.StringsWatcher, error) { 98 return w.watcher, nil 99 } 100 101 func newMockApplicationsWatcher(ch chan []string) *mockApplicationsWatcher { 102 return &mockApplicationsWatcher{ 103 watcher: watchertest.NewMockStringsWatcher(ch), 104 } 105 } 106 107 func newMockAttachmentsWatcher() *mockAttachmentsWatcher { 108 return &mockAttachmentsWatcher{ 109 changes: make(chan []watcher.MachineStorageId, 1), 110 } 111 } 112 113 type mockAttachmentsWatcher struct { 114 mockWatcher 115 changes chan []watcher.MachineStorageId 116 } 117 118 func (w *mockAttachmentsWatcher) Changes() watcher.MachineStorageIdsChannel { 119 return w.changes 120 } 121 122 func newMockAttachmentPlansWatcher() *mockAttachmentPlansWatcher { 123 return &mockAttachmentPlansWatcher{ 124 changes: make(chan []watcher.MachineStorageId, 1), 125 } 126 } 127 128 type mockAttachmentPlansWatcher struct { 129 mockWatcher 130 changes chan []watcher.MachineStorageId 131 } 132 133 func (w *mockAttachmentPlansWatcher) Changes() watcher.MachineStorageIdsChannel { 134 return w.changes 135 } 136 137 type mockVolumeAccessor struct { 138 volumesWatcher *mockStringsWatcher 139 attachmentsWatcher *mockAttachmentsWatcher 140 attachmentPlansWatcher *mockAttachmentPlansWatcher 141 blockDevicesWatcher *mockNotifyWatcher 142 provisionedMachines map[string]instance.Id 143 provisionedVolumes map[string]params.Volume 144 provisionedAttachments map[params.MachineStorageId]params.VolumeAttachment 145 blockDevices map[params.MachineStorageId]storage.BlockDevice 146 147 setVolumeInfo func([]params.Volume) ([]params.ErrorResult, error) 148 setVolumeAttachmentInfo func([]params.VolumeAttachment) ([]params.ErrorResult, error) 149 createVolumeAttachmentPlans func([]params.VolumeAttachmentPlan) ([]params.ErrorResult, error) 150 } 151 152 func (m *mockVolumeAccessor) provisionVolume(tag names.VolumeTag) params.Volume { 153 v := params.Volume{ 154 VolumeTag: tag.String(), 155 Info: params.VolumeInfo{ 156 VolumeId: "vol-" + tag.Id(), 157 }, 158 } 159 m.provisionedVolumes[tag.String()] = v 160 return v 161 } 162 163 func (w *mockVolumeAccessor) WatchVolumes(names.Tag) (watcher.StringsWatcher, error) { 164 return w.volumesWatcher, nil 165 } 166 167 func (w *mockVolumeAccessor) WatchVolumeAttachments(names.Tag) (watcher.MachineStorageIdsWatcher, error) { 168 return w.attachmentsWatcher, nil 169 } 170 171 func (w *mockVolumeAccessor) WatchBlockDevices(tag names.MachineTag) (watcher.NotifyWatcher, error) { 172 return w.blockDevicesWatcher, nil 173 } 174 175 func (w *mockVolumeAccessor) WatchVolumeAttachmentPlans(names.Tag) (watcher.MachineStorageIdsWatcher, error) { 176 return w.attachmentPlansWatcher, nil 177 } 178 179 func (v *mockVolumeAccessor) Volumes(volumes []names.VolumeTag) ([]params.VolumeResult, error) { 180 var result []params.VolumeResult 181 for _, tag := range volumes { 182 if vol, ok := v.provisionedVolumes[tag.String()]; ok { 183 result = append(result, params.VolumeResult{Result: vol}) 184 } else { 185 result = append(result, params.VolumeResult{ 186 Error: common.ServerError(errors.NotProvisionedf("volume %q", tag.Id())), 187 }) 188 } 189 } 190 return result, nil 191 } 192 193 func (v *mockVolumeAccessor) VolumeAttachments(ids []params.MachineStorageId) ([]params.VolumeAttachmentResult, error) { 194 var result []params.VolumeAttachmentResult 195 for _, id := range ids { 196 if att, ok := v.provisionedAttachments[id]; ok { 197 result = append(result, params.VolumeAttachmentResult{Result: att}) 198 } else { 199 result = append(result, params.VolumeAttachmentResult{ 200 Error: common.ServerError(errors.NotProvisionedf("volume attachment %v", id)), 201 }) 202 } 203 } 204 return result, nil 205 } 206 207 func (v *mockVolumeAccessor) VolumeBlockDevices(ids []params.MachineStorageId) ([]params.BlockDeviceResult, error) { 208 var result []params.BlockDeviceResult 209 for _, id := range ids { 210 if dev, ok := v.blockDevices[id]; ok { 211 result = append(result, params.BlockDeviceResult{Result: dev}) 212 } else { 213 result = append(result, params.BlockDeviceResult{ 214 Error: common.ServerError(errors.NotFoundf("block device for volume attachment %v", id)), 215 }) 216 } 217 } 218 return result, nil 219 } 220 221 func (v *mockVolumeAccessor) VolumeParams(volumes []names.VolumeTag) ([]params.VolumeParamsResult, error) { 222 var result []params.VolumeParamsResult 223 for _, tag := range volumes { 224 volumeParams := params.VolumeParams{ 225 VolumeTag: tag.String(), 226 Size: 1024, 227 Provider: "dummy", 228 Attributes: map[string]interface{}{ 229 "persistent": tag.String() == "volume-1", 230 }, 231 Tags: map[string]string{ 232 "very": "fancy", 233 }, 234 } 235 if tag.Id() != noAttachmentVolumeId { 236 volumeParams.Attachment = ¶ms.VolumeAttachmentParams{ 237 VolumeTag: tag.String(), 238 MachineTag: "machine-1", 239 InstanceId: string(v.provisionedMachines["machine-1"]), 240 Provider: "dummy", 241 ReadOnly: tag.String() == "volume-1", 242 } 243 } 244 result = append(result, params.VolumeParamsResult{Result: volumeParams}) 245 } 246 return result, nil 247 } 248 249 func (v *mockVolumeAccessor) RemoveVolumeParams(volumes []names.VolumeTag) ([]params.RemoveVolumeParamsResult, error) { 250 var result []params.RemoveVolumeParamsResult 251 for _, tag := range volumes { 252 v, ok := v.provisionedVolumes[tag.String()] 253 if !ok { 254 result = append(result, params.RemoveVolumeParamsResult{ 255 Error: ¶ms.Error{Code: params.CodeNotProvisioned}, 256 }) 257 continue 258 } 259 volumeParams := params.RemoveVolumeParams{ 260 Provider: "dummy", 261 VolumeId: v.Info.VolumeId, 262 Destroy: tag.Id() != releasingVolumeId, 263 } 264 result = append(result, params.RemoveVolumeParamsResult{Result: volumeParams}) 265 } 266 return result, nil 267 } 268 269 func (v *mockVolumeAccessor) VolumeAttachmentParams(ids []params.MachineStorageId) ([]params.VolumeAttachmentParamsResult, error) { 270 var result []params.VolumeAttachmentParamsResult 271 for _, id := range ids { 272 // Parameters are returned regardless of whether the attachment 273 // exists; this is to support reattachment. 274 instanceId, _ := v.provisionedMachines[id.MachineTag] 275 result = append(result, params.VolumeAttachmentParamsResult{Result: params.VolumeAttachmentParams{ 276 MachineTag: id.MachineTag, 277 VolumeTag: id.AttachmentTag, 278 InstanceId: string(instanceId), 279 Provider: "dummy", 280 ReadOnly: id.AttachmentTag == "volume-1", 281 }}) 282 } 283 return result, nil 284 } 285 286 func (v *mockVolumeAccessor) SetVolumeInfo(volumes []params.Volume) ([]params.ErrorResult, error) { 287 if v.setVolumeInfo != nil { 288 return v.setVolumeInfo(volumes) 289 } 290 return make([]params.ErrorResult, len(volumes)), nil 291 } 292 293 func (v *mockVolumeAccessor) SetVolumeAttachmentInfo(volumeAttachments []params.VolumeAttachment) ([]params.ErrorResult, error) { 294 if v.setVolumeAttachmentInfo != nil { 295 return v.setVolumeAttachmentInfo(volumeAttachments) 296 } 297 return make([]params.ErrorResult, len(volumeAttachments)), nil 298 } 299 300 func (v *mockVolumeAccessor) CreateVolumeAttachmentPlans(volumeAttachmentPlans []params.VolumeAttachmentPlan) ([]params.ErrorResult, error) { 301 if v.createVolumeAttachmentPlans != nil { 302 return v.createVolumeAttachmentPlans(volumeAttachmentPlans) 303 } 304 return make([]params.ErrorResult, len(volumeAttachmentPlans)), nil 305 } 306 307 func (v *mockVolumeAccessor) RemoveVolumeAttachmentPlan(machineIds []params.MachineStorageId) ([]params.ErrorResult, error) { 308 return make([]params.ErrorResult, len(machineIds)), nil 309 } 310 311 func (v *mockVolumeAccessor) SetVolumeAttachmentPlanBlockInfo(volumeAttachmentPlans []params.VolumeAttachmentPlan) ([]params.ErrorResult, error) { 312 return make([]params.ErrorResult, len(volumeAttachmentPlans)), nil 313 } 314 315 func (v *mockVolumeAccessor) VolumeAttachmentPlans([]params.MachineStorageId) ([]params.VolumeAttachmentPlanResult, error) { 316 return []params.VolumeAttachmentPlanResult{}, nil 317 } 318 319 func newMockVolumeAccessor() *mockVolumeAccessor { 320 return &mockVolumeAccessor{ 321 volumesWatcher: newMockStringsWatcher(), 322 attachmentsWatcher: newMockAttachmentsWatcher(), 323 attachmentPlansWatcher: newMockAttachmentPlansWatcher(), 324 blockDevicesWatcher: newMockNotifyWatcher(), 325 provisionedMachines: make(map[string]instance.Id), 326 provisionedVolumes: make(map[string]params.Volume), 327 provisionedAttachments: make(map[params.MachineStorageId]params.VolumeAttachment), 328 blockDevices: make(map[params.MachineStorageId]storage.BlockDevice), 329 } 330 } 331 332 type mockFilesystemAccessor struct { 333 testing.Stub 334 filesystemsWatcher *mockStringsWatcher 335 attachmentsWatcher *mockAttachmentsWatcher 336 provisionedMachines map[string]instance.Id 337 provisionedFilesystems map[string]params.Filesystem 338 provisionedAttachments map[params.MachineStorageId]params.FilesystemAttachment 339 340 setFilesystemInfo func([]params.Filesystem) ([]params.ErrorResult, error) 341 setFilesystemAttachmentInfo func([]params.FilesystemAttachment) ([]params.ErrorResult, error) 342 } 343 344 func (m *mockFilesystemAccessor) provisionFilesystem(tag names.FilesystemTag) params.Filesystem { 345 f := params.Filesystem{ 346 FilesystemTag: tag.String(), 347 Info: params.FilesystemInfo{ 348 FilesystemId: "fs-" + tag.Id(), 349 }, 350 } 351 m.provisionedFilesystems[tag.String()] = f 352 return f 353 } 354 355 func (w *mockFilesystemAccessor) WatchFilesystems(tag names.Tag) (watcher.StringsWatcher, error) { 356 w.AddCall("WatchFilesystems", tag) 357 return w.filesystemsWatcher, nil 358 } 359 360 func (w *mockFilesystemAccessor) WatchFilesystemAttachments(names.Tag) (watcher.MachineStorageIdsWatcher, error) { 361 return w.attachmentsWatcher, nil 362 } 363 364 func (v *mockFilesystemAccessor) Filesystems(filesystems []names.FilesystemTag) ([]params.FilesystemResult, error) { 365 var result []params.FilesystemResult 366 for _, tag := range filesystems { 367 if vol, ok := v.provisionedFilesystems[tag.String()]; ok { 368 result = append(result, params.FilesystemResult{Result: vol}) 369 } else { 370 result = append(result, params.FilesystemResult{ 371 Error: common.ServerError(errors.NotProvisionedf("filesystem %q", tag.Id())), 372 }) 373 } 374 } 375 return result, nil 376 } 377 378 func (v *mockFilesystemAccessor) FilesystemAttachments(ids []params.MachineStorageId) ([]params.FilesystemAttachmentResult, error) { 379 var result []params.FilesystemAttachmentResult 380 for _, id := range ids { 381 if att, ok := v.provisionedAttachments[id]; ok { 382 result = append(result, params.FilesystemAttachmentResult{Result: att}) 383 } else { 384 result = append(result, params.FilesystemAttachmentResult{ 385 Error: common.ServerError(errors.NotProvisionedf("filesystem attachment %v", id)), 386 }) 387 } 388 } 389 return result, nil 390 } 391 392 func (v *mockFilesystemAccessor) FilesystemParams(filesystems []names.FilesystemTag) ([]params.FilesystemParamsResult, error) { 393 results := make([]params.FilesystemParamsResult, len(filesystems)) 394 for i, tag := range filesystems { 395 filesystemParams := params.FilesystemParams{ 396 FilesystemTag: tag.String(), 397 Size: 1024, 398 Provider: "dummy", 399 Tags: map[string]string{ 400 "very": "fancy", 401 }, 402 } 403 if _, ok := names.FilesystemMachine(tag); ok { 404 // place all volume-backed filesystems on machine-scoped 405 // volumes with the same ID as the filesystem. 406 filesystemParams.VolumeTag = names.NewVolumeTag(tag.Id()).String() 407 } 408 results[i] = params.FilesystemParamsResult{Result: filesystemParams} 409 } 410 return results, nil 411 } 412 413 func (v *mockFilesystemAccessor) RemoveFilesystemParams(filesystems []names.FilesystemTag) ([]params.RemoveFilesystemParamsResult, error) { 414 results := make([]params.RemoveFilesystemParamsResult, len(filesystems)) 415 for i, tag := range filesystems { 416 f, ok := v.provisionedFilesystems[tag.String()] 417 if !ok { 418 results = append(results, params.RemoveFilesystemParamsResult{ 419 Error: ¶ms.Error{Code: params.CodeNotProvisioned}, 420 }) 421 continue 422 } 423 filesystemParams := params.RemoveFilesystemParams{ 424 Provider: "dummy", 425 FilesystemId: f.Info.FilesystemId, 426 Destroy: tag.Id() != releasingFilesystemId, 427 } 428 results[i] = params.RemoveFilesystemParamsResult{Result: filesystemParams} 429 } 430 return results, nil 431 } 432 433 func (f *mockFilesystemAccessor) FilesystemAttachmentParams(ids []params.MachineStorageId) ([]params.FilesystemAttachmentParamsResult, error) { 434 var result []params.FilesystemAttachmentParamsResult 435 for _, id := range ids { 436 // Parameters are returned regardless of whether the attachment 437 // exists; this is to support reattachment. 438 instanceId := f.provisionedMachines[id.MachineTag] 439 result = append(result, params.FilesystemAttachmentParamsResult{Result: params.FilesystemAttachmentParams{ 440 MachineTag: id.MachineTag, 441 FilesystemTag: id.AttachmentTag, 442 InstanceId: string(instanceId), 443 Provider: "dummy", 444 ReadOnly: true, 445 }}) 446 } 447 return result, nil 448 } 449 450 func (f *mockFilesystemAccessor) SetFilesystemInfo(filesystems []params.Filesystem) ([]params.ErrorResult, error) { 451 if f.setFilesystemInfo != nil { 452 return f.setFilesystemInfo(filesystems) 453 } 454 return make([]params.ErrorResult, len(filesystems)), nil 455 } 456 457 func (f *mockFilesystemAccessor) SetFilesystemAttachmentInfo(filesystemAttachments []params.FilesystemAttachment) ([]params.ErrorResult, error) { 458 if f.setFilesystemAttachmentInfo != nil { 459 return f.setFilesystemAttachmentInfo(filesystemAttachments) 460 } 461 return make([]params.ErrorResult, len(filesystemAttachments)), nil 462 } 463 464 func newMockFilesystemAccessor() *mockFilesystemAccessor { 465 return &mockFilesystemAccessor{ 466 filesystemsWatcher: newMockStringsWatcher(), 467 attachmentsWatcher: newMockAttachmentsWatcher(), 468 provisionedMachines: make(map[string]instance.Id), 469 provisionedFilesystems: make(map[string]params.Filesystem), 470 provisionedAttachments: make(map[params.MachineStorageId]params.FilesystemAttachment), 471 } 472 } 473 474 type mockLifecycleManager struct { 475 err *params.Error 476 life func([]names.Tag) ([]params.LifeResult, error) 477 attachmentLife func(ids []params.MachineStorageId) ([]params.LifeResult, error) 478 removeAttachments func([]params.MachineStorageId) ([]params.ErrorResult, error) 479 remove func([]names.Tag) ([]params.ErrorResult, error) 480 } 481 482 func (m *mockLifecycleManager) Life(tags []names.Tag) ([]params.LifeResult, error) { 483 if m.life != nil { 484 return m.life(tags) 485 } 486 var result []params.LifeResult 487 for _, tag := range tags { 488 if m.err != nil { 489 result = append(result, params.LifeResult{Error: m.err}) 490 continue 491 } 492 life := params.Alive 493 if tag.Kind() == names.ApplicationTagKind { 494 if tag.Id() == "mysql" { 495 life = params.Dying 496 } else if tag.Id() == "postgresql" { 497 life = params.Dead 498 } 499 } 500 result = append(result, params.LifeResult{Life: life}) 501 } 502 return result, nil 503 } 504 505 func (m *mockLifecycleManager) AttachmentLife(ids []params.MachineStorageId) ([]params.LifeResult, error) { 506 if m.attachmentLife != nil { 507 return m.attachmentLife(ids) 508 } 509 var result []params.LifeResult 510 for _, id := range ids { 511 switch id { 512 case dyingVolumeAttachmentId, dyingUnitVolumeAttachmentId, 513 dyingFilesystemAttachmentId, dyingUnitFilesystemAttachmentId: 514 result = append(result, params.LifeResult{Life: params.Dying}) 515 case missingVolumeAttachmentId: 516 result = append(result, params.LifeResult{ 517 Error: common.ServerError(errors.NotFoundf("attachment %v", id)), 518 }) 519 default: 520 result = append(result, params.LifeResult{Life: params.Alive}) 521 } 522 } 523 return result, nil 524 } 525 526 func (m *mockLifecycleManager) Remove(tags []names.Tag) ([]params.ErrorResult, error) { 527 if m.remove != nil { 528 return m.remove(tags) 529 } 530 return make([]params.ErrorResult, len(tags)), nil 531 } 532 533 func (m *mockLifecycleManager) RemoveAttachments(ids []params.MachineStorageId) ([]params.ErrorResult, error) { 534 if m.removeAttachments != nil { 535 return m.removeAttachments(ids) 536 } 537 return make([]params.ErrorResult, len(ids)), nil 538 } 539 540 // Set up a dummy storage provider so we can stub out volume creation. 541 type dummyProvider struct { 542 storage.Provider 543 dynamic bool 544 545 volumeSourceFunc func(*storage.Config) (storage.VolumeSource, error) 546 filesystemSourceFunc func(*storage.Config) (storage.FilesystemSource, error) 547 createVolumesFunc func([]storage.VolumeParams) ([]storage.CreateVolumesResult, error) 548 createFilesystemsFunc func([]storage.FilesystemParams) ([]storage.CreateFilesystemsResult, error) 549 attachVolumesFunc func([]storage.VolumeAttachmentParams) ([]storage.AttachVolumesResult, error) 550 attachFilesystemsFunc func([]storage.FilesystemAttachmentParams) ([]storage.AttachFilesystemsResult, error) 551 detachVolumesFunc func([]storage.VolumeAttachmentParams) ([]error, error) 552 detachFilesystemsFunc func([]storage.FilesystemAttachmentParams) ([]error, error) 553 destroyVolumesFunc func([]string) ([]error, error) 554 releaseVolumesFunc func([]string) ([]error, error) 555 destroyFilesystemsFunc func([]string) ([]error, error) 556 releaseFilesystemsFunc func([]string) ([]error, error) 557 validateVolumeParamsFunc func(storage.VolumeParams) error 558 validateFilesystemParamsFunc func(storage.FilesystemParams) error 559 } 560 561 type dummyVolumeSource struct { 562 storage.VolumeSource 563 provider *dummyProvider 564 createVolumesArgs [][]storage.VolumeParams 565 } 566 567 type dummyFilesystemSource struct { 568 storage.FilesystemSource 569 provider *dummyProvider 570 createFilesystemsArgs [][]storage.FilesystemParams 571 } 572 573 func (p *dummyProvider) VolumeSource(providerConfig *storage.Config) (storage.VolumeSource, error) { 574 if p.volumeSourceFunc != nil { 575 return p.volumeSourceFunc(providerConfig) 576 } 577 return &dummyVolumeSource{provider: p}, nil 578 } 579 580 func (p *dummyProvider) FilesystemSource(providerConfig *storage.Config) (storage.FilesystemSource, error) { 581 if p.filesystemSourceFunc != nil { 582 return p.filesystemSourceFunc(providerConfig) 583 } 584 return &dummyFilesystemSource{provider: p}, nil 585 } 586 587 func (p *dummyProvider) Dynamic() bool { 588 return p.dynamic 589 } 590 591 func (s *dummyVolumeSource) ValidateVolumeParams(params storage.VolumeParams) error { 592 if s.provider != nil && s.provider.validateVolumeParamsFunc != nil { 593 return s.provider.validateVolumeParamsFunc(params) 594 } 595 return nil 596 } 597 598 // CreateVolumes makes some volumes that we can check later to ensure things went as expected. 599 func (s *dummyVolumeSource) CreateVolumes(ctx context.ProviderCallContext, params []storage.VolumeParams) ([]storage.CreateVolumesResult, error) { 600 if s.provider != nil && s.provider.createVolumesFunc != nil { 601 return s.provider.createVolumesFunc(params) 602 } 603 604 paramsCopy := make([]storage.VolumeParams, len(params)) 605 copy(paramsCopy, params) 606 s.createVolumesArgs = append(s.createVolumesArgs, paramsCopy) 607 608 results := make([]storage.CreateVolumesResult, len(params)) 609 for i, p := range params { 610 persistent, _ := p.Attributes["persistent"].(bool) 611 results[i].Volume = &storage.Volume{ 612 p.Tag, 613 storage.VolumeInfo{ 614 Size: p.Size, 615 HardwareId: "serial-" + p.Tag.Id(), 616 VolumeId: "id-" + p.Tag.Id(), 617 Persistent: persistent, 618 }, 619 } 620 } 621 return results, nil 622 } 623 624 // DestroyVolumes destroys volumes. 625 func (s *dummyVolumeSource) DestroyVolumes(ctx context.ProviderCallContext, volumeIds []string) ([]error, error) { 626 if s.provider.destroyVolumesFunc != nil { 627 return s.provider.destroyVolumesFunc(volumeIds) 628 } 629 return make([]error, len(volumeIds)), nil 630 } 631 632 // ReleaseVolumes destroys volumes. 633 func (s *dummyVolumeSource) ReleaseVolumes(ctx context.ProviderCallContext, volumeIds []string) ([]error, error) { 634 if s.provider.releaseVolumesFunc != nil { 635 return s.provider.releaseVolumesFunc(volumeIds) 636 } 637 return make([]error, len(volumeIds)), nil 638 } 639 640 // AttachVolumes attaches volumes to machines. 641 func (s *dummyVolumeSource) AttachVolumes(ctx context.ProviderCallContext, params []storage.VolumeAttachmentParams) ([]storage.AttachVolumesResult, error) { 642 if s.provider != nil && s.provider.attachVolumesFunc != nil { 643 return s.provider.attachVolumesFunc(params) 644 } 645 646 results := make([]storage.AttachVolumesResult, len(params)) 647 for i, p := range params { 648 if p.VolumeId == "" { 649 panic("AttachVolumes called with unprovisioned volume") 650 } 651 if p.InstanceId == "" { 652 panic("AttachVolumes called with unprovisioned machine") 653 } 654 results[i].VolumeAttachment = &storage.VolumeAttachment{ 655 p.Volume, 656 p.Machine, 657 storage.VolumeAttachmentInfo{ 658 DeviceName: "/dev/sda" + p.Volume.Id(), 659 ReadOnly: p.ReadOnly, 660 }, 661 } 662 } 663 return results, nil 664 } 665 666 // DetachVolumes detaches volumes from machines. 667 func (s *dummyVolumeSource) DetachVolumes(ctx context.ProviderCallContext, params []storage.VolumeAttachmentParams) ([]error, error) { 668 if s.provider.detachVolumesFunc != nil { 669 return s.provider.detachVolumesFunc(params) 670 } 671 return make([]error, len(params)), nil 672 } 673 674 func (s *dummyFilesystemSource) ValidateFilesystemParams(params storage.FilesystemParams) error { 675 if s.provider != nil && s.provider.validateFilesystemParamsFunc != nil { 676 return s.provider.validateFilesystemParamsFunc(params) 677 } 678 return nil 679 } 680 681 // CreateFilesystems makes some filesystems that we can check later to ensure things went as expected. 682 func (s *dummyFilesystemSource) CreateFilesystems(ctx context.ProviderCallContext, params []storage.FilesystemParams) ([]storage.CreateFilesystemsResult, error) { 683 if s.provider != nil && s.provider.createFilesystemsFunc != nil { 684 return s.provider.createFilesystemsFunc(params) 685 } 686 687 paramsCopy := make([]storage.FilesystemParams, len(params)) 688 copy(paramsCopy, params) 689 s.createFilesystemsArgs = append(s.createFilesystemsArgs, paramsCopy) 690 691 results := make([]storage.CreateFilesystemsResult, len(params)) 692 for i, p := range params { 693 results[i].Filesystem = &storage.Filesystem{ 694 Tag: p.Tag, 695 FilesystemInfo: storage.FilesystemInfo{ 696 Size: p.Size, 697 FilesystemId: "id-" + p.Tag.Id(), 698 }, 699 } 700 } 701 return results, nil 702 } 703 704 // DestroyFilesystems destroys filesystems. 705 func (s *dummyFilesystemSource) DestroyFilesystems(ctx context.ProviderCallContext, filesystemIds []string) ([]error, error) { 706 if s.provider.destroyFilesystemsFunc != nil { 707 return s.provider.destroyFilesystemsFunc(filesystemIds) 708 } 709 return make([]error, len(filesystemIds)), nil 710 } 711 712 // ReleaseFilesystems destroys filesystems. 713 func (s *dummyFilesystemSource) ReleaseFilesystems(ctx context.ProviderCallContext, filesystemIds []string) ([]error, error) { 714 if s.provider.releaseFilesystemsFunc != nil { 715 return s.provider.releaseFilesystemsFunc(filesystemIds) 716 } 717 return make([]error, len(filesystemIds)), nil 718 } 719 720 // AttachFilesystems attaches filesystems to machines. 721 func (s *dummyFilesystemSource) AttachFilesystems(ctx context.ProviderCallContext, params []storage.FilesystemAttachmentParams) ([]storage.AttachFilesystemsResult, error) { 722 if s.provider != nil && s.provider.attachFilesystemsFunc != nil { 723 return s.provider.attachFilesystemsFunc(params) 724 } 725 726 results := make([]storage.AttachFilesystemsResult, len(params)) 727 for i, p := range params { 728 if p.FilesystemId == "" { 729 panic("AttachFilesystems called with unprovisioned filesystem") 730 } 731 if p.InstanceId == "" { 732 panic("AttachFilesystems called with unprovisioned machine") 733 } 734 results[i].FilesystemAttachment = &storage.FilesystemAttachment{ 735 p.Filesystem, 736 p.Machine, 737 storage.FilesystemAttachmentInfo{ 738 Path: "/srv/" + p.FilesystemId, 739 }, 740 } 741 } 742 return results, nil 743 } 744 745 // DetachFilesystems detaches filesystems from machines. 746 func (s *dummyFilesystemSource) DetachFilesystems(ctx context.ProviderCallContext, params []storage.FilesystemAttachmentParams) ([]error, error) { 747 if s.provider.detachFilesystemsFunc != nil { 748 return s.provider.detachFilesystemsFunc(params) 749 } 750 return make([]error, len(params)), nil 751 } 752 753 type mockManagedFilesystemSource struct { 754 blockDevices map[names.VolumeTag]storage.BlockDevice 755 filesystems map[names.FilesystemTag]storage.Filesystem 756 } 757 758 func (s *mockManagedFilesystemSource) ValidateFilesystemParams(params storage.FilesystemParams) error { 759 return nil 760 } 761 762 func (s *mockManagedFilesystemSource) CreateFilesystems(ctx context.ProviderCallContext, args []storage.FilesystemParams) ([]storage.CreateFilesystemsResult, error) { 763 results := make([]storage.CreateFilesystemsResult, len(args)) 764 for i, arg := range args { 765 blockDevice, ok := s.blockDevices[arg.Volume] 766 if !ok { 767 results[i].Error = errors.Errorf("filesystem %v's backing-volume is not attached", arg.Tag.Id()) 768 continue 769 } 770 results[i].Filesystem = &storage.Filesystem{ 771 Tag: arg.Tag, 772 FilesystemInfo: storage.FilesystemInfo{ 773 Size: blockDevice.Size, 774 FilesystemId: blockDevice.DeviceName, 775 }, 776 } 777 } 778 return results, nil 779 } 780 781 func (s *mockManagedFilesystemSource) DestroyFilesystems(ctx context.ProviderCallContext, filesystemIds []string) ([]error, error) { 782 return make([]error, len(filesystemIds)), nil 783 } 784 785 func (s *mockManagedFilesystemSource) ReleaseFilesystems(ctx context.ProviderCallContext, filesystemIds []string) ([]error, error) { 786 return make([]error, len(filesystemIds)), nil 787 } 788 789 func (s *mockManagedFilesystemSource) AttachFilesystems(ctx context.ProviderCallContext, args []storage.FilesystemAttachmentParams) ([]storage.AttachFilesystemsResult, error) { 790 results := make([]storage.AttachFilesystemsResult, len(args)) 791 for i, arg := range args { 792 if arg.FilesystemId == "" { 793 panic("AttachFilesystems called with unprovisioned filesystem") 794 } 795 if arg.InstanceId == "" { 796 panic("AttachFilesystems called with unprovisioned machine") 797 } 798 filesystem, ok := s.filesystems[arg.Filesystem] 799 if !ok { 800 results[i].Error = errors.Errorf("filesystem %v has not been created", arg.Filesystem.Id()) 801 continue 802 } 803 blockDevice, ok := s.blockDevices[filesystem.Volume] 804 if !ok { 805 results[i].Error = errors.Errorf("filesystem %v's backing-volume is not attached", filesystem.Tag.Id()) 806 continue 807 } 808 results[i].FilesystemAttachment = &storage.FilesystemAttachment{ 809 arg.Filesystem, 810 arg.Machine, 811 storage.FilesystemAttachmentInfo{ 812 Path: "/mnt/" + blockDevice.DeviceName, 813 ReadOnly: arg.ReadOnly, 814 }, 815 } 816 } 817 return results, nil 818 } 819 820 func (s *mockManagedFilesystemSource) DetachFilesystems(ctx context.ProviderCallContext, params []storage.FilesystemAttachmentParams) ([]error, error) { 821 return nil, errors.NotImplementedf("DetachFilesystems") 822 } 823 824 type mockMachineAccessor struct { 825 instanceIds map[names.MachineTag]instance.Id 826 watcher *mockNotifyWatcher 827 } 828 829 func (a *mockMachineAccessor) WatchMachine(names.MachineTag) (watcher.NotifyWatcher, error) { 830 return a.watcher, nil 831 } 832 833 func (a *mockMachineAccessor) InstanceIds(tags []names.MachineTag) ([]params.StringResult, error) { 834 results := make([]params.StringResult, len(tags)) 835 for i, tag := range tags { 836 instanceId, ok := a.instanceIds[tag] 837 if !ok { 838 results[i].Error = ¶ms.Error{Code: params.CodeNotFound} 839 } else if instanceId == "" { 840 results[i].Error = ¶ms.Error{Code: params.CodeNotProvisioned} 841 } else { 842 results[i].Result = string(instanceId) 843 } 844 } 845 return results, nil 846 } 847 848 func newMockMachineAccessor(c *gc.C) *mockMachineAccessor { 849 return &mockMachineAccessor{ 850 instanceIds: make(map[names.MachineTag]instance.Id), 851 watcher: newMockNotifyWatcher(), 852 } 853 } 854 855 type mockClock struct { 856 gitjujutesting.Stub 857 now time.Time 858 onNow func() time.Time 859 onAfter func(time.Duration) <-chan time.Time 860 onAfterFunc func(time.Duration, func()) clock.Timer 861 } 862 863 func (c *mockClock) Now() time.Time { 864 c.MethodCall(c, "Now") 865 if c.onNow != nil { 866 return c.onNow() 867 } 868 return c.now 869 } 870 871 func (c *mockClock) After(d time.Duration) <-chan time.Time { 872 c.MethodCall(c, "After", d) 873 if c.onAfter != nil { 874 return c.onAfter(d) 875 } 876 if d > 0 { 877 c.now = c.now.Add(d) 878 } 879 ch := make(chan time.Time, 1) 880 ch <- c.now 881 return ch 882 } 883 884 func (c *mockClock) NewTimer(d time.Duration) clock.Timer { 885 return mockTimer{time.NewTimer(0)} 886 } 887 888 func (c *mockClock) AfterFunc(d time.Duration, f func()) clock.Timer { 889 c.MethodCall(c, "AfterFunc", d, f) 890 if c.onAfterFunc != nil { 891 return c.onAfterFunc(d, f) 892 } 893 if d > 0 { 894 c.now = c.now.Add(d) 895 } 896 return mockTimer{time.AfterFunc(0, f)} 897 } 898 899 type mockTimer struct { 900 *time.Timer 901 } 902 903 func (t mockTimer) Chan() <-chan time.Time { 904 return t.C 905 } 906 907 type mockStatusSetter struct { 908 args []params.EntityStatusArgs 909 setStatus func([]params.EntityStatusArgs) error 910 } 911 912 func (m *mockStatusSetter) SetStatus(args []params.EntityStatusArgs) error { 913 if m.setStatus != nil { 914 return m.setStatus(args) 915 } 916 m.args = append(m.args, args...) 917 return nil 918 }