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