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