github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/uniter/storage_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter_test
     5  
     6  import (
     7  	"errors"
     8  
     9  	"github.com/juju/names"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	"launchpad.net/tomb"
    13  
    14  	"github.com/juju/juju/apiserver/common"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/apiserver/uniter"
    17  	"github.com/juju/juju/state"
    18  	"github.com/juju/juju/testing"
    19  )
    20  
    21  type storageSuite struct {
    22  	testing.BaseSuite
    23  	called []string
    24  }
    25  
    26  var _ = gc.Suite(&storageSuite{})
    27  
    28  func (s *storageSuite) TestWatchUnitStorageAttachments(c *gc.C) {
    29  	resources := common.NewResources()
    30  	getCanAccess := func() (common.AuthFunc, error) {
    31  		return func(names.Tag) bool {
    32  			return true
    33  		}, nil
    34  	}
    35  	unitTag := names.NewUnitTag("mysql/0")
    36  	watcher := &mockStringsWatcher{
    37  		changes: make(chan []string, 1),
    38  	}
    39  	watcher.changes <- []string{"storage/0", "storage/1"}
    40  	state := &mockStorageState{
    41  		watchStorageAttachments: func(u names.UnitTag) state.StringsWatcher {
    42  			c.Assert(u, gc.DeepEquals, unitTag)
    43  			return watcher
    44  		},
    45  	}
    46  
    47  	storage, err := uniter.NewStorageAPI(state, resources, getCanAccess)
    48  	c.Assert(err, jc.ErrorIsNil)
    49  	watches, err := storage.WatchUnitStorageAttachments(params.Entities{
    50  		Entities: []params.Entity{{unitTag.String()}},
    51  	})
    52  	c.Assert(err, jc.ErrorIsNil)
    53  	c.Assert(watches, gc.DeepEquals, params.StringsWatchResults{
    54  		Results: []params.StringsWatchResult{{
    55  			StringsWatcherId: "1",
    56  			Changes:          []string{"storage/0", "storage/1"},
    57  		}},
    58  	})
    59  	c.Assert(resources.Get("1"), gc.Equals, watcher)
    60  }
    61  
    62  func (s *storageSuite) TestWatchStorageAttachmentVolume(c *gc.C) {
    63  	resources := common.NewResources()
    64  	getCanAccess := func() (common.AuthFunc, error) {
    65  		return func(names.Tag) bool {
    66  			return true
    67  		}, nil
    68  	}
    69  	unitTag := names.NewUnitTag("mysql/0")
    70  	storageTag := names.NewStorageTag("data/0")
    71  	machineTag := names.NewMachineTag("66")
    72  	volumeTag := names.NewVolumeTag("104")
    73  	volume := &mockVolume{tag: volumeTag}
    74  	storageInstance := &mockStorageInstance{kind: state.StorageKindBlock}
    75  	storageWatcher := &mockNotifyWatcher{
    76  		changes: make(chan struct{}, 1),
    77  	}
    78  	storageWatcher.changes <- struct{}{}
    79  	volumeWatcher := &mockNotifyWatcher{
    80  		changes: make(chan struct{}, 1),
    81  	}
    82  	volumeWatcher.changes <- struct{}{}
    83  	blockDevicesWatcher := &mockNotifyWatcher{
    84  		changes: make(chan struct{}, 1),
    85  	}
    86  	blockDevicesWatcher.changes <- struct{}{}
    87  	var calls []string
    88  	state := &mockStorageState{
    89  		storageInstance: func(s names.StorageTag) (state.StorageInstance, error) {
    90  			calls = append(calls, "StorageInstance")
    91  			c.Assert(s, gc.DeepEquals, storageTag)
    92  			return storageInstance, nil
    93  		},
    94  		storageInstanceVolume: func(s names.StorageTag) (state.Volume, error) {
    95  			calls = append(calls, "StorageInstanceVolume")
    96  			c.Assert(s, gc.DeepEquals, storageTag)
    97  			return volume, nil
    98  		},
    99  		unitAssignedMachine: func(u names.UnitTag) (names.MachineTag, error) {
   100  			calls = append(calls, "UnitAssignedMachine")
   101  			c.Assert(u, gc.DeepEquals, unitTag)
   102  			return machineTag, nil
   103  		},
   104  		watchStorageAttachment: func(s names.StorageTag, u names.UnitTag) state.NotifyWatcher {
   105  			calls = append(calls, "WatchStorageAttachment")
   106  			c.Assert(s, gc.DeepEquals, storageTag)
   107  			c.Assert(u, gc.DeepEquals, unitTag)
   108  			return storageWatcher
   109  		},
   110  		watchVolumeAttachment: func(m names.MachineTag, v names.VolumeTag) state.NotifyWatcher {
   111  			calls = append(calls, "WatchVolumeAttachment")
   112  			c.Assert(m, gc.DeepEquals, machineTag)
   113  			c.Assert(v, gc.DeepEquals, volumeTag)
   114  			return volumeWatcher
   115  		},
   116  		watchBlockDevices: func(m names.MachineTag) state.NotifyWatcher {
   117  			calls = append(calls, "WatchBlockDevices")
   118  			c.Assert(m, gc.DeepEquals, machineTag)
   119  			return blockDevicesWatcher
   120  		},
   121  	}
   122  
   123  	storage, err := uniter.NewStorageAPI(state, resources, getCanAccess)
   124  	c.Assert(err, jc.ErrorIsNil)
   125  	watches, err := storage.WatchStorageAttachments(params.StorageAttachmentIds{
   126  		Ids: []params.StorageAttachmentId{{
   127  			StorageTag: storageTag.String(),
   128  			UnitTag:    unitTag.String(),
   129  		}},
   130  	})
   131  	c.Assert(err, jc.ErrorIsNil)
   132  	c.Assert(watches, gc.DeepEquals, params.NotifyWatchResults{
   133  		Results: []params.NotifyWatchResult{{
   134  			NotifyWatcherId: "1",
   135  		}},
   136  	})
   137  	c.Assert(calls, gc.DeepEquals, []string{
   138  		"UnitAssignedMachine",
   139  		"StorageInstance",
   140  		"StorageInstanceVolume",
   141  		"WatchVolumeAttachment",
   142  		"WatchBlockDevices",
   143  		"WatchStorageAttachment",
   144  	})
   145  }
   146  
   147  func (s *storageSuite) TestWatchStorageAttachmentFilesystem(c *gc.C) {
   148  	resources := common.NewResources()
   149  	getCanAccess := func() (common.AuthFunc, error) {
   150  		return func(names.Tag) bool {
   151  			return true
   152  		}, nil
   153  	}
   154  	unitTag := names.NewUnitTag("mysql/0")
   155  	storageTag := names.NewStorageTag("data/0")
   156  	machineTag := names.NewMachineTag("66")
   157  	filesystemTag := names.NewFilesystemTag("104")
   158  	filesystem := &mockFilesystem{tag: filesystemTag}
   159  	storageInstance := &mockStorageInstance{kind: state.StorageKindFilesystem}
   160  	storageWatcher := &mockNotifyWatcher{
   161  		changes: make(chan struct{}, 1),
   162  	}
   163  	storageWatcher.changes <- struct{}{}
   164  	filesystemWatcher := &mockNotifyWatcher{
   165  		changes: make(chan struct{}, 1),
   166  	}
   167  	filesystemWatcher.changes <- struct{}{}
   168  	var calls []string
   169  	state := &mockStorageState{
   170  		storageInstance: func(s names.StorageTag) (state.StorageInstance, error) {
   171  			calls = append(calls, "StorageInstance")
   172  			c.Assert(s, gc.DeepEquals, storageTag)
   173  			return storageInstance, nil
   174  		},
   175  		storageInstanceFilesystem: func(s names.StorageTag) (state.Filesystem, error) {
   176  			calls = append(calls, "StorageInstanceFilesystem")
   177  			c.Assert(s, gc.DeepEquals, storageTag)
   178  			return filesystem, nil
   179  		},
   180  		unitAssignedMachine: func(u names.UnitTag) (names.MachineTag, error) {
   181  			calls = append(calls, "UnitAssignedMachine")
   182  			c.Assert(u, gc.DeepEquals, unitTag)
   183  			return machineTag, nil
   184  		},
   185  		watchStorageAttachment: func(s names.StorageTag, u names.UnitTag) state.NotifyWatcher {
   186  			calls = append(calls, "WatchStorageAttachment")
   187  			c.Assert(s, gc.DeepEquals, storageTag)
   188  			c.Assert(u, gc.DeepEquals, unitTag)
   189  			return storageWatcher
   190  		},
   191  		watchFilesystemAttachment: func(m names.MachineTag, f names.FilesystemTag) state.NotifyWatcher {
   192  			calls = append(calls, "WatchFilesystemAttachment")
   193  			c.Assert(m, gc.DeepEquals, machineTag)
   194  			c.Assert(f, gc.DeepEquals, filesystemTag)
   195  			return filesystemWatcher
   196  		},
   197  	}
   198  
   199  	storage, err := uniter.NewStorageAPI(state, resources, getCanAccess)
   200  	c.Assert(err, jc.ErrorIsNil)
   201  	watches, err := storage.WatchStorageAttachments(params.StorageAttachmentIds{
   202  		Ids: []params.StorageAttachmentId{{
   203  			StorageTag: storageTag.String(),
   204  			UnitTag:    unitTag.String(),
   205  		}},
   206  	})
   207  	c.Assert(err, jc.ErrorIsNil)
   208  	c.Assert(watches, gc.DeepEquals, params.NotifyWatchResults{
   209  		Results: []params.NotifyWatchResult{{
   210  			NotifyWatcherId: "1",
   211  		}},
   212  	})
   213  	c.Assert(calls, gc.DeepEquals, []string{
   214  		"UnitAssignedMachine",
   215  		"StorageInstance",
   216  		"StorageInstanceFilesystem",
   217  		"WatchFilesystemAttachment",
   218  		"WatchStorageAttachment",
   219  	})
   220  }
   221  
   222  func (s *storageSuite) TestDestroyUnitStorageAttachments(c *gc.C) {
   223  	resources := common.NewResources()
   224  	getCanAccess := func() (common.AuthFunc, error) {
   225  		return func(names.Tag) bool {
   226  			return true
   227  		}, nil
   228  	}
   229  	unitTag := names.NewUnitTag("mysql/0")
   230  	var calls []string
   231  	state := &mockStorageState{
   232  		destroyUnitStorageAttachments: func(u names.UnitTag) error {
   233  			calls = append(calls, "DestroyUnitStorageAttachments")
   234  			c.Assert(u, gc.DeepEquals, unitTag)
   235  			return nil
   236  		},
   237  	}
   238  
   239  	storage, err := uniter.NewStorageAPI(state, resources, getCanAccess)
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	errors, err := storage.DestroyUnitStorageAttachments(params.Entities{
   242  		Entities: []params.Entity{{
   243  			Tag: unitTag.String(),
   244  		}},
   245  	})
   246  	c.Assert(err, jc.ErrorIsNil)
   247  	c.Assert(calls, jc.DeepEquals, []string{"DestroyUnitStorageAttachments"})
   248  	c.Assert(errors, jc.DeepEquals, params.ErrorResults{
   249  		[]params.ErrorResult{{}},
   250  	})
   251  }
   252  
   253  func (s *storageSuite) TestRemoveStorageAttachments(c *gc.C) {
   254  	setMock := func(st *mockStorageState, f func(s names.StorageTag, u names.UnitTag) error) {
   255  		st.remove = f
   256  	}
   257  
   258  	unitTag0 := names.NewUnitTag("mysql/0")
   259  	unitTag1 := names.NewUnitTag("mysql/1")
   260  	storageTag0 := names.NewStorageTag("data/0")
   261  	storageTag1 := names.NewStorageTag("data/1")
   262  
   263  	resources := common.NewResources()
   264  	getCanAccess := func() (common.AuthFunc, error) {
   265  		return func(tag names.Tag) bool {
   266  			return tag == unitTag0
   267  		}, nil
   268  	}
   269  
   270  	state := &mockStorageState{}
   271  	setMock(state, func(s names.StorageTag, u names.UnitTag) error {
   272  		c.Assert(u, gc.DeepEquals, unitTag0)
   273  		if s == storageTag1 {
   274  			return errors.New("badness")
   275  		}
   276  		return nil
   277  	})
   278  
   279  	storage, err := uniter.NewStorageAPI(state, resources, getCanAccess)
   280  	c.Assert(err, jc.ErrorIsNil)
   281  	errors, err := storage.RemoveStorageAttachments(params.StorageAttachmentIds{
   282  		Ids: []params.StorageAttachmentId{{
   283  			StorageTag: storageTag0.String(),
   284  			UnitTag:    unitTag0.String(),
   285  		}, {
   286  			StorageTag: storageTag1.String(),
   287  			UnitTag:    unitTag0.String(),
   288  		}, {
   289  			StorageTag: storageTag0.String(),
   290  			UnitTag:    unitTag1.String(),
   291  		}, {
   292  			StorageTag: unitTag0.String(), // oops
   293  			UnitTag:    unitTag0.String(),
   294  		}, {
   295  			StorageTag: storageTag0.String(),
   296  			UnitTag:    storageTag0.String(), // oops
   297  		}},
   298  	})
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	c.Assert(errors, jc.DeepEquals, params.ErrorResults{
   301  		Results: []params.ErrorResult{
   302  			{nil},
   303  			{&params.Error{Message: "badness"}},
   304  			{&params.Error{Code: params.CodeUnauthorized, Message: "permission denied"}},
   305  			{&params.Error{Message: `"unit-mysql-0" is not a valid storage tag`}},
   306  			{&params.Error{Message: `"storage-data-0" is not a valid unit tag`}},
   307  		},
   308  	})
   309  }
   310  
   311  const (
   312  	unitConstraintsCall = "mockUnitStorageConstraints"
   313  	addStorageCall      = "mockAdd"
   314  )
   315  
   316  func (s *storageSuite) TestAddUnitStorageConstraintsErrors(c *gc.C) {
   317  	setMockConstraints := func(st *mockStorageState, f func(u names.UnitTag) (map[string]state.StorageConstraints, error)) {
   318  		st.unitStorageConstraints = f
   319  	}
   320  
   321  	unitTag0 := names.NewUnitTag("mysql/0")
   322  	storageName0 := "data"
   323  	storageName1 := "store"
   324  
   325  	resources := common.NewResources()
   326  	getCanAccess := func() (common.AuthFunc, error) {
   327  		return func(tag names.Tag) bool {
   328  			return tag == unitTag0
   329  		}, nil
   330  	}
   331  
   332  	s.called = []string{}
   333  	mockState := &mockStorageState{}
   334  	setMockConstraints(mockState, func(u names.UnitTag) (map[string]state.StorageConstraints, error) {
   335  		s.called = append(s.called, unitConstraintsCall)
   336  		c.Assert(u, gc.DeepEquals, unitTag0)
   337  
   338  		return map[string]state.StorageConstraints{
   339  			storageName0: state.StorageConstraints{},
   340  		}, nil
   341  	})
   342  
   343  	storage, err := uniter.NewStorageAPI(mockState, resources, getCanAccess)
   344  	c.Assert(err, jc.ErrorIsNil)
   345  	size := uint64(10)
   346  	count := uint64(0)
   347  	errors, err := storage.AddUnitStorage(params.StoragesAddParams{
   348  		Storages: []params.StorageAddParams{
   349  			{
   350  				UnitTag:     unitTag0.String(),
   351  				StorageName: storageName0,
   352  				Constraints: params.StorageConstraints{Pool: "matter"},
   353  			}, {
   354  				UnitTag:     unitTag0.String(),
   355  				StorageName: storageName0,
   356  				Constraints: params.StorageConstraints{Size: &size},
   357  			}, {
   358  				UnitTag:     unitTag0.String(),
   359  				StorageName: storageName0,
   360  				Constraints: params.StorageConstraints{},
   361  			}, {
   362  				UnitTag:     unitTag0.String(),
   363  				StorageName: storageName0,
   364  				Constraints: params.StorageConstraints{Count: &count},
   365  			}, {
   366  				UnitTag:     unitTag0.String(),
   367  				StorageName: storageName1,
   368  				Constraints: params.StorageConstraints{},
   369  			},
   370  		}},
   371  	)
   372  	c.Assert(err, jc.ErrorIsNil)
   373  	c.Assert(s.called, jc.SameContents, []string{
   374  		unitConstraintsCall,
   375  		unitConstraintsCall,
   376  		unitConstraintsCall,
   377  		unitConstraintsCall,
   378  		unitConstraintsCall,
   379  	})
   380  	c.Assert(errors, jc.DeepEquals, params.ErrorResults{
   381  		Results: []params.ErrorResult{
   382  			{&params.Error{Message: `adding storage data for unit-mysql-0: only count can be specified`}},
   383  			{&params.Error{Message: `adding storage data for unit-mysql-0: only count can be specified`}},
   384  			{&params.Error{Message: `adding storage data for unit-mysql-0: count must be specified`}},
   385  			{&params.Error{Message: `adding storage data for unit-mysql-0: count must be specified`}},
   386  			{&params.Error{
   387  				Code:    "not found",
   388  				Message: "adding storage store for unit-mysql-0: storage \"store\" not found"}},
   389  		},
   390  	})
   391  }
   392  
   393  func (s *storageSuite) TestAddUnitStorage(c *gc.C) {
   394  	setMockConstraints := func(st *mockStorageState, f func(u names.UnitTag) (map[string]state.StorageConstraints, error)) {
   395  		st.unitStorageConstraints = f
   396  	}
   397  	setMockAdd := func(st *mockStorageState, f func(tag names.UnitTag, name string, cons state.StorageConstraints) error) {
   398  		st.addUnitStorage = f
   399  	}
   400  
   401  	unitTag0 := names.NewUnitTag("mysql/0")
   402  	storageName0 := "data"
   403  	storageName1 := "store"
   404  
   405  	unitPool := "real"
   406  	size := uint64(3)
   407  	unitSize := size * 2
   408  	unitCount := uint64(100)
   409  	testCount := uint64(10)
   410  
   411  	resources := common.NewResources()
   412  	getCanAccess := func() (common.AuthFunc, error) {
   413  		return func(tag names.Tag) bool {
   414  			return tag == unitTag0
   415  		}, nil
   416  	}
   417  
   418  	s.called = []string{}
   419  	mockState := &mockStorageState{}
   420  
   421  	setMockConstraints(mockState, func(u names.UnitTag) (map[string]state.StorageConstraints, error) {
   422  		s.called = append(s.called, unitConstraintsCall)
   423  		c.Assert(u, gc.DeepEquals, unitTag0)
   424  
   425  		return map[string]state.StorageConstraints{
   426  			storageName0: state.StorageConstraints{
   427  				Pool:  unitPool,
   428  				Size:  unitSize,
   429  				Count: unitCount,
   430  			},
   431  			storageName1: state.StorageConstraints{},
   432  		}, nil
   433  	})
   434  
   435  	setMockAdd(mockState, func(u names.UnitTag, name string, cons state.StorageConstraints) error {
   436  		s.called = append(s.called, addStorageCall)
   437  		c.Assert(u, gc.DeepEquals, unitTag0)
   438  		if name == storageName1 {
   439  			return errors.New("badness")
   440  		}
   441  		c.Assert(cons.Count, gc.Not(gc.Equals), unitCount)
   442  		c.Assert(cons.Count, jc.DeepEquals, testCount)
   443  		c.Assert(cons.Pool, jc.DeepEquals, unitPool)
   444  		c.Assert(cons.Size, jc.DeepEquals, unitSize)
   445  		return nil
   446  	})
   447  
   448  	storage, err := uniter.NewStorageAPI(mockState, resources, getCanAccess)
   449  	c.Assert(err, jc.ErrorIsNil)
   450  	errors, err := storage.AddUnitStorage(params.StoragesAddParams{
   451  		Storages: []params.StorageAddParams{
   452  			{
   453  				UnitTag:     unitTag0.String(),
   454  				StorageName: storageName0,
   455  				Constraints: params.StorageConstraints{Count: &testCount},
   456  			}, {
   457  				UnitTag:     unitTag0.String(),
   458  				StorageName: storageName1,
   459  				Constraints: params.StorageConstraints{Count: &testCount},
   460  			},
   461  		}},
   462  	)
   463  	c.Assert(err, jc.ErrorIsNil)
   464  	c.Assert(s.called, jc.SameContents, []string{
   465  		unitConstraintsCall, addStorageCall,
   466  		unitConstraintsCall, addStorageCall,
   467  	})
   468  	c.Assert(errors, jc.DeepEquals, params.ErrorResults{
   469  		Results: []params.ErrorResult{
   470  			{nil},
   471  			{&params.Error{Message: "adding storage store for unit-mysql-0: badness"}},
   472  		},
   473  	})
   474  }
   475  
   476  type mockStorageState struct {
   477  	uniter.StorageStateInterface
   478  	destroyUnitStorageAttachments func(names.UnitTag) error
   479  	remove                        func(names.StorageTag, names.UnitTag) error
   480  	storageInstance               func(names.StorageTag) (state.StorageInstance, error)
   481  	storageInstanceFilesystem     func(names.StorageTag) (state.Filesystem, error)
   482  	storageInstanceVolume         func(names.StorageTag) (state.Volume, error)
   483  	unitAssignedMachine           func(names.UnitTag) (names.MachineTag, error)
   484  	watchStorageAttachments       func(names.UnitTag) state.StringsWatcher
   485  	watchStorageAttachment        func(names.StorageTag, names.UnitTag) state.NotifyWatcher
   486  	watchFilesystemAttachment     func(names.MachineTag, names.FilesystemTag) state.NotifyWatcher
   487  	watchVolumeAttachment         func(names.MachineTag, names.VolumeTag) state.NotifyWatcher
   488  	watchBlockDevices             func(names.MachineTag) state.NotifyWatcher
   489  	addUnitStorage                func(u names.UnitTag, name string, cons state.StorageConstraints) error
   490  	unitStorageConstraints        func(u names.UnitTag) (map[string]state.StorageConstraints, error)
   491  }
   492  
   493  func (m *mockStorageState) DestroyUnitStorageAttachments(u names.UnitTag) error {
   494  	return m.destroyUnitStorageAttachments(u)
   495  }
   496  
   497  func (m *mockStorageState) RemoveStorageAttachment(s names.StorageTag, u names.UnitTag) error {
   498  	return m.remove(s, u)
   499  }
   500  
   501  func (m *mockStorageState) StorageInstance(s names.StorageTag) (state.StorageInstance, error) {
   502  	return m.storageInstance(s)
   503  }
   504  
   505  func (m *mockStorageState) StorageInstanceFilesystem(s names.StorageTag) (state.Filesystem, error) {
   506  	return m.storageInstanceFilesystem(s)
   507  }
   508  
   509  func (m *mockStorageState) StorageInstanceVolume(s names.StorageTag) (state.Volume, error) {
   510  	return m.storageInstanceVolume(s)
   511  }
   512  
   513  func (m *mockStorageState) UnitAssignedMachine(u names.UnitTag) (names.MachineTag, error) {
   514  	return m.unitAssignedMachine(u)
   515  }
   516  
   517  func (m *mockStorageState) WatchStorageAttachments(u names.UnitTag) state.StringsWatcher {
   518  	return m.watchStorageAttachments(u)
   519  }
   520  
   521  func (m *mockStorageState) WatchStorageAttachment(s names.StorageTag, u names.UnitTag) state.NotifyWatcher {
   522  	return m.watchStorageAttachment(s, u)
   523  }
   524  
   525  func (m *mockStorageState) WatchFilesystemAttachment(mtag names.MachineTag, f names.FilesystemTag) state.NotifyWatcher {
   526  	return m.watchFilesystemAttachment(mtag, f)
   527  }
   528  
   529  func (m *mockStorageState) WatchVolumeAttachment(mtag names.MachineTag, v names.VolumeTag) state.NotifyWatcher {
   530  	return m.watchVolumeAttachment(mtag, v)
   531  }
   532  
   533  func (m *mockStorageState) WatchBlockDevices(mtag names.MachineTag) state.NotifyWatcher {
   534  	return m.watchBlockDevices(mtag)
   535  }
   536  
   537  func (m *mockStorageState) AddStorageForUnit(tag names.UnitTag, name string, cons state.StorageConstraints) error {
   538  	return m.addUnitStorage(tag, name, cons)
   539  }
   540  
   541  func (m *mockStorageState) UnitStorageConstraints(u names.UnitTag) (map[string]state.StorageConstraints, error) {
   542  	return m.unitStorageConstraints(u)
   543  }
   544  
   545  type mockStringsWatcher struct {
   546  	state.StringsWatcher
   547  	changes chan []string
   548  }
   549  
   550  func (m *mockStringsWatcher) Changes() <-chan []string {
   551  	return m.changes
   552  }
   553  
   554  type mockNotifyWatcher struct {
   555  	tomb    tomb.Tomb
   556  	changes chan struct{}
   557  }
   558  
   559  func (m *mockNotifyWatcher) Stop() error {
   560  	m.Kill()
   561  	return m.Wait()
   562  }
   563  
   564  func (m *mockNotifyWatcher) Kill() {
   565  	m.tomb.Kill(nil)
   566  }
   567  
   568  func (m *mockNotifyWatcher) Wait() error {
   569  	return m.tomb.Wait()
   570  }
   571  
   572  func (m *mockNotifyWatcher) Err() error {
   573  	return m.tomb.Err()
   574  }
   575  
   576  func (m *mockNotifyWatcher) Changes() <-chan struct{} {
   577  	return m.changes
   578  }
   579  
   580  type mockVolume struct {
   581  	state.Volume
   582  	tag names.VolumeTag
   583  }
   584  
   585  func (m *mockVolume) VolumeTag() names.VolumeTag {
   586  	return m.tag
   587  }
   588  
   589  type mockFilesystem struct {
   590  	state.Filesystem
   591  	tag names.FilesystemTag
   592  }
   593  
   594  func (m *mockFilesystem) FilesystemTag() names.FilesystemTag {
   595  	return m.tag
   596  }
   597  
   598  type mockStorageInstance struct {
   599  	state.StorageInstance
   600  	kind state.StorageKind
   601  }
   602  
   603  func (m *mockStorageInstance) Kind() state.StorageKind {
   604  	return m.kind
   605  }