github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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 "strconv" 8 "sync" 9 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 gc "gopkg.in/check.v1" 13 14 apiwatcher "github.com/juju/juju/api/watcher" 15 "github.com/juju/juju/apiserver/common" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/environs/config" 18 "github.com/juju/juju/instance" 19 "github.com/juju/juju/storage" 20 "github.com/juju/juju/testing" 21 ) 22 23 const attachedVolumeId = "1" 24 const needsInstanceVolumeId = "23" 25 26 var dyingVolumeAttachmentId = params.MachineStorageId{ 27 MachineTag: "machine-0", 28 AttachmentTag: "volume-0", 29 } 30 31 var dyingFilesystemAttachmentId = params.MachineStorageId{ 32 MachineTag: "machine-0", 33 AttachmentTag: "filesystem-0", 34 } 35 36 var missingVolumeAttachmentId = params.MachineStorageId{ 37 MachineTag: "machine-3", 38 AttachmentTag: "volume-1", 39 } 40 41 type mockNotifyWatcher struct { 42 changes chan struct{} 43 } 44 45 func (*mockNotifyWatcher) Stop() error { 46 return nil 47 } 48 49 func (*mockNotifyWatcher) Err() error { 50 return nil 51 } 52 53 func (w *mockNotifyWatcher) Changes() <-chan struct{} { 54 return w.changes 55 } 56 57 type mockStringsWatcher struct { 58 changes chan []string 59 } 60 61 func (*mockStringsWatcher) Stop() error { 62 return nil 63 } 64 65 func (*mockStringsWatcher) Err() error { 66 return nil 67 } 68 69 func (w *mockStringsWatcher) Changes() <-chan []string { 70 return w.changes 71 } 72 73 type mockAttachmentsWatcher struct { 74 changes chan []params.MachineStorageId 75 } 76 77 func (*mockAttachmentsWatcher) Stop() error { 78 return nil 79 } 80 81 func (*mockAttachmentsWatcher) Err() error { 82 return nil 83 } 84 85 func (w *mockAttachmentsWatcher) Changes() <-chan []params.MachineStorageId { 86 return w.changes 87 } 88 89 type mockEnvironAccessor struct { 90 watcher *mockNotifyWatcher 91 mu sync.Mutex 92 cfg *config.Config 93 } 94 95 func (e *mockEnvironAccessor) WatchForEnvironConfigChanges() (apiwatcher.NotifyWatcher, error) { 96 return e.watcher, nil 97 } 98 99 func (e *mockEnvironAccessor) EnvironConfig() (*config.Config, error) { 100 e.mu.Lock() 101 cfg := e.cfg 102 e.mu.Unlock() 103 return cfg, nil 104 } 105 106 func (e *mockEnvironAccessor) setConfig(cfg *config.Config) { 107 e.mu.Lock() 108 e.cfg = cfg 109 e.mu.Unlock() 110 } 111 112 func newMockEnvironAccessor(c *gc.C) *mockEnvironAccessor { 113 return &mockEnvironAccessor{ 114 watcher: &mockNotifyWatcher{make(chan struct{}, 1)}, 115 cfg: testing.EnvironConfig(c), 116 } 117 } 118 119 type mockVolumeAccessor struct { 120 volumesWatcher *mockStringsWatcher 121 attachmentsWatcher *mockAttachmentsWatcher 122 blockDevicesWatcher *mockNotifyWatcher 123 provisionedMachines map[string]instance.Id 124 provisionedVolumes map[string]params.Volume 125 provisionedAttachments map[params.MachineStorageId]params.VolumeAttachment 126 blockDevices map[params.MachineStorageId]storage.BlockDevice 127 128 setVolumeInfo func([]params.Volume) ([]params.ErrorResult, error) 129 setVolumeAttachmentInfo func([]params.VolumeAttachment) ([]params.ErrorResult, error) 130 } 131 132 func (m *mockVolumeAccessor) provisionVolume(tag names.VolumeTag) params.Volume { 133 v := params.Volume{ 134 VolumeTag: tag.String(), 135 Info: params.VolumeInfo{ 136 VolumeId: "vol-" + tag.Id(), 137 }, 138 } 139 m.provisionedVolumes[tag.String()] = v 140 return v 141 } 142 143 func (w *mockVolumeAccessor) WatchVolumes() (apiwatcher.StringsWatcher, error) { 144 return w.volumesWatcher, nil 145 } 146 147 func (w *mockVolumeAccessor) WatchVolumeAttachments() (apiwatcher.MachineStorageIdsWatcher, error) { 148 return w.attachmentsWatcher, nil 149 } 150 151 func (w *mockVolumeAccessor) WatchBlockDevices(tag names.MachineTag) (apiwatcher.NotifyWatcher, error) { 152 return w.blockDevicesWatcher, nil 153 } 154 155 func (v *mockVolumeAccessor) Volumes(volumes []names.VolumeTag) ([]params.VolumeResult, error) { 156 var result []params.VolumeResult 157 for _, tag := range volumes { 158 if vol, ok := v.provisionedVolumes[tag.String()]; ok { 159 result = append(result, params.VolumeResult{Result: vol}) 160 } else { 161 result = append(result, params.VolumeResult{ 162 Error: common.ServerError(errors.NotProvisionedf("volume %q", tag.Id())), 163 }) 164 } 165 } 166 return result, nil 167 } 168 169 func (v *mockVolumeAccessor) VolumeAttachments(ids []params.MachineStorageId) ([]params.VolumeAttachmentResult, error) { 170 var result []params.VolumeAttachmentResult 171 for _, id := range ids { 172 if att, ok := v.provisionedAttachments[id]; ok { 173 result = append(result, params.VolumeAttachmentResult{Result: att}) 174 } else { 175 result = append(result, params.VolumeAttachmentResult{ 176 Error: common.ServerError(errors.NotProvisionedf("volume attachment %v", id)), 177 }) 178 } 179 } 180 return result, nil 181 } 182 183 func (v *mockVolumeAccessor) VolumeBlockDevices(ids []params.MachineStorageId) ([]params.BlockDeviceResult, error) { 184 var result []params.BlockDeviceResult 185 for _, id := range ids { 186 if dev, ok := v.blockDevices[id]; ok { 187 result = append(result, params.BlockDeviceResult{Result: dev}) 188 } else { 189 result = append(result, params.BlockDeviceResult{ 190 Error: common.ServerError(errors.NotFoundf("block device for volume attachment %v", id)), 191 }) 192 } 193 } 194 return result, nil 195 } 196 197 func (v *mockVolumeAccessor) VolumeParams(volumes []names.VolumeTag) ([]params.VolumeParamsResult, error) { 198 var result []params.VolumeParamsResult 199 for _, tag := range volumes { 200 // Parameters are returned regardless of whether the volume 201 // exists; this is to support destruction. 202 volumeParams := params.VolumeParams{ 203 VolumeTag: tag.String(), 204 Size: 1024, 205 Provider: "dummy", 206 Attributes: map[string]interface{}{ 207 "persistent": tag.String() == "volume-1", 208 }, 209 Tags: map[string]string{ 210 "very": "fancy", 211 }, 212 } 213 volumeParams.Attachment = ¶ms.VolumeAttachmentParams{ 214 VolumeTag: tag.String(), 215 MachineTag: "machine-1", 216 InstanceId: string(v.provisionedMachines["machine-1"]), 217 Provider: "dummy", 218 ReadOnly: tag.String() == "volume-1", 219 } 220 result = append(result, params.VolumeParamsResult{Result: volumeParams}) 221 } 222 return result, nil 223 } 224 225 func (v *mockVolumeAccessor) VolumeAttachmentParams(ids []params.MachineStorageId) ([]params.VolumeAttachmentParamsResult, error) { 226 var result []params.VolumeAttachmentParamsResult 227 for _, id := range ids { 228 // Parameters are returned regardless of whether the attachment 229 // exists; this is to support reattachment. 230 instanceId, _ := v.provisionedMachines[id.MachineTag] 231 result = append(result, params.VolumeAttachmentParamsResult{Result: params.VolumeAttachmentParams{ 232 MachineTag: id.MachineTag, 233 VolumeTag: id.AttachmentTag, 234 InstanceId: string(instanceId), 235 Provider: "dummy", 236 ReadOnly: id.AttachmentTag == "volume-1", 237 }}) 238 } 239 return result, nil 240 } 241 242 func (v *mockVolumeAccessor) SetVolumeInfo(volumes []params.Volume) ([]params.ErrorResult, error) { 243 return v.setVolumeInfo(volumes) 244 } 245 246 func (v *mockVolumeAccessor) SetVolumeAttachmentInfo(volumeAttachments []params.VolumeAttachment) ([]params.ErrorResult, error) { 247 if v.setVolumeAttachmentInfo != nil { 248 return v.setVolumeAttachmentInfo(volumeAttachments) 249 } 250 return nil, nil 251 } 252 253 func newMockVolumeAccessor() *mockVolumeAccessor { 254 return &mockVolumeAccessor{ 255 volumesWatcher: &mockStringsWatcher{make(chan []string, 1)}, 256 attachmentsWatcher: &mockAttachmentsWatcher{make(chan []params.MachineStorageId, 1)}, 257 blockDevicesWatcher: &mockNotifyWatcher{make(chan struct{}, 1)}, 258 provisionedMachines: make(map[string]instance.Id), 259 provisionedVolumes: make(map[string]params.Volume), 260 provisionedAttachments: make(map[params.MachineStorageId]params.VolumeAttachment), 261 blockDevices: make(map[params.MachineStorageId]storage.BlockDevice), 262 } 263 } 264 265 type mockFilesystemAccessor struct { 266 filesystemsWatcher *mockStringsWatcher 267 attachmentsWatcher *mockAttachmentsWatcher 268 provisionedMachines map[string]instance.Id 269 provisionedFilesystems map[string]params.Filesystem 270 provisionedAttachments map[params.MachineStorageId]params.FilesystemAttachment 271 272 setFilesystemInfo func([]params.Filesystem) ([]params.ErrorResult, error) 273 setFilesystemAttachmentInfo func([]params.FilesystemAttachment) ([]params.ErrorResult, error) 274 } 275 276 func (m *mockFilesystemAccessor) provisionFilesystem(tag names.FilesystemTag) params.Filesystem { 277 f := params.Filesystem{ 278 FilesystemTag: tag.String(), 279 Info: params.FilesystemInfo{ 280 FilesystemId: "vol-" + tag.Id(), 281 }, 282 } 283 m.provisionedFilesystems[tag.String()] = f 284 return f 285 } 286 287 func (w *mockFilesystemAccessor) WatchFilesystems() (apiwatcher.StringsWatcher, error) { 288 return w.filesystemsWatcher, nil 289 } 290 291 func (w *mockFilesystemAccessor) WatchFilesystemAttachments() (apiwatcher.MachineStorageIdsWatcher, error) { 292 return w.attachmentsWatcher, nil 293 } 294 295 func (v *mockFilesystemAccessor) Filesystems(filesystems []names.FilesystemTag) ([]params.FilesystemResult, error) { 296 var result []params.FilesystemResult 297 for _, tag := range filesystems { 298 if vol, ok := v.provisionedFilesystems[tag.String()]; ok { 299 result = append(result, params.FilesystemResult{Result: vol}) 300 } else { 301 result = append(result, params.FilesystemResult{ 302 Error: common.ServerError(errors.NotProvisionedf("filesystem %q", tag.Id())), 303 }) 304 } 305 } 306 return result, nil 307 } 308 309 func (v *mockFilesystemAccessor) FilesystemAttachments(ids []params.MachineStorageId) ([]params.FilesystemAttachmentResult, error) { 310 var result []params.FilesystemAttachmentResult 311 for _, id := range ids { 312 if att, ok := v.provisionedAttachments[id]; ok { 313 result = append(result, params.FilesystemAttachmentResult{Result: att}) 314 } else { 315 result = append(result, params.FilesystemAttachmentResult{ 316 Error: common.ServerError(errors.NotProvisionedf("filesystem attachment %v", id)), 317 }) 318 } 319 } 320 return result, nil 321 } 322 323 func (v *mockFilesystemAccessor) FilesystemParams(filesystems []names.FilesystemTag) ([]params.FilesystemParamsResult, error) { 324 var result []params.FilesystemParamsResult 325 for _, tag := range filesystems { 326 if _, ok := v.provisionedFilesystems[tag.String()]; ok { 327 result = append(result, params.FilesystemParamsResult{ 328 Error: ¶ms.Error{Message: "already provisioned"}, 329 }) 330 } else { 331 filesystemParams := params.FilesystemParams{ 332 FilesystemTag: tag.String(), 333 Size: 1024, 334 Provider: "dummy", 335 Tags: map[string]string{ 336 "very": "fancy", 337 }, 338 } 339 if _, ok := names.FilesystemMachine(tag); ok { 340 // place all volume-backed filesystems on machine-scoped 341 // volumes with the same ID as the filesystem. 342 filesystemParams.VolumeTag = names.NewVolumeTag(tag.Id()).String() 343 } 344 result = append(result, params.FilesystemParamsResult{Result: filesystemParams}) 345 } 346 } 347 return result, nil 348 } 349 350 func (f *mockFilesystemAccessor) FilesystemAttachmentParams(ids []params.MachineStorageId) ([]params.FilesystemAttachmentParamsResult, error) { 351 var result []params.FilesystemAttachmentParamsResult 352 for _, id := range ids { 353 // Parameters are returned regardless of whether the attachment 354 // exists; this is to support reattachment. 355 instanceId := f.provisionedMachines[id.MachineTag] 356 result = append(result, params.FilesystemAttachmentParamsResult{Result: params.FilesystemAttachmentParams{ 357 MachineTag: id.MachineTag, 358 FilesystemTag: id.AttachmentTag, 359 InstanceId: string(instanceId), 360 Provider: "dummy", 361 ReadOnly: true, 362 }}) 363 } 364 return result, nil 365 } 366 367 func (f *mockFilesystemAccessor) SetFilesystemInfo(filesystems []params.Filesystem) ([]params.ErrorResult, error) { 368 return f.setFilesystemInfo(filesystems) 369 } 370 371 func (f *mockFilesystemAccessor) SetFilesystemAttachmentInfo(filesystemAttachments []params.FilesystemAttachment) ([]params.ErrorResult, error) { 372 if f.setFilesystemAttachmentInfo != nil { 373 return f.setFilesystemAttachmentInfo(filesystemAttachments) 374 } 375 return nil, nil 376 } 377 378 func newMockFilesystemAccessor() *mockFilesystemAccessor { 379 return &mockFilesystemAccessor{ 380 filesystemsWatcher: &mockStringsWatcher{make(chan []string, 1)}, 381 attachmentsWatcher: &mockAttachmentsWatcher{make(chan []params.MachineStorageId, 1)}, 382 provisionedMachines: make(map[string]instance.Id), 383 provisionedFilesystems: make(map[string]params.Filesystem), 384 provisionedAttachments: make(map[params.MachineStorageId]params.FilesystemAttachment), 385 } 386 } 387 388 type mockLifecycleManager struct { 389 life func([]names.Tag) ([]params.LifeResult, error) 390 attachmentLife func(ids []params.MachineStorageId) ([]params.LifeResult, error) 391 removeAttachments func([]params.MachineStorageId) ([]params.ErrorResult, error) 392 remove func([]names.Tag) ([]params.ErrorResult, error) 393 } 394 395 func (m *mockLifecycleManager) Life(tags []names.Tag) ([]params.LifeResult, error) { 396 if m.life != nil { 397 return m.life(tags) 398 } 399 var result []params.LifeResult 400 for _, tag := range tags { 401 id, _ := strconv.Atoi(tag.Id()) 402 if id <= 100 { 403 result = append(result, params.LifeResult{Life: params.Alive}) 404 } else { 405 result = append(result, params.LifeResult{Life: params.Dying}) 406 } 407 } 408 return result, nil 409 } 410 411 func (m *mockLifecycleManager) AttachmentLife(ids []params.MachineStorageId) ([]params.LifeResult, error) { 412 if m.attachmentLife != nil { 413 return m.attachmentLife(ids) 414 } 415 var result []params.LifeResult 416 for _, id := range ids { 417 switch id { 418 case dyingVolumeAttachmentId, dyingFilesystemAttachmentId: 419 result = append(result, params.LifeResult{Life: params.Dying}) 420 case missingVolumeAttachmentId: 421 result = append(result, params.LifeResult{ 422 Error: common.ServerError(errors.NotFoundf("attachment %v", id)), 423 }) 424 default: 425 result = append(result, params.LifeResult{Life: params.Alive}) 426 } 427 } 428 return result, nil 429 } 430 431 func (m *mockLifecycleManager) Remove(tags []names.Tag) ([]params.ErrorResult, error) { 432 if m.remove != nil { 433 return m.remove(tags) 434 } 435 return make([]params.ErrorResult, len(tags)), nil 436 } 437 438 func (m *mockLifecycleManager) RemoveAttachments(ids []params.MachineStorageId) ([]params.ErrorResult, error) { 439 if m.removeAttachments != nil { 440 return m.removeAttachments(ids) 441 } 442 return make([]params.ErrorResult, len(ids)), nil 443 } 444 445 // Set up a dummy storage provider so we can stub out volume creation. 446 type dummyProvider struct { 447 storage.Provider 448 dynamic bool 449 450 volumeSourceFunc func(*config.Config, *storage.Config) (storage.VolumeSource, error) 451 filesystemSourceFunc func(*config.Config, *storage.Config) (storage.FilesystemSource, error) 452 detachVolumesFunc func([]storage.VolumeAttachmentParams) error 453 detachFilesystemsFunc func([]storage.FilesystemAttachmentParams) error 454 destroyVolumesFunc func([]string) []error 455 } 456 457 type dummyVolumeSource struct { 458 storage.VolumeSource 459 provider *dummyProvider 460 createVolumesArgs [][]storage.VolumeParams 461 } 462 463 type dummyFilesystemSource struct { 464 storage.FilesystemSource 465 provider *dummyProvider 466 createFilesystemsArgs [][]storage.FilesystemParams 467 } 468 469 func (p *dummyProvider) VolumeSource(environConfig *config.Config, providerConfig *storage.Config) (storage.VolumeSource, error) { 470 if p.volumeSourceFunc != nil { 471 return p.volumeSourceFunc(environConfig, providerConfig) 472 } 473 return &dummyVolumeSource{provider: p}, nil 474 } 475 476 func (p *dummyProvider) FilesystemSource(environConfig *config.Config, providerConfig *storage.Config) (storage.FilesystemSource, error) { 477 if p.filesystemSourceFunc != nil { 478 return p.filesystemSourceFunc(environConfig, providerConfig) 479 } 480 return &dummyFilesystemSource{provider: p}, nil 481 } 482 483 func (p *dummyProvider) Dynamic() bool { 484 return p.dynamic 485 } 486 487 func (*dummyVolumeSource) ValidateVolumeParams(params storage.VolumeParams) error { 488 return nil 489 } 490 491 // CreateVolumes makes some volumes that we can check later to ensure things went as expected. 492 func (s *dummyVolumeSource) CreateVolumes(params []storage.VolumeParams) ([]storage.Volume, []storage.VolumeAttachment, error) { 493 paramsCopy := make([]storage.VolumeParams, len(params)) 494 copy(paramsCopy, params) 495 s.createVolumesArgs = append(s.createVolumesArgs, paramsCopy) 496 497 var volumes []storage.Volume 498 var volumeAttachments []storage.VolumeAttachment 499 for _, p := range params { 500 persistent, _ := p.Attributes["persistent"].(bool) 501 volumes = append(volumes, storage.Volume{ 502 p.Tag, 503 storage.VolumeInfo{ 504 Size: p.Size, 505 HardwareId: "serial-" + p.Tag.Id(), 506 VolumeId: "id-" + p.Tag.Id(), 507 Persistent: persistent, 508 }, 509 }) 510 } 511 return volumes, volumeAttachments, nil 512 } 513 514 // DestroyVolumes destroys volumes. 515 func (s *dummyVolumeSource) DestroyVolumes(volumeIds []string) []error { 516 if s.provider.destroyVolumesFunc != nil { 517 return s.provider.destroyVolumesFunc(volumeIds) 518 } 519 return make([]error, len(volumeIds)) 520 } 521 522 // AttachVolumes attaches volumes to machines. 523 func (*dummyVolumeSource) AttachVolumes(params []storage.VolumeAttachmentParams) ([]storage.VolumeAttachment, error) { 524 var volumeAttachments []storage.VolumeAttachment 525 for _, p := range params { 526 if p.VolumeId == "" { 527 panic("AttachVolumes called with unprovisioned volume") 528 } 529 if p.InstanceId == "" { 530 panic("AttachVolumes called with unprovisioned machine") 531 } 532 volumeAttachments = append(volumeAttachments, storage.VolumeAttachment{ 533 p.Volume, 534 p.Machine, 535 storage.VolumeAttachmentInfo{ 536 DeviceName: "/dev/sda" + p.Volume.Id(), 537 ReadOnly: p.ReadOnly, 538 }, 539 }) 540 } 541 return volumeAttachments, nil 542 } 543 544 // DetachVolumes detaches volumes from machines. 545 func (s *dummyVolumeSource) DetachVolumes(params []storage.VolumeAttachmentParams) error { 546 if s.provider.detachVolumesFunc != nil { 547 return s.provider.detachVolumesFunc(params) 548 } 549 return nil 550 } 551 552 func (*dummyFilesystemSource) ValidateFilesystemParams(params storage.FilesystemParams) error { 553 return nil 554 } 555 556 // CreateFilesystems makes some filesystems that we can check later to ensure things went as expected. 557 func (s *dummyFilesystemSource) CreateFilesystems(params []storage.FilesystemParams) ([]storage.Filesystem, error) { 558 paramsCopy := make([]storage.FilesystemParams, len(params)) 559 copy(paramsCopy, params) 560 s.createFilesystemsArgs = append(s.createFilesystemsArgs, paramsCopy) 561 562 var filesystems []storage.Filesystem 563 for _, p := range params { 564 filesystems = append(filesystems, storage.Filesystem{ 565 Tag: p.Tag, 566 FilesystemInfo: storage.FilesystemInfo{ 567 Size: p.Size, 568 FilesystemId: "id-" + p.Tag.Id(), 569 }, 570 }) 571 } 572 return filesystems, nil 573 } 574 575 // AttachFilesystems attaches filesystems to machines. 576 func (*dummyFilesystemSource) AttachFilesystems(params []storage.FilesystemAttachmentParams) ([]storage.FilesystemAttachment, error) { 577 var filesystemAttachments []storage.FilesystemAttachment 578 for _, p := range params { 579 if p.FilesystemId == "" { 580 panic("AttachFilesystems called with unprovisioned filesystem") 581 } 582 if p.InstanceId == "" { 583 panic("AttachFilesystems called with unprovisioned machine") 584 } 585 filesystemAttachments = append(filesystemAttachments, storage.FilesystemAttachment{ 586 p.Filesystem, 587 p.Machine, 588 storage.FilesystemAttachmentInfo{ 589 Path: "/srv/" + p.FilesystemId, 590 }, 591 }) 592 } 593 return filesystemAttachments, nil 594 } 595 596 // DetachFilesystems detaches filesystems from machines. 597 func (s *dummyFilesystemSource) DetachFilesystems(params []storage.FilesystemAttachmentParams) error { 598 if s.provider.detachFilesystemsFunc != nil { 599 return s.provider.detachFilesystemsFunc(params) 600 } 601 return nil 602 } 603 604 type mockManagedFilesystemSource struct { 605 blockDevices map[names.VolumeTag]storage.BlockDevice 606 filesystems map[names.FilesystemTag]storage.Filesystem 607 } 608 609 func (s *mockManagedFilesystemSource) ValidateFilesystemParams(params storage.FilesystemParams) error { 610 return nil 611 } 612 613 func (s *mockManagedFilesystemSource) CreateFilesystems(args []storage.FilesystemParams) ([]storage.Filesystem, error) { 614 var filesystems []storage.Filesystem 615 for _, arg := range args { 616 blockDevice, ok := s.blockDevices[arg.Volume] 617 if !ok { 618 return nil, errors.Errorf("filesystem %v's backing-volume is not attached", arg.Tag.Id()) 619 } 620 filesystems = append(filesystems, storage.Filesystem{ 621 Tag: arg.Tag, 622 FilesystemInfo: storage.FilesystemInfo{ 623 Size: blockDevice.Size, 624 FilesystemId: blockDevice.DeviceName, 625 }, 626 }) 627 } 628 return filesystems, nil 629 } 630 631 func (s *mockManagedFilesystemSource) DestroyFilesystems(filesystemIds []string) []error { 632 return make([]error, len(filesystemIds)) 633 } 634 635 func (s *mockManagedFilesystemSource) AttachFilesystems(args []storage.FilesystemAttachmentParams) ([]storage.FilesystemAttachment, error) { 636 var filesystemAttachments []storage.FilesystemAttachment 637 for _, arg := range args { 638 if arg.FilesystemId == "" { 639 panic("AttachFilesystems called with unprovisioned filesystem") 640 } 641 if arg.InstanceId == "" { 642 panic("AttachFilesystems called with unprovisioned machine") 643 } 644 filesystem, ok := s.filesystems[arg.Filesystem] 645 if !ok { 646 return nil, errors.Errorf("filesystem %v has not been created", arg.Filesystem.Id()) 647 } 648 blockDevice, ok := s.blockDevices[filesystem.Volume] 649 if !ok { 650 return nil, errors.Errorf("filesystem %v's backing-volume is not attached", filesystem.Tag.Id()) 651 } 652 filesystemAttachments = append(filesystemAttachments, storage.FilesystemAttachment{ 653 arg.Filesystem, 654 arg.Machine, 655 storage.FilesystemAttachmentInfo{ 656 Path: "/mnt/" + blockDevice.DeviceName, 657 ReadOnly: arg.ReadOnly, 658 }, 659 }) 660 } 661 return filesystemAttachments, nil 662 } 663 664 func (s *mockManagedFilesystemSource) DetachFilesystems(params []storage.FilesystemAttachmentParams) error { 665 return errors.NotImplementedf("DetachFilesystems") 666 } 667 668 type mockMachineAccessor struct { 669 instanceIds map[names.MachineTag]instance.Id 670 watcher *mockNotifyWatcher 671 } 672 673 func (a *mockMachineAccessor) WatchMachine(names.MachineTag) (apiwatcher.NotifyWatcher, error) { 674 return a.watcher, nil 675 } 676 677 func (a *mockMachineAccessor) InstanceIds(tags []names.MachineTag) ([]params.StringResult, error) { 678 results := make([]params.StringResult, len(tags)) 679 for i, tag := range tags { 680 instanceId, ok := a.instanceIds[tag] 681 if !ok { 682 results[i].Error = ¶ms.Error{Code: params.CodeNotFound} 683 } else if instanceId == "" { 684 results[i].Error = ¶ms.Error{Code: params.CodeNotProvisioned} 685 } else { 686 results[i].Result = string(instanceId) 687 } 688 } 689 return results, nil 690 } 691 692 func newMockMachineAccessor(c *gc.C) *mockMachineAccessor { 693 return &mockMachineAccessor{ 694 instanceIds: make(map[names.MachineTag]instance.Id), 695 watcher: &mockNotifyWatcher{make(chan struct{}, 1)}, 696 } 697 }