github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/storageprovisioner/mock_test.go (about)

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