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