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