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