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 = &params.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 = &params.Error{Code: params.CodeNotFound}
   696  		} else if instanceId == "" {
   697  			results[i].Error = &params.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  }