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