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