github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/storageprovisioner/mock_test.go (about)

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