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