github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/storageprovisioner/mock_test.go (about)

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