github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/state/storage_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  
    10  	"github.com/juju/errors"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils/set"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/charm.v6-unstable"
    15  	"gopkg.in/juju/names.v2"
    16  	"gopkg.in/mgo.v2"
    17  
    18  	"github.com/juju/juju/provider/dummy"
    19  	"github.com/juju/juju/state"
    20  	"github.com/juju/juju/state/testing"
    21  	"github.com/juju/juju/storage"
    22  	"github.com/juju/juju/storage/poolmanager"
    23  	"github.com/juju/juju/storage/provider"
    24  	dummystorage "github.com/juju/juju/storage/provider/dummy"
    25  	"github.com/juju/juju/testing/factory"
    26  )
    27  
    28  type StorageStateSuiteBase struct {
    29  	ConnSuite
    30  }
    31  
    32  func (s *StorageStateSuiteBase) SetUpTest(c *gc.C) {
    33  	s.ConnSuite.SetUpTest(c)
    34  
    35  	// Create a default pool for block devices.
    36  	pm := poolmanager.New(state.NewStateSettings(s.State), dummy.StorageProviders())
    37  	_, err := pm.Create("loop-pool", provider.LoopProviderType, map[string]interface{}{})
    38  	c.Assert(err, jc.ErrorIsNil)
    39  
    40  	// Create a pool that creates persistent block devices.
    41  	_, err = pm.Create("persistent-block", "environscoped-block", map[string]interface{}{
    42  		"persistent": true,
    43  	})
    44  	c.Assert(err, jc.ErrorIsNil)
    45  }
    46  
    47  func (s *StorageStateSuiteBase) setupSingleStorage(c *gc.C, kind, pool string) (*state.Application, *state.Unit, names.StorageTag) {
    48  	// There are test charms called "storage-block" and
    49  	// "storage-filesystem" which are what you'd expect.
    50  	ch := s.AddTestingCharm(c, "storage-"+kind)
    51  	storage := map[string]state.StorageConstraints{
    52  		"data": makeStorageCons(pool, 1024, 1),
    53  	}
    54  	service := s.AddTestingServiceWithStorage(c, "storage-"+kind, ch, storage)
    55  	unit, err := service.AddUnit()
    56  	c.Assert(err, jc.ErrorIsNil)
    57  	storageTag := names.NewStorageTag("data/0")
    58  	return service, unit, storageTag
    59  }
    60  
    61  func (s *StorageStateSuiteBase) createStorageCharm(c *gc.C, charmName string, storageMeta charm.Storage) *state.Charm {
    62  	return s.createStorageCharmRev(c, charmName, storageMeta, 1)
    63  }
    64  
    65  func (s *StorageStateSuiteBase) createStorageCharmRev(c *gc.C, charmName string, storageMeta charm.Storage, rev int) *state.Charm {
    66  	meta := fmt.Sprintf(`
    67  name: %s
    68  summary: A charm for testing storage
    69  description: ditto
    70  storage:
    71    %s:
    72      type: %s
    73  `, charmName, storageMeta.Name, storageMeta.Type)
    74  	if storageMeta.ReadOnly {
    75  		meta += "    read-only: true\n"
    76  	}
    77  	if storageMeta.Shared {
    78  		meta += "    shared: true\n"
    79  	}
    80  	if storageMeta.MinimumSize > 0 {
    81  		meta += fmt.Sprintf("    minimum-size: %dM\n", storageMeta.MinimumSize)
    82  	}
    83  	if storageMeta.Location != "" {
    84  		meta += "    location: " + storageMeta.Location + "\n"
    85  	}
    86  	if storageMeta.CountMin != 1 || storageMeta.CountMax != 1 {
    87  		meta += "    multiple:\n"
    88  		meta += fmt.Sprintf("      range: %d-", storageMeta.CountMin)
    89  		if storageMeta.CountMax >= 0 {
    90  			meta += fmt.Sprint(storageMeta.CountMax)
    91  		}
    92  		meta += "\n"
    93  	}
    94  	ch := s.AddMetaCharm(c, charmName, meta, rev)
    95  	return ch
    96  }
    97  
    98  func (s *StorageStateSuiteBase) setupMixedScopeStorageService(c *gc.C, kind string) *state.Application {
    99  	storageCons := map[string]state.StorageConstraints{
   100  		"multi1to10": makeStorageCons("environscoped", 1024, 1),
   101  		"multi2up":   makeStorageCons("machinescoped", 2048, 2),
   102  	}
   103  	ch := s.AddTestingCharm(c, "storage-"+kind+"2")
   104  	return s.AddTestingServiceWithStorage(c, "storage-"+kind+"2", ch, storageCons)
   105  }
   106  
   107  func (s *StorageStateSuiteBase) storageInstanceExists(c *gc.C, tag names.StorageTag) bool {
   108  	_, err := state.TxnRevno(
   109  		s.State,
   110  		state.StorageInstancesC,
   111  		state.DocID(s.State, tag.Id()),
   112  	)
   113  	if err != nil {
   114  		c.Assert(err, gc.Equals, mgo.ErrNotFound)
   115  		return false
   116  	}
   117  	return true
   118  }
   119  
   120  func (s *StorageStateSuiteBase) assertFilesystemUnprovisioned(c *gc.C, tag names.FilesystemTag) {
   121  	filesystem := s.filesystem(c, tag)
   122  	_, err := filesystem.Info()
   123  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
   124  	_, ok := filesystem.Params()
   125  	c.Assert(ok, jc.IsTrue)
   126  }
   127  
   128  func (s *StorageStateSuiteBase) assertFilesystemInfo(c *gc.C, tag names.FilesystemTag, expect state.FilesystemInfo) {
   129  	filesystem := s.filesystem(c, tag)
   130  	info, err := filesystem.Info()
   131  	c.Assert(err, jc.ErrorIsNil)
   132  	c.Assert(info, jc.DeepEquals, expect)
   133  	_, ok := filesystem.Params()
   134  	c.Assert(ok, jc.IsFalse)
   135  }
   136  
   137  func (s *StorageStateSuiteBase) assertFilesystemAttachmentUnprovisioned(c *gc.C, m names.MachineTag, f names.FilesystemTag) {
   138  	filesystemAttachment := s.filesystemAttachment(c, m, f)
   139  	_, err := filesystemAttachment.Info()
   140  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
   141  	_, ok := filesystemAttachment.Params()
   142  	c.Assert(ok, jc.IsTrue)
   143  }
   144  
   145  func (s *StorageStateSuiteBase) assertFilesystemAttachmentInfo(c *gc.C, m names.MachineTag, f names.FilesystemTag, expect state.FilesystemAttachmentInfo) {
   146  	filesystemAttachment := s.filesystemAttachment(c, m, f)
   147  	info, err := filesystemAttachment.Info()
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	c.Assert(info, jc.DeepEquals, expect)
   150  	_, ok := filesystemAttachment.Params()
   151  	c.Assert(ok, jc.IsFalse)
   152  }
   153  
   154  func (s *StorageStateSuiteBase) assertVolumeUnprovisioned(c *gc.C, tag names.VolumeTag) {
   155  	volume := s.volume(c, tag)
   156  	_, err := volume.Info()
   157  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
   158  	_, ok := volume.Params()
   159  	c.Assert(ok, jc.IsTrue)
   160  }
   161  
   162  func (s *StorageStateSuiteBase) assertVolumeInfo(c *gc.C, tag names.VolumeTag, expect state.VolumeInfo) {
   163  	volume := s.volume(c, tag)
   164  	info, err := volume.Info()
   165  	c.Assert(err, jc.ErrorIsNil)
   166  	c.Assert(info, jc.DeepEquals, expect)
   167  	_, ok := volume.Params()
   168  	c.Assert(ok, jc.IsFalse)
   169  }
   170  
   171  func (s *StorageStateSuiteBase) machine(c *gc.C, id string) *state.Machine {
   172  	machine, err := s.State.Machine(id)
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	return machine
   175  }
   176  
   177  func (s *StorageStateSuiteBase) filesystem(c *gc.C, tag names.FilesystemTag) state.Filesystem {
   178  	filesystem, err := s.State.Filesystem(tag)
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	return filesystem
   181  }
   182  
   183  func (s *StorageStateSuiteBase) filesystemVolume(c *gc.C, tag names.FilesystemTag) state.Volume {
   184  	filesystem := s.filesystem(c, tag)
   185  	volumeTag, err := filesystem.Volume()
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	return s.volume(c, volumeTag)
   188  }
   189  
   190  func (s *StorageStateSuiteBase) filesystemAttachment(c *gc.C, m names.MachineTag, f names.FilesystemTag) state.FilesystemAttachment {
   191  	attachment, err := s.State.FilesystemAttachment(m, f)
   192  	c.Assert(err, jc.ErrorIsNil)
   193  	return attachment
   194  }
   195  
   196  func (s *StorageStateSuiteBase) volume(c *gc.C, tag names.VolumeTag) state.Volume {
   197  	volume, err := s.State.Volume(tag)
   198  	c.Assert(err, jc.ErrorIsNil)
   199  	return volume
   200  }
   201  
   202  func (s *StorageStateSuiteBase) volumeFilesystem(c *gc.C, tag names.VolumeTag) state.Filesystem {
   203  	filesystem, err := s.State.VolumeFilesystem(tag)
   204  	c.Assert(err, jc.ErrorIsNil)
   205  	return filesystem
   206  }
   207  
   208  func (s *StorageStateSuiteBase) volumeAttachment(c *gc.C, m names.MachineTag, v names.VolumeTag) state.VolumeAttachment {
   209  	attachment, err := s.State.VolumeAttachment(m, v)
   210  	c.Assert(err, jc.ErrorIsNil)
   211  	return attachment
   212  }
   213  
   214  func (s *StorageStateSuiteBase) storageInstanceVolume(c *gc.C, tag names.StorageTag) state.Volume {
   215  	volume, err := s.State.StorageInstanceVolume(tag)
   216  	c.Assert(err, jc.ErrorIsNil)
   217  	return volume
   218  }
   219  
   220  func (s *StorageStateSuiteBase) storageInstanceFilesystem(c *gc.C, tag names.StorageTag) state.Filesystem {
   221  	filesystem, err := s.State.StorageInstanceFilesystem(tag)
   222  	c.Assert(err, jc.ErrorIsNil)
   223  	return filesystem
   224  }
   225  
   226  func (s *StorageStateSuiteBase) obliterateUnit(c *gc.C, tag names.UnitTag) {
   227  	u, err := s.State.Unit(tag.Id())
   228  	c.Assert(err, jc.ErrorIsNil)
   229  	err = u.Destroy()
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	s.obliterateUnitStorage(c, tag)
   232  	err = u.EnsureDead()
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	err = u.Remove()
   235  	c.Assert(err, jc.ErrorIsNil)
   236  }
   237  
   238  func (s *StorageStateSuiteBase) obliterateUnitStorage(c *gc.C, tag names.UnitTag) {
   239  	attachments, err := s.State.UnitStorageAttachments(tag)
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	for _, a := range attachments {
   242  		err = s.State.DestroyStorageAttachment(a.StorageInstance(), a.Unit())
   243  		c.Assert(err, jc.ErrorIsNil)
   244  		err = s.State.RemoveStorageAttachment(a.StorageInstance(), a.Unit())
   245  		c.Assert(err, jc.ErrorIsNil)
   246  	}
   247  }
   248  
   249  func (s *StorageStateSuiteBase) obliterateVolume(c *gc.C, tag names.VolumeTag) {
   250  	err := s.State.DestroyVolume(tag)
   251  	if errors.IsNotFound(err) {
   252  		return
   253  	}
   254  	attachments, err := s.State.VolumeAttachments(tag)
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	for _, a := range attachments {
   257  		s.obliterateVolumeAttachment(c, a.Machine(), a.Volume())
   258  	}
   259  	err = s.State.RemoveVolume(tag)
   260  	c.Assert(err, jc.ErrorIsNil)
   261  }
   262  
   263  func (s *StorageStateSuiteBase) obliterateVolumeAttachment(c *gc.C, m names.MachineTag, v names.VolumeTag) {
   264  	err := s.State.DetachVolume(m, v)
   265  	c.Assert(err, jc.ErrorIsNil)
   266  	err = s.State.RemoveVolumeAttachment(m, v)
   267  	c.Assert(err, jc.ErrorIsNil)
   268  }
   269  
   270  func (s *StorageStateSuiteBase) obliterateFilesystem(c *gc.C, tag names.FilesystemTag) {
   271  	err := s.State.DestroyFilesystem(tag)
   272  	if errors.IsNotFound(err) {
   273  		return
   274  	}
   275  	attachments, err := s.State.FilesystemAttachments(tag)
   276  	c.Assert(err, jc.ErrorIsNil)
   277  	for _, a := range attachments {
   278  		s.obliterateFilesystemAttachment(c, a.Machine(), a.Filesystem())
   279  	}
   280  	err = s.State.RemoveFilesystem(tag)
   281  	c.Assert(err, jc.ErrorIsNil)
   282  }
   283  
   284  func (s *StorageStateSuiteBase) obliterateFilesystemAttachment(c *gc.C, m names.MachineTag, f names.FilesystemTag) {
   285  	err := s.State.DetachFilesystem(m, f)
   286  	c.Assert(err, jc.ErrorIsNil)
   287  	err = s.State.RemoveFilesystemAttachment(m, f)
   288  	c.Assert(err, jc.ErrorIsNil)
   289  }
   290  
   291  // assertMachineStorageRefs ensures that the specified machine's set of volume
   292  // and filesystem references corresponds exactly to the volume and filesystem
   293  // attachments that relate to the machine.
   294  func assertMachineStorageRefs(c *gc.C, st *state.State, m names.MachineTag) {
   295  	machines, closer := state.GetRawCollection(st, state.MachinesC)
   296  	defer closer()
   297  
   298  	var doc struct {
   299  		Volumes     []string `bson:"volumes,omitempty"`
   300  		Filesystems []string `bson:"filesystems,omitempty"`
   301  	}
   302  	err := machines.FindId(state.DocID(st, m.Id())).One(&doc)
   303  	c.Assert(err, jc.ErrorIsNil)
   304  
   305  	have := make(set.Tags)
   306  	for _, v := range doc.Volumes {
   307  		have.Add(names.NewVolumeTag(v))
   308  	}
   309  	for _, f := range doc.Filesystems {
   310  		have.Add(names.NewFilesystemTag(f))
   311  	}
   312  
   313  	expect := make(set.Tags)
   314  	volumeAttachments, err := st.MachineVolumeAttachments(m)
   315  	c.Assert(err, jc.ErrorIsNil)
   316  	for _, a := range volumeAttachments {
   317  		expect.Add(a.Volume())
   318  	}
   319  	filesystemAttachments, err := st.MachineFilesystemAttachments(m)
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	for _, a := range filesystemAttachments {
   322  		expect.Add(a.Filesystem())
   323  	}
   324  
   325  	c.Assert(have, jc.DeepEquals, expect)
   326  }
   327  
   328  func makeStorageCons(pool string, size, count uint64) state.StorageConstraints {
   329  	return state.StorageConstraints{Pool: pool, Size: size, Count: count}
   330  }
   331  
   332  type StorageStateSuite struct {
   333  	StorageStateSuiteBase
   334  }
   335  
   336  var _ = gc.Suite(&StorageStateSuite{})
   337  
   338  func (s *StorageStateSuite) TestAddServiceStorageConstraintsDefault(c *gc.C) {
   339  	ch := s.AddTestingCharm(c, "storage-block")
   340  	storageBlock, err := s.State.AddApplication(state.AddApplicationArgs{Name: "storage-block", Charm: ch})
   341  	c.Assert(err, jc.ErrorIsNil)
   342  	constraints, err := storageBlock.StorageConstraints()
   343  	c.Assert(err, jc.ErrorIsNil)
   344  	c.Assert(constraints, jc.DeepEquals, map[string]state.StorageConstraints{
   345  		"data": {
   346  			Pool:  "loop",
   347  			Count: 1,
   348  			Size:  1024,
   349  		},
   350  		"allecto": {
   351  			Pool:  "loop",
   352  			Count: 0,
   353  			Size:  1024,
   354  		},
   355  	})
   356  
   357  	ch = s.AddTestingCharm(c, "storage-filesystem")
   358  	storageFilesystem, err := s.State.AddApplication(state.AddApplicationArgs{Name: "storage-filesystem", Charm: ch})
   359  	c.Assert(err, jc.ErrorIsNil)
   360  	constraints, err = storageFilesystem.StorageConstraints()
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	c.Assert(constraints, jc.DeepEquals, map[string]state.StorageConstraints{
   363  		"data": {
   364  			Pool:  "rootfs",
   365  			Count: 1,
   366  			Size:  1024,
   367  		},
   368  	})
   369  }
   370  
   371  func (s *StorageStateSuite) TestAddServiceStorageConstraintsValidation(c *gc.C) {
   372  	ch := s.AddTestingCharm(c, "storage-block2")
   373  	addService := func(storage map[string]state.StorageConstraints) (*state.Application, error) {
   374  		return s.State.AddApplication(state.AddApplicationArgs{Name: "storage-block2", Charm: ch, Storage: storage})
   375  	}
   376  	assertErr := func(storage map[string]state.StorageConstraints, expect string) {
   377  		_, err := addService(storage)
   378  		c.Assert(err, gc.ErrorMatches, expect)
   379  	}
   380  
   381  	storageCons := map[string]state.StorageConstraints{
   382  		"multi1to10": makeStorageCons("loop-pool", 1024, 1),
   383  		"multi2up":   makeStorageCons("loop-pool", 2048, 1),
   384  	}
   385  	assertErr(storageCons, `cannot add application "storage-block2": charm "storage-block2" store "multi2up": 2 instances required, 1 specified`)
   386  	storageCons["multi2up"] = makeStorageCons("loop-pool", 1024, 2)
   387  	assertErr(storageCons, `cannot add application "storage-block2": charm "storage-block2" store "multi2up": minimum storage size is 2.0GB, 1.0GB specified`)
   388  	storageCons["multi2up"] = makeStorageCons("loop-pool", 2048, 2)
   389  	storageCons["multi1to10"] = makeStorageCons("loop-pool", 1024, 11)
   390  	assertErr(storageCons, `cannot add application "storage-block2": charm "storage-block2" store "multi1to10": at most 10 instances supported, 11 specified`)
   391  	storageCons["multi1to10"] = makeStorageCons("ebs-fast", 1024, 10)
   392  	assertErr(storageCons, `cannot add application "storage-block2": pool "ebs-fast" not found`)
   393  	storageCons["multi1to10"] = makeStorageCons("loop-pool", 1024, 10)
   394  	_, err := addService(storageCons)
   395  	c.Assert(err, jc.ErrorIsNil)
   396  }
   397  
   398  func (s *StorageStateSuite) assertAddServiceStorageConstraintsDefaults(c *gc.C, pool string, cons, expect map[string]state.StorageConstraints) {
   399  	if pool != "" {
   400  		err := s.State.UpdateModelConfig(map[string]interface{}{
   401  			"storage-default-block-source": pool,
   402  		}, nil, nil)
   403  		c.Assert(err, jc.ErrorIsNil)
   404  	}
   405  	ch := s.AddTestingCharm(c, "storage-block")
   406  	service, err := s.State.AddApplication(state.AddApplicationArgs{Name: "storage-block2", Charm: ch, Storage: cons})
   407  	c.Assert(err, jc.ErrorIsNil)
   408  	savedCons, err := service.StorageConstraints()
   409  	c.Assert(err, jc.ErrorIsNil)
   410  	c.Assert(savedCons, jc.DeepEquals, expect)
   411  	// TODO(wallyworld) - test pool name stored in data model
   412  }
   413  
   414  func (s *StorageStateSuite) TestAddServiceStorageConstraintsNoConstraintsUsed(c *gc.C) {
   415  	storageCons := map[string]state.StorageConstraints{
   416  		"data": makeStorageCons("", 0, 0),
   417  	}
   418  	expectedCons := map[string]state.StorageConstraints{
   419  		"data":    makeStorageCons("loop", 1024, 1),
   420  		"allecto": makeStorageCons("loop", 1024, 0),
   421  	}
   422  	s.assertAddServiceStorageConstraintsDefaults(c, "loop-pool", storageCons, expectedCons)
   423  }
   424  
   425  func (s *StorageStateSuite) TestAddServiceStorageConstraintsJustCount(c *gc.C) {
   426  	storageCons := map[string]state.StorageConstraints{
   427  		"data": makeStorageCons("", 0, 1),
   428  	}
   429  	expectedCons := map[string]state.StorageConstraints{
   430  		"data":    makeStorageCons("loop-pool", 1024, 1),
   431  		"allecto": makeStorageCons("loop", 1024, 0),
   432  	}
   433  	s.assertAddServiceStorageConstraintsDefaults(c, "loop-pool", storageCons, expectedCons)
   434  }
   435  
   436  func (s *StorageStateSuite) TestAddServiceStorageConstraintsDefaultPool(c *gc.C) {
   437  	storageCons := map[string]state.StorageConstraints{
   438  		"data": makeStorageCons("", 2048, 1),
   439  	}
   440  	expectedCons := map[string]state.StorageConstraints{
   441  		"data":    makeStorageCons("loop-pool", 2048, 1),
   442  		"allecto": makeStorageCons("loop", 1024, 0),
   443  	}
   444  	s.assertAddServiceStorageConstraintsDefaults(c, "loop-pool", storageCons, expectedCons)
   445  }
   446  
   447  func (s *StorageStateSuite) TestAddServiceStorageConstraintsNoUserDefaultPool(c *gc.C) {
   448  	storageCons := map[string]state.StorageConstraints{
   449  		"data": makeStorageCons("", 2048, 1),
   450  	}
   451  	expectedCons := map[string]state.StorageConstraints{
   452  		"data":    makeStorageCons("loop", 2048, 1),
   453  		"allecto": makeStorageCons("loop", 1024, 0),
   454  	}
   455  	s.assertAddServiceStorageConstraintsDefaults(c, "", storageCons, expectedCons)
   456  }
   457  
   458  func (s *StorageStateSuite) TestAddServiceStorageConstraintsDefaultSizeFallback(c *gc.C) {
   459  	storageCons := map[string]state.StorageConstraints{
   460  		"data": makeStorageCons("loop-pool", 0, 1),
   461  	}
   462  	expectedCons := map[string]state.StorageConstraints{
   463  		"data":    makeStorageCons("loop-pool", 1024, 1),
   464  		"allecto": makeStorageCons("loop", 1024, 0),
   465  	}
   466  	s.assertAddServiceStorageConstraintsDefaults(c, "loop-pool", storageCons, expectedCons)
   467  }
   468  
   469  func (s *StorageStateSuite) TestAddServiceStorageConstraintsDefaultSizeFromCharm(c *gc.C) {
   470  	storageCons := map[string]state.StorageConstraints{
   471  		"multi1to10": makeStorageCons("loop", 0, 3),
   472  	}
   473  	expectedCons := map[string]state.StorageConstraints{
   474  		"multi1to10": makeStorageCons("loop", 1024, 3),
   475  		"multi2up":   makeStorageCons("loop", 2048, 2),
   476  	}
   477  	ch := s.AddTestingCharm(c, "storage-block2")
   478  	service, err := s.State.AddApplication(state.AddApplicationArgs{Name: "storage-block2", Charm: ch, Storage: storageCons})
   479  	c.Assert(err, jc.ErrorIsNil)
   480  	savedCons, err := service.StorageConstraints()
   481  	c.Assert(err, jc.ErrorIsNil)
   482  	c.Assert(savedCons, jc.DeepEquals, expectedCons)
   483  }
   484  
   485  func (s *StorageStateSuite) TestProviderFallbackToType(c *gc.C) {
   486  	ch := s.AddTestingCharm(c, "storage-block")
   487  	addService := func(storage map[string]state.StorageConstraints) (*state.Application, error) {
   488  		return s.State.AddApplication(state.AddApplicationArgs{Name: "storage-block", Charm: ch, Storage: storage})
   489  	}
   490  	storageCons := map[string]state.StorageConstraints{
   491  		"data": makeStorageCons("loop", 1024, 1),
   492  	}
   493  	_, err := addService(storageCons)
   494  	c.Assert(err, jc.ErrorIsNil)
   495  }
   496  
   497  func (s *StorageStateSuite) TestAddUnit(c *gc.C) {
   498  	s.assertStorageUnitsAdded(c)
   499  }
   500  
   501  func (s *StorageStateSuite) assertStorageUnitsAdded(c *gc.C) {
   502  	err := s.State.UpdateModelConfig(map[string]interface{}{
   503  		"storage-default-block-source": "loop-pool",
   504  	}, nil, nil)
   505  	c.Assert(err, jc.ErrorIsNil)
   506  
   507  	// Each unit added to the service will create storage instances
   508  	// to satisfy the service's storage constraints.
   509  	ch := s.AddTestingCharm(c, "storage-block2")
   510  	storage := map[string]state.StorageConstraints{
   511  		"multi1to10": makeStorageCons("", 1024, 1),
   512  		"multi2up":   makeStorageCons("loop-pool", 2048, 2),
   513  	}
   514  	service := s.AddTestingServiceWithStorage(c, "storage-block2", ch, storage)
   515  	for i := 0; i < 2; i++ {
   516  		u, err := service.AddUnit()
   517  		c.Assert(err, jc.ErrorIsNil)
   518  		storageAttachments, err := s.State.UnitStorageAttachments(u.UnitTag())
   519  		c.Assert(err, jc.ErrorIsNil)
   520  		count := make(map[string]int)
   521  		for _, att := range storageAttachments {
   522  			c.Assert(att.Unit(), gc.Equals, u.UnitTag())
   523  			storageInstance, err := s.State.StorageInstance(att.StorageInstance())
   524  			c.Assert(err, jc.ErrorIsNil)
   525  			count[storageInstance.StorageName()]++
   526  			c.Assert(storageInstance.Kind(), gc.Equals, state.StorageKindBlock)
   527  		}
   528  		c.Assert(count, gc.DeepEquals, map[string]int{
   529  			"multi1to10": 1,
   530  			"multi2up":   2,
   531  		})
   532  		// TODO(wallyworld) - test pool name stored in data model
   533  	}
   534  }
   535  
   536  func (s *StorageStateSuite) TestAllStorageInstances(c *gc.C) {
   537  	s.assertStorageUnitsAdded(c)
   538  
   539  	all, err := s.State.AllStorageInstances()
   540  	c.Assert(err, jc.ErrorIsNil)
   541  	c.Assert(all, gc.HasLen, 6)
   542  
   543  	nameSet := set.NewStrings("multi1to10", "multi2up")
   544  	ownerSet := set.NewStrings("unit-storage-block2-0", "unit-storage-block2-1")
   545  
   546  	for _, one := range all {
   547  		c.Assert(one.Kind(), gc.DeepEquals, state.StorageKindBlock)
   548  		c.Assert(nameSet.Contains(one.StorageName()), jc.IsTrue)
   549  		c.Assert(ownerSet.Contains(one.Owner().String()), jc.IsTrue)
   550  	}
   551  }
   552  
   553  func (s *StorageStateSuite) TestStorageAttachments(c *gc.C) {
   554  	s.assertStorageUnitsAdded(c)
   555  
   556  	assertAttachments := func(tag names.StorageTag, expect ...names.UnitTag) {
   557  		attachments, err := s.State.StorageAttachments(tag)
   558  		c.Assert(err, jc.ErrorIsNil)
   559  		units := make([]names.UnitTag, len(attachments))
   560  		for i, a := range attachments {
   561  			units[i] = a.Unit()
   562  		}
   563  		c.Assert(units, jc.SameContents, expect)
   564  	}
   565  
   566  	u0 := names.NewUnitTag("storage-block2/0")
   567  	u1 := names.NewUnitTag("storage-block2/1")
   568  
   569  	assertAttachments(names.NewStorageTag("multi1to10/0"), u0)
   570  	assertAttachments(names.NewStorageTag("multi2up/1"), u0)
   571  	assertAttachments(names.NewStorageTag("multi2up/2"), u0)
   572  	assertAttachments(names.NewStorageTag("multi1to10/3"), u1)
   573  	assertAttachments(names.NewStorageTag("multi2up/4"), u1)
   574  	assertAttachments(names.NewStorageTag("multi2up/5"), u1)
   575  }
   576  
   577  func (s *StorageStateSuite) TestAllStorageInstancesEmpty(c *gc.C) {
   578  	all, err := s.State.AllStorageInstances()
   579  	c.Assert(err, jc.ErrorIsNil)
   580  	c.Assert(all, gc.HasLen, 0)
   581  }
   582  
   583  func (s *StorageStateSuite) TestUnitEnsureDead(c *gc.C) {
   584  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   585  	// destroying a unit with storage attachments is fine; this is what
   586  	// will trigger the death and removal of storage attachments.
   587  	err := u.Destroy()
   588  	c.Assert(err, jc.ErrorIsNil)
   589  	// until all storage attachments are removed, the unit cannot be
   590  	// marked as being dead.
   591  	assertUnitEnsureDeadError := func() {
   592  		err = u.EnsureDead()
   593  		c.Assert(err, gc.ErrorMatches, "unit has storage attachments")
   594  	}
   595  	assertUnitEnsureDeadError()
   596  	err = s.State.DestroyStorageAttachment(storageTag, u.UnitTag())
   597  	c.Assert(err, jc.ErrorIsNil)
   598  	assertUnitEnsureDeadError()
   599  	err = s.State.DestroyStorageInstance(storageTag)
   600  	c.Assert(err, jc.ErrorIsNil)
   601  	assertUnitEnsureDeadError()
   602  	err = s.State.RemoveStorageAttachment(storageTag, u.UnitTag())
   603  	c.Assert(err, jc.ErrorIsNil)
   604  	err = u.EnsureDead()
   605  	c.Assert(err, jc.ErrorIsNil)
   606  }
   607  
   608  func (s *StorageStateSuite) TestRemoveStorageAttachmentsRemovesDyingInstance(c *gc.C) {
   609  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   610  
   611  	// Mark the storage instance as Dying, so that it will be removed
   612  	// when the last attachment is removed.
   613  	err := s.State.DestroyStorageInstance(storageTag)
   614  	c.Assert(err, jc.ErrorIsNil)
   615  
   616  	si, err := s.State.StorageInstance(storageTag)
   617  	c.Assert(err, jc.ErrorIsNil)
   618  	c.Assert(si.Life(), gc.Equals, state.Dying)
   619  
   620  	err = s.State.DestroyStorageAttachment(storageTag, u.UnitTag())
   621  	c.Assert(err, jc.ErrorIsNil)
   622  	err = s.State.RemoveStorageAttachment(storageTag, u.UnitTag())
   623  	c.Assert(err, jc.ErrorIsNil)
   624  	exists := s.storageInstanceExists(c, storageTag)
   625  	c.Assert(exists, jc.IsFalse)
   626  }
   627  
   628  func (s *StorageStateSuite) TestRemoveStorageAttachmentsRemovesUnitOwnedInstance(c *gc.C) {
   629  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   630  
   631  	// Even though the storage instance is Alive, it will be removed when
   632  	// the last attachment is removed, since it is not possible to add
   633  	// more attachments later.
   634  	si, err := s.State.StorageInstance(storageTag)
   635  	c.Assert(err, jc.ErrorIsNil)
   636  	c.Assert(si.Life(), gc.Equals, state.Alive)
   637  
   638  	err = s.State.DestroyStorageAttachment(storageTag, u.UnitTag())
   639  	c.Assert(err, jc.ErrorIsNil)
   640  	err = s.State.RemoveStorageAttachment(storageTag, u.UnitTag())
   641  	c.Assert(err, jc.ErrorIsNil)
   642  	exists := s.storageInstanceExists(c, storageTag)
   643  	c.Assert(exists, jc.IsFalse)
   644  }
   645  
   646  func (s *StorageStateSuite) TestConcurrentDestroyStorageInstanceRemoveStorageAttachmentsRemovesInstance(c *gc.C) {
   647  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   648  
   649  	defer state.SetBeforeHooks(c, s.State, func() {
   650  		err := s.State.DestroyStorageAttachment(storageTag, u.UnitTag())
   651  		c.Assert(err, jc.ErrorIsNil)
   652  		err = s.State.RemoveStorageAttachment(storageTag, u.UnitTag())
   653  		c.Assert(err, jc.ErrorIsNil)
   654  	}).Check()
   655  
   656  	// Destroying the instance should check that there are no concurrent
   657  	// changes to the storage instance's attachments, and recompute
   658  	// operations if there are.
   659  	err := s.State.DestroyStorageInstance(storageTag)
   660  	c.Assert(err, jc.ErrorIsNil)
   661  
   662  	exists := s.storageInstanceExists(c, storageTag)
   663  	c.Assert(exists, jc.IsFalse)
   664  }
   665  
   666  func (s *StorageStateSuite) TestConcurrentRemoveStorageAttachment(c *gc.C) {
   667  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   668  
   669  	err := s.State.DestroyStorageInstance(storageTag)
   670  	c.Assert(err, jc.ErrorIsNil)
   671  
   672  	destroy := func() {
   673  		err = s.State.DestroyStorageAttachment(storageTag, u.UnitTag())
   674  		c.Assert(err, jc.ErrorIsNil)
   675  	}
   676  	remove := func() {
   677  		err = s.State.RemoveStorageAttachment(storageTag, u.UnitTag())
   678  		c.Assert(err, jc.ErrorIsNil)
   679  	}
   680  
   681  	defer state.SetBeforeHooks(c, s.State, destroy, remove).Check()
   682  	destroy()
   683  	remove()
   684  	exists := s.storageInstanceExists(c, storageTag)
   685  	c.Assert(exists, jc.IsFalse)
   686  }
   687  
   688  func (s *StorageStateSuite) TestRemoveAliveStorageAttachmentError(c *gc.C) {
   689  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   690  
   691  	err := s.State.RemoveStorageAttachment(storageTag, u.UnitTag())
   692  	c.Assert(err, gc.ErrorMatches, "cannot remove storage attachment data/0:storage-block/0: storage attachment is not dying")
   693  
   694  	attachments, err := s.State.UnitStorageAttachments(u.UnitTag())
   695  	c.Assert(err, jc.ErrorIsNil)
   696  	c.Assert(attachments, gc.HasLen, 1)
   697  	c.Assert(attachments[0].StorageInstance(), gc.Equals, storageTag)
   698  }
   699  
   700  func (s *StorageStateSuite) TestConcurrentDestroyInstanceRemoveStorageAttachmentsRemovesInstance(c *gc.C) {
   701  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   702  
   703  	defer state.SetBeforeHooks(c, s.State, func() {
   704  		// Concurrently mark the storage instance as Dying,
   705  		// so that it will be removed when the last attachment
   706  		// is removed.
   707  		err := s.State.DestroyStorageInstance(storageTag)
   708  		c.Assert(err, jc.ErrorIsNil)
   709  	}, nil).Check()
   710  
   711  	// Removing the attachment should check that there are no concurrent
   712  	// changes to the storage instance's life, and recompute operations
   713  	// if it does.
   714  	err := s.State.DestroyStorageAttachment(storageTag, u.UnitTag())
   715  	c.Assert(err, jc.ErrorIsNil)
   716  	err = s.State.RemoveStorageAttachment(storageTag, u.UnitTag())
   717  	c.Assert(err, jc.ErrorIsNil)
   718  	exists := s.storageInstanceExists(c, storageTag)
   719  	c.Assert(exists, jc.IsFalse)
   720  }
   721  
   722  func (s *StorageStateSuite) TestConcurrentDestroyStorageInstance(c *gc.C) {
   723  	_, _, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   724  
   725  	defer state.SetBeforeHooks(c, s.State, func() {
   726  		err := s.State.DestroyStorageInstance(storageTag)
   727  		c.Assert(err, jc.ErrorIsNil)
   728  	}).Check()
   729  
   730  	err := s.State.DestroyStorageInstance(storageTag)
   731  	c.Assert(err, jc.ErrorIsNil)
   732  
   733  	si, err := s.State.StorageInstance(storageTag)
   734  	c.Assert(err, jc.ErrorIsNil)
   735  	c.Assert(si.Life(), gc.Equals, state.Dying)
   736  }
   737  
   738  func (s *StorageStateSuite) TestWatchStorageAttachments(c *gc.C) {
   739  	ch := s.AddTestingCharm(c, "storage-block2")
   740  	storage := map[string]state.StorageConstraints{
   741  		"multi1to10": makeStorageCons("loop-pool", 1024, 1),
   742  		"multi2up":   makeStorageCons("loop-pool", 2048, 2),
   743  	}
   744  	service := s.AddTestingServiceWithStorage(c, "storage-block2", ch, storage)
   745  	u, err := service.AddUnit()
   746  	c.Assert(err, jc.ErrorIsNil)
   747  
   748  	w := s.State.WatchStorageAttachments(u.UnitTag())
   749  	defer testing.AssertStop(c, w)
   750  	wc := testing.NewStringsWatcherC(c, s.State, w)
   751  	wc.AssertChange("multi1to10/0", "multi2up/1", "multi2up/2")
   752  	wc.AssertNoChange()
   753  
   754  	err = s.State.DestroyStorageAttachment(names.NewStorageTag("multi2up/1"), u.UnitTag())
   755  	c.Assert(err, jc.ErrorIsNil)
   756  	wc.AssertChange("multi2up/1")
   757  	wc.AssertNoChange()
   758  }
   759  
   760  func (s *StorageStateSuite) TestWatchStorageAttachment(c *gc.C) {
   761  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   762  
   763  	w := s.State.WatchStorageAttachment(storageTag, u.UnitTag())
   764  	defer testing.AssertStop(c, w)
   765  	wc := testing.NewNotifyWatcherC(c, s.State, w)
   766  	wc.AssertOneChange()
   767  
   768  	err := s.State.DestroyStorageAttachment(storageTag, u.UnitTag())
   769  	c.Assert(err, jc.ErrorIsNil)
   770  	wc.AssertOneChange()
   771  
   772  	err = s.State.RemoveStorageAttachment(storageTag, u.UnitTag())
   773  	c.Assert(err, jc.ErrorIsNil)
   774  	wc.AssertOneChange()
   775  }
   776  
   777  func (s *StorageStateSuite) TestDestroyUnitStorageAttachments(c *gc.C) {
   778  	service := s.setupMixedScopeStorageService(c, "block")
   779  	u, err := service.AddUnit()
   780  	c.Assert(err, jc.ErrorIsNil)
   781  	defer state.SetBeforeHooks(c, s.State, func() {
   782  		err := s.State.DestroyUnitStorageAttachments(u.UnitTag())
   783  		c.Assert(err, jc.ErrorIsNil)
   784  		attachments, err := s.State.UnitStorageAttachments(u.UnitTag())
   785  		c.Assert(err, jc.ErrorIsNil)
   786  		c.Assert(attachments, gc.HasLen, 3)
   787  		for _, a := range attachments {
   788  			c.Assert(a.Life(), gc.Equals, state.Dying)
   789  			err := s.State.RemoveStorageAttachment(a.StorageInstance(), u.UnitTag())
   790  			c.Assert(err, jc.ErrorIsNil)
   791  		}
   792  	}).Check()
   793  
   794  	err = s.State.DestroyUnitStorageAttachments(u.UnitTag())
   795  	c.Assert(err, jc.ErrorIsNil)
   796  }
   797  
   798  func (s *StorageStateSuite) TestStorageLocationConflictIdentical(c *gc.C) {
   799  	s.testStorageLocationConflict(
   800  		c, "/srv", "/srv",
   801  		`cannot assign unit "storage-filesystem2/0" to machine 0: `+
   802  			`validating filesystem mount points: `+
   803  			`mount point "/srv" for "data-old" storage contains `+
   804  			`mount point "/srv" for "data-new" storage`,
   805  	)
   806  }
   807  
   808  func (s *StorageStateSuite) TestStorageLocationConflictIdenticalAfterCleaning(c *gc.C) {
   809  	s.testStorageLocationConflict(
   810  		c, "/srv", "/xyz/.././srv",
   811  		`cannot assign unit "storage-filesystem2/0" to machine 0: `+
   812  			`validating filesystem mount points: `+
   813  			`mount point "/srv" for "data-old" storage contains `+
   814  			`mount point "/xyz/.././srv" for "data-new" storage`,
   815  	)
   816  }
   817  
   818  func (s *StorageStateSuite) TestStorageLocationConflictSecondInsideFirst(c *gc.C) {
   819  	s.testStorageLocationConflict(
   820  		c, "/srv", "/srv/within",
   821  		`cannot assign unit "storage-filesystem2/0" to machine 0: `+
   822  			`validating filesystem mount points: `+
   823  			`mount point "/srv" for "data-old" storage contains `+
   824  			`mount point "/srv/within" for "data-new" storage`,
   825  	)
   826  }
   827  
   828  func (s *StorageStateSuite) TestStorageLocationConflictFirstInsideSecond(c *gc.C) {
   829  	s.testStorageLocationConflict(
   830  		c, "/srv/within", "/srv",
   831  		`cannot assign unit "storage-filesystem2/0" to machine 0: `+
   832  			`validating filesystem mount points: `+
   833  			`mount point "/srv" for "data-new" storage contains `+
   834  			`mount point "/srv/within" for "data-old" storage`,
   835  	)
   836  }
   837  
   838  func (s *StorageStateSuite) TestStorageLocationConflictPrefix(c *gc.C) {
   839  	s.testStorageLocationConflict(c, "/srv", "/srvtd", "")
   840  }
   841  
   842  func (s *StorageStateSuite) TestStorageLocationConflictSameParent(c *gc.C) {
   843  	s.testStorageLocationConflict(c, "/srv/1", "/srv/2", "")
   844  }
   845  
   846  func (s *StorageStateSuite) TestStorageLocationConflictAutoGenerated(c *gc.C) {
   847  	s.testStorageLocationConflict(c, "", "", "")
   848  }
   849  
   850  func (s *StorageStateSuite) testStorageLocationConflict(c *gc.C, first, second, expectErr string) {
   851  	ch1 := s.createStorageCharm(c, "storage-filesystem", charm.Storage{
   852  		Name:     "data-old",
   853  		Type:     charm.StorageFilesystem,
   854  		CountMin: 1,
   855  		CountMax: 1,
   856  		Location: first,
   857  	})
   858  	ch2 := s.createStorageCharm(c, "storage-filesystem2", charm.Storage{
   859  		Name:     "data-new",
   860  		Type:     charm.StorageFilesystem,
   861  		CountMin: 1,
   862  		CountMax: 1,
   863  		Location: second,
   864  	})
   865  	svc1 := s.AddTestingService(c, "storage-filesystem", ch1)
   866  	svc2 := s.AddTestingService(c, "storage-filesystem2", ch2)
   867  
   868  	u1, err := svc1.AddUnit()
   869  	c.Assert(err, jc.ErrorIsNil)
   870  	err = s.State.AssignUnit(u1, state.AssignCleanEmpty)
   871  	c.Assert(err, jc.ErrorIsNil)
   872  
   873  	machineId, err := u1.AssignedMachineId()
   874  	c.Assert(err, jc.ErrorIsNil)
   875  	m, err := s.State.Machine(machineId)
   876  	c.Assert(err, jc.ErrorIsNil)
   877  
   878  	u2, err := svc2.AddUnit()
   879  	c.Assert(err, jc.ErrorIsNil)
   880  	err = u2.AssignToMachine(m)
   881  	if expectErr == "" {
   882  		c.Assert(err, jc.ErrorIsNil)
   883  	} else {
   884  		c.Assert(err, gc.ErrorMatches, expectErr)
   885  	}
   886  }
   887  
   888  func mustStorageConfig(name string, provider storage.ProviderType, attrs map[string]interface{}) *storage.Config {
   889  	cfg, err := storage.NewConfig(name, provider, attrs)
   890  	if err != nil {
   891  		panic(err)
   892  	}
   893  	return cfg
   894  }
   895  
   896  var testingStorageProviders = storage.StaticProviderRegistry{
   897  	map[storage.ProviderType]storage.Provider{
   898  		"dummy": &dummystorage.StorageProvider{
   899  			DefaultPools_: []*storage.Config{radiancePool},
   900  		},
   901  		"lancashire": &dummystorage.StorageProvider{
   902  			DefaultPools_: []*storage.Config{blackPool},
   903  		},
   904  	},
   905  }
   906  
   907  var radiancePool = mustStorageConfig("radiance", "dummy", map[string]interface{}{"k": "v"})
   908  var blackPool = mustStorageConfig("black", "lancashire", map[string]interface{}{})
   909  
   910  func (s *StorageStateSuite) TestNewModelDefaultPools(c *gc.C) {
   911  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   912  		StorageProviderRegistry: testingStorageProviders,
   913  	})
   914  	s.AddCleanup(func(*gc.C) { st.Close() })
   915  
   916  	// When a model is created, it is populated with the default
   917  	// pools of each storage provider supported by the model's
   918  	// cloud provider.
   919  	pm := poolmanager.New(state.NewStateSettings(st), testingStorageProviders)
   920  	listed, err := pm.List()
   921  	c.Assert(err, jc.ErrorIsNil)
   922  	sort.Sort(byStorageConfigName(listed))
   923  	c.Assert(listed, jc.DeepEquals, []*storage.Config{blackPool, radiancePool})
   924  }
   925  
   926  type byStorageConfigName []*storage.Config
   927  
   928  func (c byStorageConfigName) Len() int {
   929  	return len(c)
   930  }
   931  
   932  func (c byStorageConfigName) Less(a, b int) bool {
   933  	return c[a].Name() < c[b].Name()
   934  }
   935  
   936  func (c byStorageConfigName) Swap(a, b int) {
   937  	c[a], c[b] = c[b], c[a]
   938  }
   939  
   940  // TODO(axw) the following require shared storage support to test:
   941  // - StorageAttachments can't be added to Dying StorageInstance
   942  // - StorageInstance without attachments is removed by Destroy
   943  // - concurrent add-unit and StorageAttachment removal does not
   944  //   remove storage instance.
   945  
   946  type StorageSubordinateStateSuite struct {
   947  	StorageStateSuiteBase
   948  
   949  	mysql                  *state.Application
   950  	mysqlUnit              *state.Unit
   951  	mysqlRelunit           *state.RelationUnit
   952  	subordinateApplication *state.Application
   953  	relation               *state.Relation
   954  }
   955  
   956  var _ = gc.Suite(&StorageSubordinateStateSuite{})
   957  
   958  func (s *StorageSubordinateStateSuite) SetUpTest(c *gc.C) {
   959  	s.StorageStateSuiteBase.SetUpTest(c)
   960  
   961  	var err error
   962  	storageCharm := s.AddTestingCharm(c, "storage-filesystem-subordinate")
   963  	s.subordinateApplication = s.AddTestingService(c, "storage-filesystem-subordinate", storageCharm)
   964  	s.mysql = s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
   965  	s.mysqlUnit, err = s.mysql.AddUnit()
   966  	c.Assert(err, jc.ErrorIsNil)
   967  
   968  	eps, err := s.State.InferEndpoints("mysql", "storage-filesystem-subordinate")
   969  	c.Assert(err, jc.ErrorIsNil)
   970  	s.relation, err = s.State.AddRelation(eps...)
   971  	c.Assert(err, jc.ErrorIsNil)
   972  	s.mysqlRelunit, err = s.relation.Unit(s.mysqlUnit)
   973  	c.Assert(err, jc.ErrorIsNil)
   974  }
   975  
   976  func (s *StorageSubordinateStateSuite) TestSubordinateStoragePrincipalUnassigned(c *gc.C) {
   977  	storageTag := names.NewStorageTag("data/0")
   978  	exists := s.storageInstanceExists(c, storageTag)
   979  	c.Assert(exists, jc.IsFalse)
   980  
   981  	err := s.mysqlRelunit.EnterScope(nil)
   982  	c.Assert(err, jc.ErrorIsNil)
   983  
   984  	// The subordinate unit will have been created, along with its storage.
   985  	exists = s.storageInstanceExists(c, storageTag)
   986  	c.Assert(exists, jc.IsTrue)
   987  
   988  	// The principal unit is not yet assigned to a machine, so there should
   989  	// be no filesystem associated with the storage instance yet.
   990  	_, err = s.State.StorageInstanceFilesystem(storageTag)
   991  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   992  
   993  	// Assigning the principal unit to a machine should cause the subordinate
   994  	// unit's machine storage to be created.
   995  	err = s.State.AssignUnit(s.mysqlUnit, state.AssignCleanEmpty)
   996  	c.Assert(err, jc.ErrorIsNil)
   997  	_ = s.storageInstanceFilesystem(c, storageTag)
   998  }
   999  
  1000  func (s *StorageSubordinateStateSuite) TestSubordinateStoragePrincipalAssigned(c *gc.C) {
  1001  	err := s.State.AssignUnit(s.mysqlUnit, state.AssignCleanEmpty)
  1002  	c.Assert(err, jc.ErrorIsNil)
  1003  
  1004  	err = s.mysqlRelunit.EnterScope(nil)
  1005  	c.Assert(err, jc.ErrorIsNil)
  1006  
  1007  	// The subordinate unit will have been created, along with its storage.
  1008  	storageTag := names.NewStorageTag("data/0")
  1009  	exists := s.storageInstanceExists(c, storageTag)
  1010  	c.Assert(exists, jc.IsTrue)
  1011  
  1012  	// The principal unit was assigned to a machine when the subordinate
  1013  	// unit was created, so there should be a filesystem associated with
  1014  	// the storage instance now.
  1015  	_ = s.storageInstanceFilesystem(c, storageTag)
  1016  }
  1017  
  1018  func (s *StorageSubordinateStateSuite) TestSubordinateStoragePrincipalAssignRace(c *gc.C) {
  1019  	// Add the subordinate before attempting to commit the transaction
  1020  	// that assigns the unit to a machine. The transaction should fail
  1021  	// and be reattempted with the knowledge of the subordinate, and
  1022  	// add the subordinate's storage.
  1023  	defer state.SetBeforeHooks(c, s.State, func() {
  1024  		err := s.mysqlRelunit.EnterScope(nil)
  1025  		c.Assert(err, jc.ErrorIsNil)
  1026  	}).Check()
  1027  
  1028  	err := s.State.AssignUnit(s.mysqlUnit, state.AssignCleanEmpty)
  1029  	c.Assert(err, jc.ErrorIsNil)
  1030  	_ = s.storageInstanceFilesystem(c, names.NewStorageTag("data/0"))
  1031  }