github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/filesystem_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  	"github.com/juju/errors"
     8  	"github.com/juju/names"
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  	"gopkg.in/juju/charm.v6-unstable"
    12  
    13  	"github.com/juju/juju/state"
    14  	"github.com/juju/juju/state/testing"
    15  )
    16  
    17  type FilesystemStateSuite struct {
    18  	StorageStateSuiteBase
    19  }
    20  
    21  var _ = gc.Suite(&FilesystemStateSuite{})
    22  
    23  func (s *FilesystemStateSuite) TestAddServiceInvalidPool(c *gc.C) {
    24  	ch := s.AddTestingCharm(c, "storage-filesystem")
    25  	storage := map[string]state.StorageConstraints{
    26  		"data": makeStorageCons("invalid-pool", 1024, 1),
    27  	}
    28  	_, err := s.State.AddService(state.AddServiceArgs{Name: "storage-filesystem", Owner: s.Owner.String(), Charm: ch, Storage: storage})
    29  	c.Assert(err, gc.ErrorMatches, `.* pool "invalid-pool" not found`)
    30  }
    31  
    32  func (s *FilesystemStateSuite) TestAddServiceNoPoolNoDefault(c *gc.C) {
    33  	// no pool specified, no default configured: use rootfs.
    34  	s.testAddServiceDefaultPool(c, "rootfs", 0)
    35  }
    36  
    37  func (s *FilesystemStateSuite) TestAddServiceNoPoolNoDefaultWithUnits(c *gc.C) {
    38  	// no pool specified, no default configured: use rootfs, add a unit during
    39  	// service deploy.
    40  	s.testAddServiceDefaultPool(c, "rootfs", 1)
    41  }
    42  
    43  func (s *FilesystemStateSuite) TestAddServiceNoPoolDefaultBlock(c *gc.C) {
    44  	// no pool specified, default block configured: use default
    45  	// block with managed fs on top.
    46  	err := s.State.UpdateModelConfig(map[string]interface{}{
    47  		"storage-default-block-source": "machinescoped",
    48  	}, nil, nil)
    49  	c.Assert(err, jc.ErrorIsNil)
    50  	s.testAddServiceDefaultPool(c, "machinescoped", 0)
    51  }
    52  
    53  func (s *FilesystemStateSuite) testAddServiceDefaultPool(c *gc.C, expectedPool string, numUnits int) {
    54  	ch := s.AddTestingCharm(c, "storage-filesystem")
    55  	storage := map[string]state.StorageConstraints{
    56  		"data": makeStorageCons("", 1024, 1),
    57  	}
    58  
    59  	args := state.AddServiceArgs{
    60  		Name:     "storage-filesystem",
    61  		Owner:    s.Owner.String(),
    62  		Charm:    ch,
    63  		Storage:  storage,
    64  		NumUnits: numUnits,
    65  	}
    66  	svc, err := s.State.AddService(args)
    67  	c.Assert(err, jc.ErrorIsNil)
    68  	cons, err := svc.StorageConstraints()
    69  	c.Assert(err, jc.ErrorIsNil)
    70  	expected := map[string]state.StorageConstraints{
    71  		"data": state.StorageConstraints{
    72  			Pool:  expectedPool,
    73  			Size:  1024,
    74  			Count: 1,
    75  		},
    76  	}
    77  	c.Assert(cons, jc.DeepEquals, expected)
    78  
    79  	svc, err = s.State.Service(args.Name)
    80  	c.Assert(err, jc.ErrorIsNil)
    81  
    82  	units, err := svc.AllUnits()
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	c.Assert(units, gc.HasLen, numUnits)
    85  
    86  	for _, unit := range units {
    87  		scons, err := unit.StorageConstraints()
    88  		c.Assert(err, jc.ErrorIsNil)
    89  		c.Assert(scons, gc.DeepEquals, expected)
    90  
    91  		storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag())
    92  		c.Assert(err, jc.ErrorIsNil)
    93  		c.Assert(storageAttachments, gc.HasLen, 1)
    94  		storageInstance, err := s.State.StorageInstance(storageAttachments[0].StorageInstance())
    95  		c.Assert(err, jc.ErrorIsNil)
    96  		c.Assert(storageInstance.Kind(), gc.Equals, state.StorageKindFilesystem)
    97  	}
    98  }
    99  
   100  func (s *FilesystemStateSuite) TestAddFilesystemWithoutBackingVolume(c *gc.C) {
   101  	s.addUnitWithFilesystem(c, "rootfs", false)
   102  }
   103  
   104  func (s *FilesystemStateSuite) TestAddFilesystemWithBackingVolume(c *gc.C) {
   105  	s.addUnitWithFilesystem(c, "loop", true)
   106  }
   107  
   108  func (s *FilesystemStateSuite) TestSetFilesystemInfoImmutable(c *gc.C) {
   109  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "rootfs")
   110  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   111  	c.Assert(err, jc.ErrorIsNil)
   112  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   113  	filesystemTag := filesystem.FilesystemTag()
   114  
   115  	assignedMachineId, err := u.AssignedMachineId()
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	machine, err := s.State.Machine(assignedMachineId)
   118  	c.Assert(err, jc.ErrorIsNil)
   119  	err = machine.SetProvisioned("inst-id", "fake_nonce", nil)
   120  	c.Assert(err, jc.ErrorIsNil)
   121  
   122  	filesystemInfoSet := state.FilesystemInfo{Size: 123, FilesystemId: "fs-id"}
   123  	err = s.State.SetFilesystemInfo(filesystem.FilesystemTag(), filesystemInfoSet)
   124  	c.Assert(err, jc.ErrorIsNil)
   125  
   126  	// The first call to SetFilesystemInfo takes the pool name from
   127  	// the params; the second does not, but it must not change
   128  	// either. Callers are expected to get the existing info and
   129  	// update it, leaving immutable values intact.
   130  	err = s.State.SetFilesystemInfo(filesystem.FilesystemTag(), filesystemInfoSet)
   131  	c.Assert(err, gc.ErrorMatches, `cannot set info for filesystem "0/0": cannot change pool from "rootfs" to ""`)
   132  
   133  	filesystemInfoSet.Pool = "rootfs"
   134  	s.assertFilesystemInfo(c, filesystemTag, filesystemInfoSet)
   135  }
   136  
   137  func (s *FilesystemStateSuite) TestSetFilesystemInfoNoFilesystemId(c *gc.C) {
   138  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "loop-pool")
   139  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   140  	c.Assert(err, jc.ErrorIsNil)
   141  
   142  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   143  	filesystemTag := filesystem.FilesystemTag()
   144  	s.assertFilesystemUnprovisioned(c, filesystemTag)
   145  
   146  	filesystemInfoSet := state.FilesystemInfo{Size: 123}
   147  	err = s.State.SetFilesystemInfo(filesystem.FilesystemTag(), filesystemInfoSet)
   148  	c.Assert(err, gc.ErrorMatches, `cannot set info for filesystem "0/0": filesystem ID not set`)
   149  }
   150  
   151  func (s *FilesystemStateSuite) TestVolumeFilesystem(c *gc.C) {
   152  	filesystemAttachment, _ := s.addUnitWithFilesystem(c, "loop", true)
   153  	filesystem := s.filesystem(c, filesystemAttachment.Filesystem())
   154  	_, err := filesystem.Info()
   155  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
   156  
   157  	volumeTag, err := filesystem.Volume()
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	filesystem = s.volumeFilesystem(c, volumeTag)
   160  	c.Assert(filesystem.FilesystemTag(), gc.Equals, filesystemAttachment.Filesystem())
   161  }
   162  
   163  func (s *FilesystemStateSuite) addUnitWithFilesystem(c *gc.C, pool string, withVolume bool) (state.FilesystemAttachment, state.StorageAttachment) {
   164  	ch := s.AddTestingCharm(c, "storage-filesystem")
   165  	storage := map[string]state.StorageConstraints{
   166  		"data": makeStorageCons(pool, 1024, 1),
   167  	}
   168  	service := s.AddTestingServiceWithStorage(c, "storage-filesystem", ch, storage)
   169  	unit, err := service.AddUnit()
   170  	c.Assert(err, jc.ErrorIsNil)
   171  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	assignedMachineId, err := unit.AssignedMachineId()
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	assignedMachineTag := names.NewMachineTag(assignedMachineId)
   176  
   177  	storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag())
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	c.Assert(storageAttachments, gc.HasLen, 1)
   180  	storageInstance, err := s.State.StorageInstance(storageAttachments[0].StorageInstance())
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	c.Assert(storageInstance.Kind(), gc.Equals, state.StorageKindFilesystem)
   183  
   184  	filesystem := s.storageInstanceFilesystem(c, storageInstance.StorageTag())
   185  	c.Assert(filesystem.FilesystemTag(), gc.Equals, names.NewFilesystemTag("0/0"))
   186  	filesystemStorageTag, err := filesystem.Storage()
   187  	c.Assert(err, jc.ErrorIsNil)
   188  	c.Assert(filesystemStorageTag, gc.Equals, storageInstance.StorageTag())
   189  	_, err = filesystem.Info()
   190  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
   191  	_, ok := filesystem.Params()
   192  	c.Assert(ok, jc.IsTrue)
   193  
   194  	volume, err := s.State.StorageInstanceVolume(storageInstance.StorageTag())
   195  	if withVolume {
   196  		c.Assert(err, jc.ErrorIsNil)
   197  		c.Assert(volume.VolumeTag(), gc.Equals, names.NewVolumeTag("0/0"))
   198  		volumeStorageTag, err := volume.StorageInstance()
   199  		c.Assert(err, jc.ErrorIsNil)
   200  		c.Assert(volumeStorageTag, gc.Equals, storageInstance.StorageTag())
   201  		filesystemVolume, err := filesystem.Volume()
   202  		c.Assert(err, jc.ErrorIsNil)
   203  		c.Assert(filesystemVolume, gc.Equals, volume.VolumeTag())
   204  		_, err = s.State.VolumeAttachment(assignedMachineTag, filesystemVolume)
   205  		c.Assert(err, jc.ErrorIsNil)
   206  	} else {
   207  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
   208  		_, err = filesystem.Volume()
   209  		c.Assert(errors.Cause(err), gc.Equals, state.ErrNoBackingVolume)
   210  	}
   211  
   212  	machine, err := s.State.Machine(assignedMachineId)
   213  	c.Assert(err, jc.ErrorIsNil)
   214  	filesystemAttachments, err := s.State.MachineFilesystemAttachments(assignedMachineTag)
   215  	c.Assert(err, jc.ErrorIsNil)
   216  	c.Assert(filesystemAttachments, gc.HasLen, 1)
   217  	c.Assert(filesystemAttachments[0].Filesystem(), gc.Equals, filesystem.FilesystemTag())
   218  	c.Assert(filesystemAttachments[0].Machine(), gc.Equals, machine.MachineTag())
   219  	_, err = filesystemAttachments[0].Info()
   220  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
   221  	_, ok = filesystemAttachments[0].Params()
   222  	c.Assert(ok, jc.IsTrue)
   223  
   224  	assertMachineStorageRefs(c, s.State, machine.MachineTag())
   225  
   226  	att, err := s.State.FilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag())
   227  	c.Assert(err, jc.ErrorIsNil)
   228  	return att, storageAttachments[0]
   229  }
   230  
   231  func (s *FilesystemStateSuite) TestWatchFilesystemAttachment(c *gc.C) {
   232  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "rootfs")
   233  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   234  	c.Assert(err, jc.ErrorIsNil)
   235  	assignedMachineId, err := u.AssignedMachineId()
   236  	c.Assert(err, jc.ErrorIsNil)
   237  	machineTag := names.NewMachineTag(assignedMachineId)
   238  
   239  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   240  	filesystemTag := filesystem.FilesystemTag()
   241  
   242  	w := s.State.WatchFilesystemAttachment(machineTag, filesystemTag)
   243  	defer testing.AssertStop(c, w)
   244  	wc := testing.NewNotifyWatcherC(c, s.State, w)
   245  	wc.AssertOneChange()
   246  
   247  	machine, err := s.State.Machine(assignedMachineId)
   248  	c.Assert(err, jc.ErrorIsNil)
   249  	err = machine.SetProvisioned("inst-id", "fake_nonce", nil)
   250  	c.Assert(err, jc.ErrorIsNil)
   251  
   252  	// filesystem attachment will NOT react to filesystem changes
   253  	err = s.State.SetFilesystemInfo(filesystemTag, state.FilesystemInfo{
   254  		FilesystemId: "fs-123",
   255  	})
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	wc.AssertNoChange()
   258  
   259  	err = s.State.SetFilesystemAttachmentInfo(
   260  		machineTag, filesystemTag, state.FilesystemAttachmentInfo{
   261  			MountPoint: "/srv",
   262  		},
   263  	)
   264  	c.Assert(err, jc.ErrorIsNil)
   265  	wc.AssertOneChange()
   266  }
   267  
   268  func (s *FilesystemStateSuite) TestFilesystemInfo(c *gc.C) {
   269  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "rootfs")
   270  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   271  	c.Assert(err, jc.ErrorIsNil)
   272  	assignedMachineId, err := u.AssignedMachineId()
   273  	c.Assert(err, jc.ErrorIsNil)
   274  	machineTag := names.NewMachineTag(assignedMachineId)
   275  
   276  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   277  	filesystemTag := filesystem.FilesystemTag()
   278  
   279  	s.assertFilesystemUnprovisioned(c, filesystemTag)
   280  	s.assertFilesystemAttachmentUnprovisioned(c, machineTag, filesystemTag)
   281  
   282  	machine, err := s.State.Machine(assignedMachineId)
   283  	c.Assert(err, jc.ErrorIsNil)
   284  	err = machine.SetProvisioned("inst-id", "fake_nonce", nil)
   285  	c.Assert(err, jc.ErrorIsNil)
   286  
   287  	filesystemInfo := state.FilesystemInfo{FilesystemId: "fs-123", Size: 456}
   288  	err = s.State.SetFilesystemInfo(filesystemTag, filesystemInfo)
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	filesystemInfo.Pool = "rootfs" // taken from params
   291  	s.assertFilesystemInfo(c, filesystemTag, filesystemInfo)
   292  	s.assertFilesystemAttachmentUnprovisioned(c, machineTag, filesystemTag)
   293  
   294  	filesystemAttachmentInfo := state.FilesystemAttachmentInfo{MountPoint: "/srv"}
   295  	err = s.State.SetFilesystemAttachmentInfo(machineTag, filesystemTag, filesystemAttachmentInfo)
   296  	c.Assert(err, jc.ErrorIsNil)
   297  	s.assertFilesystemAttachmentInfo(c, machineTag, filesystemTag, filesystemAttachmentInfo)
   298  }
   299  
   300  func (s *FilesystemStateSuite) TestVolumeBackedFilesystemScope(c *gc.C) {
   301  	_, unit, storageTag := s.setupSingleStorage(c, "filesystem", "environscoped-block")
   302  	err := s.State.AssignUnit(unit, state.AssignCleanEmpty)
   303  	c.Assert(err, jc.ErrorIsNil)
   304  
   305  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   306  	c.Assert(filesystem.Tag(), gc.Equals, names.NewFilesystemTag("0/0"))
   307  	volumeTag, err := filesystem.Volume()
   308  	c.Assert(err, jc.ErrorIsNil)
   309  	c.Assert(volumeTag, gc.Equals, names.NewVolumeTag("0"))
   310  }
   311  
   312  func (s *FilesystemStateSuite) TestWatchModelFilesystems(c *gc.C) {
   313  	service := s.setupMixedScopeStorageService(c, "filesystem")
   314  	addUnit := func() {
   315  		u, err := service.AddUnit()
   316  		c.Assert(err, jc.ErrorIsNil)
   317  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   318  		c.Assert(err, jc.ErrorIsNil)
   319  	}
   320  	addUnit()
   321  
   322  	w := s.State.WatchModelFilesystems()
   323  	defer testing.AssertStop(c, w)
   324  	wc := testing.NewStringsWatcherC(c, s.State, w)
   325  	wc.AssertChangeInSingleEvent("0") // initial
   326  	wc.AssertNoChange()
   327  
   328  	addUnit()
   329  	wc.AssertChangeInSingleEvent("3")
   330  	wc.AssertNoChange()
   331  
   332  	// TODO(axw) respond to Dying/Dead when we have
   333  	// the means to progress Filesystem lifecycle.
   334  }
   335  
   336  func (s *FilesystemStateSuite) TestWatchEnvironFilesystemAttachments(c *gc.C) {
   337  	service := s.setupMixedScopeStorageService(c, "filesystem")
   338  	addUnit := func() {
   339  		u, err := service.AddUnit()
   340  		c.Assert(err, jc.ErrorIsNil)
   341  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   342  		c.Assert(err, jc.ErrorIsNil)
   343  	}
   344  	addUnit()
   345  
   346  	w := s.State.WatchEnvironFilesystemAttachments()
   347  	defer testing.AssertStop(c, w)
   348  	wc := testing.NewStringsWatcherC(c, s.State, w)
   349  	wc.AssertChangeInSingleEvent("0:0") // initial
   350  	wc.AssertNoChange()
   351  
   352  	addUnit()
   353  	wc.AssertChangeInSingleEvent("1:3")
   354  	wc.AssertNoChange()
   355  
   356  	// TODO(axw) respond to Dying/Dead when we have
   357  	// the means to progress Volume lifecycle.
   358  }
   359  
   360  func (s *FilesystemStateSuite) TestWatchMachineFilesystems(c *gc.C) {
   361  	service := s.setupMixedScopeStorageService(c, "filesystem")
   362  	addUnit := func() {
   363  		u, err := service.AddUnit()
   364  		c.Assert(err, jc.ErrorIsNil)
   365  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   366  		c.Assert(err, jc.ErrorIsNil)
   367  	}
   368  	addUnit()
   369  
   370  	w := s.State.WatchMachineFilesystems(names.NewMachineTag("0"))
   371  	defer testing.AssertStop(c, w)
   372  	wc := testing.NewStringsWatcherC(c, s.State, w)
   373  	wc.AssertChangeInSingleEvent("0/1", "0/2") // initial
   374  	wc.AssertNoChange()
   375  
   376  	addUnit()
   377  	// no change, since we're only interested in the one machine.
   378  	wc.AssertNoChange()
   379  
   380  	// TODO(axw) respond to Dying/Dead when we have
   381  	// the means to progress Filesystem lifecycle.
   382  }
   383  
   384  func (s *FilesystemStateSuite) TestWatchMachineFilesystemAttachments(c *gc.C) {
   385  	service := s.setupMixedScopeStorageService(c, "filesystem")
   386  	addUnit := func(to *state.Machine) (u *state.Unit, m *state.Machine) {
   387  		var err error
   388  		u, err = service.AddUnit()
   389  		c.Assert(err, jc.ErrorIsNil)
   390  		if to != nil {
   391  			err = u.AssignToMachine(to)
   392  			c.Assert(err, jc.ErrorIsNil)
   393  			return u, to
   394  		}
   395  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   396  		c.Assert(err, jc.ErrorIsNil)
   397  		mid, err := u.AssignedMachineId()
   398  		c.Assert(err, jc.ErrorIsNil)
   399  		m, err = s.State.Machine(mid)
   400  		c.Assert(err, jc.ErrorIsNil)
   401  		return u, m
   402  	}
   403  	_, m0 := addUnit(nil)
   404  
   405  	w := s.State.WatchMachineFilesystemAttachments(names.NewMachineTag("0"))
   406  	defer testing.AssertStop(c, w)
   407  	wc := testing.NewStringsWatcherC(c, s.State, w)
   408  	wc.AssertChangeInSingleEvent("0:0/1", "0:0/2") // initial
   409  	wc.AssertNoChange()
   410  
   411  	addUnit(nil)
   412  	// no change, since we're only interested in the one machine.
   413  	wc.AssertNoChange()
   414  
   415  	err := s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("0"))
   416  	c.Assert(err, jc.ErrorIsNil)
   417  	// no change, since we're only interested in attachments of
   418  	// machine-scoped volumes.
   419  	wc.AssertNoChange()
   420  
   421  	err = s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("0/1"))
   422  	c.Assert(err, jc.ErrorIsNil)
   423  	wc.AssertChangeInSingleEvent("0:0/1") // dying
   424  	wc.AssertNoChange()
   425  
   426  	err = s.State.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("0/1"))
   427  	c.Assert(err, jc.ErrorIsNil)
   428  	wc.AssertChangeInSingleEvent("0:0/1") // removed
   429  	wc.AssertNoChange()
   430  
   431  	addUnit(m0)
   432  	wc.AssertChangeInSingleEvent("0:0/7", "0:0/8")
   433  	wc.AssertNoChange()
   434  }
   435  
   436  func (s *FilesystemStateSuite) TestParseFilesystemAttachmentId(c *gc.C) {
   437  	assertValid := func(id string, m names.MachineTag, v names.FilesystemTag) {
   438  		machineTag, filesystemTag, err := state.ParseFilesystemAttachmentId(id)
   439  		c.Assert(err, jc.ErrorIsNil)
   440  		c.Assert(machineTag, gc.Equals, m)
   441  		c.Assert(filesystemTag, gc.Equals, v)
   442  	}
   443  	assertValid("0:0", names.NewMachineTag("0"), names.NewFilesystemTag("0"))
   444  	assertValid("0:0/1", names.NewMachineTag("0"), names.NewFilesystemTag("0/1"))
   445  	assertValid("0/lxc/0:1", names.NewMachineTag("0/lxc/0"), names.NewFilesystemTag("1"))
   446  }
   447  
   448  func (s *FilesystemStateSuite) TestParseFilesystemAttachmentIdError(c *gc.C) {
   449  	assertError := func(id, expect string) {
   450  		_, _, err := state.ParseFilesystemAttachmentId(id)
   451  		c.Assert(err, gc.ErrorMatches, expect)
   452  	}
   453  	assertError("", `invalid filesystem attachment ID ""`)
   454  	assertError("0", `invalid filesystem attachment ID "0"`)
   455  	assertError("0:foo", `invalid filesystem attachment ID "0:foo"`)
   456  	assertError("bar:0", `invalid filesystem attachment ID "bar:0"`)
   457  }
   458  
   459  func (s *FilesystemStateSuite) TestRemoveStorageInstanceUnassignsFilesystem(c *gc.C) {
   460  	filesystemAttachment, storageAttachment := s.addUnitWithFilesystem(c, "loop", true)
   461  	filesystem := s.filesystem(c, filesystemAttachment.Filesystem())
   462  	volume := s.filesystemVolume(c, filesystemAttachment.Filesystem())
   463  	storageTag := storageAttachment.StorageInstance()
   464  	unitTag := storageAttachment.Unit()
   465  
   466  	err := s.State.DestroyStorageInstance(storageTag)
   467  	c.Assert(err, jc.ErrorIsNil)
   468  	err = s.State.DestroyStorageAttachment(storageTag, unitTag)
   469  	c.Assert(err, jc.ErrorIsNil)
   470  
   471  	// The storage instance and attachment are dying, but not yet
   472  	// removed from state. The filesystem should still be assigned.
   473  	s.storageInstanceFilesystem(c, storageTag)
   474  	s.storageInstanceVolume(c, storageTag)
   475  
   476  	err = s.State.RemoveStorageAttachment(storageTag, unitTag)
   477  	c.Assert(err, jc.ErrorIsNil)
   478  
   479  	// The storage instance is now gone; the filesystem should no longer
   480  	// be assigned to the storage.
   481  	_, err = s.State.StorageInstanceFilesystem(storageTag)
   482  	c.Assert(err, gc.ErrorMatches, `filesystem for storage instance "data/0" not found`)
   483  	_, err = s.State.StorageInstanceVolume(storageTag)
   484  	c.Assert(err, gc.ErrorMatches, `volume for storage instance "data/0" not found`)
   485  
   486  	// The filesystem and volume should not have been destroyed, though.
   487  	s.filesystem(c, filesystem.FilesystemTag())
   488  	s.volume(c, volume.VolumeTag())
   489  }
   490  
   491  func (s *FilesystemStateSuite) TestSetFilesystemAttachmentInfoFilesystemNotProvisioned(c *gc.C) {
   492  	filesystemAttachment, _ := s.addUnitWithFilesystem(c, "rootfs", false)
   493  	err := s.State.SetFilesystemAttachmentInfo(
   494  		filesystemAttachment.Machine(),
   495  		filesystemAttachment.Filesystem(),
   496  		state.FilesystemAttachmentInfo{},
   497  	)
   498  	c.Assert(err, gc.ErrorMatches, `cannot set info for filesystem attachment 0/0:0: filesystem "0/0" not provisioned`)
   499  }
   500  
   501  func (s *FilesystemStateSuite) TestSetFilesystemAttachmentInfoMachineNotProvisioned(c *gc.C) {
   502  	filesystemAttachment, _ := s.addUnitWithFilesystem(c, "rootfs", false)
   503  	err := s.State.SetFilesystemInfo(
   504  		filesystemAttachment.Filesystem(),
   505  		state.FilesystemInfo{Size: 123, FilesystemId: "fs-id"},
   506  	)
   507  	c.Assert(err, jc.ErrorIsNil)
   508  	err = s.State.SetFilesystemAttachmentInfo(
   509  		filesystemAttachment.Machine(),
   510  		filesystemAttachment.Filesystem(),
   511  		state.FilesystemAttachmentInfo{},
   512  	)
   513  	c.Assert(err, gc.ErrorMatches, `cannot set info for filesystem attachment 0/0:0: machine 0 not provisioned`)
   514  }
   515  
   516  func (s *FilesystemStateSuite) TestSetFilesystemInfoVolumeAttachmentNotProvisioned(c *gc.C) {
   517  	filesystemAttachment, _ := s.addUnitWithFilesystem(c, "loop", true)
   518  	err := s.State.SetFilesystemInfo(
   519  		filesystemAttachment.Filesystem(),
   520  		state.FilesystemInfo{Size: 123, FilesystemId: "fs-id"},
   521  	)
   522  	c.Assert(err, gc.ErrorMatches, `cannot set info for filesystem "0/0": volume attachment "0/0" on "0" not provisioned`)
   523  }
   524  
   525  func (s *FilesystemStateSuite) TestDestroyFilesystem(c *gc.C) {
   526  	filesystem, _ := s.setupFilesystemAttachment(c, "rootfs")
   527  	assertDestroy := func() {
   528  		err := s.State.DestroyFilesystem(filesystem.FilesystemTag())
   529  		c.Assert(err, jc.ErrorIsNil)
   530  		filesystem = s.filesystem(c, filesystem.FilesystemTag())
   531  		c.Assert(filesystem.Life(), gc.Equals, state.Dying)
   532  	}
   533  	defer state.SetBeforeHooks(c, s.State, assertDestroy).Check()
   534  	assertDestroy()
   535  }
   536  
   537  func (s *FilesystemStateSuite) TestDestroyFilesystemNoAttachments(c *gc.C) {
   538  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   539  
   540  	err := s.State.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   541  	c.Assert(err, jc.ErrorIsNil)
   542  
   543  	defer state.SetBeforeHooks(c, s.State, func() {
   544  		err := s.State.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag())
   545  		c.Assert(err, jc.ErrorIsNil)
   546  		assertMachineStorageRefs(c, s.State, machine.MachineTag())
   547  	}).Check()
   548  
   549  	err = s.State.DestroyFilesystem(filesystem.FilesystemTag())
   550  	c.Assert(err, jc.ErrorIsNil)
   551  	filesystem = s.filesystem(c, filesystem.FilesystemTag())
   552  
   553  	// There are no more attachments, so the filesystem should
   554  	// have been progressed directly to Dead.
   555  	c.Assert(filesystem.Life(), gc.Equals, state.Dead)
   556  }
   557  
   558  func (s *FilesystemStateSuite) TestRemoveFilesystem(c *gc.C) {
   559  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   560  	err := s.State.DestroyFilesystem(filesystem.FilesystemTag())
   561  	c.Assert(err, jc.ErrorIsNil)
   562  	err = s.State.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   563  	c.Assert(err, jc.ErrorIsNil)
   564  	err = s.State.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag())
   565  	c.Assert(err, jc.ErrorIsNil)
   566  	assertRemove := func() {
   567  		err = s.State.RemoveFilesystem(filesystem.FilesystemTag())
   568  		c.Assert(err, jc.ErrorIsNil)
   569  		_, err = s.State.Filesystem(filesystem.FilesystemTag())
   570  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
   571  	}
   572  	defer state.SetBeforeHooks(c, s.State, assertRemove).Check()
   573  	assertRemove()
   574  }
   575  
   576  func (s *FilesystemStateSuite) TestRemoveFilesystemVolumeBacked(c *gc.C) {
   577  	filesystem, machine := s.setupFilesystemAttachment(c, "loop")
   578  	volume := s.filesystemVolume(c, filesystem.FilesystemTag())
   579  	assertVolumeLife := func(life state.Life) {
   580  		volume := s.volume(c, volume.VolumeTag())
   581  		c.Assert(volume.Life(), gc.Equals, life)
   582  	}
   583  	assertVolumeAttachmentLife := func(life state.Life) {
   584  		attachment := s.volumeAttachment(c, machine.MachineTag(), volume.VolumeTag())
   585  		c.Assert(attachment.Life(), gc.Equals, life)
   586  	}
   587  
   588  	err := s.State.DestroyFilesystem(filesystem.FilesystemTag())
   589  	c.Assert(err, jc.ErrorIsNil)
   590  	// Destroying the filesystem does not trigger destruction
   591  	// of the volume. It cannot be destroyed until all remnants
   592  	// of the filesystem are gone.
   593  	assertVolumeLife(state.Alive)
   594  
   595  	err = s.State.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   596  	c.Assert(err, jc.ErrorIsNil)
   597  	// Likewise for the volume attachment.
   598  	assertVolumeAttachmentLife(state.Alive)
   599  
   600  	err = s.State.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag())
   601  	c.Assert(err, jc.ErrorIsNil)
   602  	// Removing the filesystem attachment causes the backing-volume
   603  	// to be detached.
   604  	assertVolumeAttachmentLife(state.Dying)
   605  
   606  	err = s.State.RemoveFilesystem(filesystem.FilesystemTag())
   607  	c.Assert(err, jc.ErrorIsNil)
   608  	// Removing the filesystem causes the backing-volume to be
   609  	// destroyed.
   610  	assertVolumeLife(state.Dying)
   611  
   612  	assertMachineStorageRefs(c, s.State, machine.MachineTag())
   613  }
   614  
   615  func (s *FilesystemStateSuite) TestFilesystemVolumeBackedDestroyDetachVolumeFail(c *gc.C) {
   616  	filesystem, machine := s.setupFilesystemAttachment(c, "loop")
   617  	volume := s.filesystemVolume(c, filesystem.FilesystemTag())
   618  
   619  	err := s.State.DestroyFilesystem(filesystem.FilesystemTag())
   620  	c.Assert(err, jc.ErrorIsNil)
   621  	err = s.State.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   622  	c.Assert(err, jc.ErrorIsNil)
   623  
   624  	// Can't destroy (detach) volume until the filesystem (attachment) is removed.
   625  	err = s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag())
   626  	c.Assert(err, gc.ErrorMatches, "detaching volume 0/0 from machine 0: volume contains attached filesystem")
   627  	c.Assert(err, jc.Satisfies, state.IsContainsFilesystem)
   628  	err = s.State.DestroyVolume(volume.VolumeTag())
   629  	c.Assert(err, gc.ErrorMatches, "destroying volume 0/0: volume contains filesystem")
   630  	c.Assert(err, jc.Satisfies, state.IsContainsFilesystem)
   631  	assertMachineStorageRefs(c, s.State, machine.MachineTag())
   632  
   633  	err = s.State.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag())
   634  	c.Assert(err, jc.ErrorIsNil)
   635  	err = s.State.RemoveFilesystem(filesystem.FilesystemTag())
   636  	c.Assert(err, jc.ErrorIsNil)
   637  
   638  	err = s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag())
   639  	c.Assert(err, jc.ErrorIsNil)
   640  	err = s.State.DestroyVolume(volume.VolumeTag())
   641  	c.Assert(err, jc.ErrorIsNil)
   642  }
   643  
   644  func (s *FilesystemStateSuite) TestRemoveFilesystemNotFound(c *gc.C) {
   645  	err := s.State.RemoveFilesystem(names.NewFilesystemTag("42"))
   646  	c.Assert(err, jc.ErrorIsNil)
   647  }
   648  
   649  func (s *FilesystemStateSuite) TestRemoveFilesystemNotDead(c *gc.C) {
   650  	filesystem, _ := s.setupFilesystemAttachment(c, "rootfs")
   651  	err := s.State.RemoveFilesystem(filesystem.FilesystemTag())
   652  	c.Assert(err, gc.ErrorMatches, "removing filesystem 0/0: filesystem is not dead")
   653  	err = s.State.DestroyFilesystem(filesystem.FilesystemTag())
   654  	c.Assert(err, jc.ErrorIsNil)
   655  	err = s.State.RemoveFilesystem(filesystem.FilesystemTag())
   656  	c.Assert(err, gc.ErrorMatches, "removing filesystem 0/0: filesystem is not dead")
   657  }
   658  
   659  func (s *FilesystemStateSuite) TestDetachFilesystem(c *gc.C) {
   660  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   661  	assertDetach := func() {
   662  		err := s.State.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   663  		c.Assert(err, jc.ErrorIsNil)
   664  		attachment := s.filesystemAttachment(c, machine.MachineTag(), filesystem.FilesystemTag())
   665  		c.Assert(attachment.Life(), gc.Equals, state.Dying)
   666  	}
   667  	defer state.SetBeforeHooks(c, s.State, assertDetach).Check()
   668  	assertDetach()
   669  }
   670  
   671  func (s *FilesystemStateSuite) TestRemoveLastFilesystemAttachment(c *gc.C) {
   672  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   673  
   674  	err := s.State.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   675  	c.Assert(err, jc.ErrorIsNil)
   676  
   677  	err = s.State.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag())
   678  	c.Assert(err, jc.ErrorIsNil)
   679  
   680  	err = s.State.DestroyFilesystem(filesystem.FilesystemTag())
   681  	c.Assert(err, jc.ErrorIsNil)
   682  	filesystem = s.filesystem(c, filesystem.FilesystemTag())
   683  	// The filesystem had no attachments when it was destroyed,
   684  	// so it should be Dead.
   685  	c.Assert(filesystem.Life(), gc.Equals, state.Dead)
   686  	assertMachineStorageRefs(c, s.State, machine.MachineTag())
   687  }
   688  
   689  func (s *FilesystemStateSuite) TestRemoveLastFilesystemAttachmentConcurrently(c *gc.C) {
   690  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   691  
   692  	err := s.State.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   693  	c.Assert(err, jc.ErrorIsNil)
   694  
   695  	defer state.SetBeforeHooks(c, s.State, func() {
   696  		err := s.State.DestroyFilesystem(filesystem.FilesystemTag())
   697  		c.Assert(err, jc.ErrorIsNil)
   698  		filesystem := s.filesystem(c, filesystem.FilesystemTag())
   699  		c.Assert(filesystem.Life(), gc.Equals, state.Dying)
   700  	}).Check()
   701  
   702  	err = s.State.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag())
   703  	c.Assert(err, jc.ErrorIsNil)
   704  
   705  	// Last attachment was removed, and the filesystem was (concurrently)
   706  	// destroyed, so the filesystem should be Dead.
   707  	filesystem = s.filesystem(c, filesystem.FilesystemTag())
   708  	c.Assert(filesystem.Life(), gc.Equals, state.Dead)
   709  	assertMachineStorageRefs(c, s.State, machine.MachineTag())
   710  }
   711  
   712  func (s *FilesystemStateSuite) TestRemoveFilesystemAttachmentNotFound(c *gc.C) {
   713  	err := s.State.RemoveFilesystemAttachment(names.NewMachineTag("42"), names.NewFilesystemTag("42"))
   714  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   715  	c.Assert(err, gc.ErrorMatches, `removing attachment of filesystem 42 from machine 42: filesystem "42" on machine "42" not found`)
   716  }
   717  
   718  func (s *FilesystemStateSuite) TestRemoveFilesystemAttachmentConcurrently(c *gc.C) {
   719  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   720  	err := s.State.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   721  	c.Assert(err, jc.ErrorIsNil)
   722  	remove := func() {
   723  		err := s.State.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag())
   724  		c.Assert(err, jc.ErrorIsNil)
   725  		assertMachineStorageRefs(c, s.State, machine.MachineTag())
   726  	}
   727  	defer state.SetBeforeHooks(c, s.State, remove).Check()
   728  	remove()
   729  }
   730  
   731  func (s *FilesystemStateSuite) TestRemoveFilesystemAttachmentAlive(c *gc.C) {
   732  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   733  	err := s.State.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag())
   734  	c.Assert(err, gc.ErrorMatches, "removing attachment of filesystem 0/0 from machine 0: filesystem attachment is not dying")
   735  }
   736  
   737  func (s *FilesystemStateSuite) TestRemoveMachineRemovesFilesystems(c *gc.C) {
   738  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   739  
   740  	c.Assert(machine.Destroy(), jc.ErrorIsNil)
   741  	c.Assert(machine.EnsureDead(), jc.ErrorIsNil)
   742  	c.Assert(machine.Remove(), jc.ErrorIsNil)
   743  
   744  	// Machine is gone: filesystem should be gone too.
   745  	_, err := s.State.Filesystem(filesystem.FilesystemTag())
   746  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   747  
   748  	attachments, err := s.State.MachineFilesystemAttachments(machine.MachineTag())
   749  	c.Assert(err, jc.ErrorIsNil)
   750  	c.Assert(attachments, gc.HasLen, 0)
   751  }
   752  
   753  func (s *FilesystemStateSuite) TestFilesystemBindingMachine(c *gc.C) {
   754  	// Filesystems created unassigned to a storage instance are
   755  	// bound to the initially attached machine.
   756  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   757  	c.Assert(filesystem.LifeBinding(), gc.Equals, machine.Tag())
   758  
   759  	err := s.State.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   760  	c.Assert(err, jc.ErrorIsNil)
   761  	err = s.State.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag())
   762  	c.Assert(err, jc.ErrorIsNil)
   763  	filesystem = s.filesystem(c, filesystem.FilesystemTag())
   764  	c.Assert(filesystem.Life(), gc.Equals, state.Dead)
   765  
   766  	// TODO(axw) when we can assign storage to an existing filesystem, we
   767  	// should test that a machine-bound filesystem is not destroyed when
   768  	// its assigned storage instance is removed.
   769  }
   770  
   771  func (s *FilesystemStateSuite) TestFilesystemBindingStorage(c *gc.C) {
   772  	// Filesystems created assigned to a storage instance are bound
   773  	// to the storage instance.
   774  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "rootfs")
   775  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   776  	c.Assert(err, jc.ErrorIsNil)
   777  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   778  	c.Assert(filesystem.LifeBinding(), gc.Equals, storageTag)
   779  
   780  	err = s.State.DestroyStorageInstance(storageTag)
   781  	c.Assert(err, jc.ErrorIsNil)
   782  	attachments, err := s.State.StorageAttachments(storageTag)
   783  	c.Assert(err, jc.ErrorIsNil)
   784  	for _, a := range attachments {
   785  		err = s.State.DestroyStorageAttachment(storageTag, a.Unit())
   786  		c.Assert(err, jc.ErrorIsNil)
   787  		err = s.State.RemoveStorageAttachment(storageTag, a.Unit())
   788  		c.Assert(err, jc.ErrorIsNil)
   789  	}
   790  
   791  	// The storage instance should be removed,
   792  	// and the filesystem should be Dying.
   793  	_, err = s.State.StorageInstance(storageTag)
   794  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   795  	filesystem = s.filesystem(c, filesystem.FilesystemTag())
   796  	c.Assert(filesystem.Life(), gc.Equals, state.Dying)
   797  }
   798  
   799  func (s *FilesystemStateSuite) TestFilesystemVolumeBinding(c *gc.C) {
   800  	// A volume backing a filesystem is bound to the filesystem.
   801  	filesystem, _ := s.setupFilesystemAttachment(c, "loop")
   802  	volume := s.filesystemVolume(c, filesystem.FilesystemTag())
   803  	c.Assert(volume.LifeBinding(), gc.Equals, filesystem.Tag())
   804  
   805  	// TestRemoveFilesystemVolumeBacked tests that removal of
   806  	// filesystem destroys volume.
   807  }
   808  
   809  func (s *FilesystemStateSuite) TestEnsureMachineDeadAddFilesystemConcurrently(c *gc.C) {
   810  	_, machine := s.setupFilesystemAttachment(c, "rootfs")
   811  	addFilesystem := func() {
   812  		_, u, _ := s.setupSingleStorage(c, "filesystem", "rootfs")
   813  		err := u.AssignToMachine(machine)
   814  		c.Assert(err, jc.ErrorIsNil)
   815  		s.obliterateUnit(c, u.UnitTag())
   816  	}
   817  	defer state.SetBeforeHooks(c, s.State, addFilesystem).Check()
   818  
   819  	// Adding another filesystem to the machine will cause EnsureDead to
   820  	// retry, but it will succeed because both filesystems are inherently
   821  	// machine-bound.
   822  	err := machine.EnsureDead()
   823  	c.Assert(err, jc.ErrorIsNil)
   824  }
   825  
   826  func (s *FilesystemStateSuite) TestEnsureMachineDeadRemoveFilesystemConcurrently(c *gc.C) {
   827  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   828  	removeFilesystem := func() {
   829  		s.obliterateFilesystem(c, filesystem.FilesystemTag())
   830  	}
   831  	defer state.SetBeforeHooks(c, s.State, removeFilesystem).Check()
   832  
   833  	// Removing a filesystem concurrently does not cause a transaction failure.
   834  	err := machine.EnsureDead()
   835  	c.Assert(err, jc.ErrorIsNil)
   836  }
   837  
   838  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsSingletonNoLocation(c *gc.C) {
   839  	s.testFilesystemAttachmentParams(c, 0, 1, "", state.FilesystemAttachmentParams{
   840  		Location: "/var/lib/juju/storage/data/0",
   841  	})
   842  }
   843  
   844  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsMultipleNoLocation(c *gc.C) {
   845  	s.testFilesystemAttachmentParams(c, 0, -1, "", state.FilesystemAttachmentParams{
   846  		Location: "/var/lib/juju/storage/data/0",
   847  	})
   848  }
   849  
   850  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsSingletonLocation(c *gc.C) {
   851  	s.testFilesystemAttachmentParams(c, 0, 1, "/srv", state.FilesystemAttachmentParams{
   852  		Location: "/srv",
   853  	})
   854  }
   855  
   856  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsMultipleLocation(c *gc.C) {
   857  	s.testFilesystemAttachmentParams(c, 0, -1, "/srv", state.FilesystemAttachmentParams{
   858  		Location: "/srv/data/0",
   859  	})
   860  }
   861  
   862  func (s *FilesystemStateSuite) testFilesystemAttachmentParams(
   863  	c *gc.C, countMin, countMax int, location string,
   864  	expect state.FilesystemAttachmentParams,
   865  ) {
   866  	ch := s.createStorageCharm(c, "storage-filesystem", charm.Storage{
   867  		Name:     "data",
   868  		Type:     charm.StorageFilesystem,
   869  		CountMin: countMin,
   870  		CountMax: countMax,
   871  		Location: location,
   872  	})
   873  	storage := map[string]state.StorageConstraints{
   874  		"data": makeStorageCons("rootfs", 1024, 1),
   875  	}
   876  
   877  	service := s.AddTestingServiceWithStorage(c, "storage-filesystem", ch, storage)
   878  	unit, err := service.AddUnit()
   879  	c.Assert(err, jc.ErrorIsNil)
   880  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
   881  	c.Assert(err, jc.ErrorIsNil)
   882  	machineId, err := unit.AssignedMachineId()
   883  	c.Assert(err, jc.ErrorIsNil)
   884  
   885  	storageTag := names.NewStorageTag("data/0")
   886  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   887  	filesystemAttachment := s.filesystemAttachment(
   888  		c, names.NewMachineTag(machineId), filesystem.FilesystemTag(),
   889  	)
   890  	params, ok := filesystemAttachment.Params()
   891  	c.Assert(ok, jc.IsTrue)
   892  	c.Assert(params, jc.DeepEquals, expect)
   893  }
   894  
   895  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsLocationConflictConcurrent(c *gc.C) {
   896  	s.testFilesystemAttachmentParamsConcurrent(
   897  		c, "/srv", "/srv",
   898  		`cannot assign unit "storage-filesystem-after/0" to machine 0: `+
   899  			`validating filesystem mount points: `+
   900  			`mount point "/srv" for "data" storage contains mount point "/srv" for "data" storage`)
   901  }
   902  
   903  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsLocationAutoConcurrent(c *gc.C) {
   904  	s.testFilesystemAttachmentParamsConcurrent(c, "", "", "")
   905  }
   906  
   907  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsLocationAutoAndManualConcurrent(c *gc.C) {
   908  	s.testFilesystemAttachmentParamsConcurrent(c, "", "/srv", "")
   909  }
   910  
   911  func (s *FilesystemStateSuite) testFilesystemAttachmentParamsConcurrent(c *gc.C, locBefore, locAfter, expectErr string) {
   912  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   913  	c.Assert(err, jc.ErrorIsNil)
   914  
   915  	storage := map[string]state.StorageConstraints{
   916  		"data": makeStorageCons("rootfs", 1024, 1),
   917  	}
   918  
   919  	deploy := func(rev int, location, serviceName string) error {
   920  		ch := s.createStorageCharmRev(c, "storage-filesystem", charm.Storage{
   921  			Name:     "data",
   922  			Type:     charm.StorageFilesystem,
   923  			CountMin: 1,
   924  			CountMax: 1,
   925  			Location: location,
   926  		}, rev)
   927  		service := s.AddTestingServiceWithStorage(c, serviceName, ch, storage)
   928  		unit, err := service.AddUnit()
   929  		c.Assert(err, jc.ErrorIsNil)
   930  		return unit.AssignToMachine(machine)
   931  	}
   932  
   933  	defer state.SetBeforeHooks(c, s.State, func() {
   934  		err := deploy(1, locBefore, "storage-filesystem-before")
   935  		c.Assert(err, jc.ErrorIsNil)
   936  	}).Check()
   937  
   938  	err = deploy(2, locAfter, "storage-filesystem-after")
   939  	if expectErr != "" {
   940  		c.Assert(err, gc.ErrorMatches, expectErr)
   941  	} else {
   942  		c.Assert(err, jc.ErrorIsNil)
   943  	}
   944  }
   945  
   946  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsConcurrentRemove(c *gc.C) {
   947  	// this creates a filesystem mounted at "/srv".
   948  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   949  
   950  	ch := s.createStorageCharm(c, "storage-filesystem", charm.Storage{
   951  		Name:     "data",
   952  		Type:     charm.StorageFilesystem,
   953  		CountMin: 1,
   954  		CountMax: 1,
   955  		Location: "/not/in/srv",
   956  	})
   957  	service := s.AddTestingService(c, "storage-filesystem", ch)
   958  	unit, err := service.AddUnit()
   959  	c.Assert(err, jc.ErrorIsNil)
   960  
   961  	defer state.SetBeforeHooks(c, s.State, func() {
   962  		err := s.State.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   963  		c.Assert(err, jc.ErrorIsNil)
   964  		err = s.State.RemoveFilesystemAttachment(
   965  			machine.MachineTag(), filesystem.FilesystemTag(),
   966  		)
   967  		c.Assert(err, jc.ErrorIsNil)
   968  	}).Check()
   969  
   970  	err = unit.AssignToMachine(machine)
   971  	c.Assert(err, jc.ErrorIsNil)
   972  }
   973  
   974  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsLocationStorageDir(c *gc.C) {
   975  	ch := s.createStorageCharm(c, "storage-filesystem", charm.Storage{
   976  		Name:     "data",
   977  		Type:     charm.StorageFilesystem,
   978  		CountMin: 1,
   979  		CountMax: 1,
   980  		Location: "/var/lib/juju/storage",
   981  	})
   982  	service := s.AddTestingService(c, "storage-filesystem", ch)
   983  	unit, err := service.AddUnit()
   984  	c.Assert(err, jc.ErrorIsNil)
   985  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
   986  	c.Assert(err, gc.ErrorMatches, `cannot assign unit \"storage-filesystem/0\" to machine: `+
   987  		`cannot assign unit "storage-filesystem/0" to clean, empty machine: `+
   988  		`getting filesystem mount point for storage data: `+
   989  		`invalid location "/var/lib/juju/storage": `+
   990  		`must not fall within "/var/lib/juju/storage"`)
   991  }
   992  
   993  func (s *FilesystemStateSuite) TestFilesystemAttachmentLocationConflict(c *gc.C) {
   994  	// this creates a filesystem mounted at "/srv".
   995  	_, machine := s.setupFilesystemAttachment(c, "rootfs")
   996  
   997  	ch := s.createStorageCharm(c, "storage-filesystem", charm.Storage{
   998  		Name:     "data",
   999  		Type:     charm.StorageFilesystem,
  1000  		CountMin: 1,
  1001  		CountMax: 1,
  1002  		Location: "/srv/within",
  1003  	})
  1004  	svc := s.AddTestingService(c, "storage-filesystem", ch)
  1005  
  1006  	u, err := svc.AddUnit()
  1007  	c.Assert(err, jc.ErrorIsNil)
  1008  	err = u.AssignToMachine(machine)
  1009  	c.Assert(err, gc.ErrorMatches,
  1010  		`cannot assign unit "storage-filesystem/0" to machine 0: `+
  1011  			`validating filesystem mount points: `+
  1012  			`mount point "/srv" for filesystem 0/0 contains `+
  1013  			`mount point "/srv/within" for "data" storage`)
  1014  }
  1015  
  1016  func (s *FilesystemStateSuite) setupFilesystemAttachment(c *gc.C, pool string) (state.Filesystem, *state.Machine) {
  1017  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
  1018  		Series: "quantal",
  1019  		Jobs:   []state.MachineJob{state.JobHostUnits},
  1020  		Filesystems: []state.MachineFilesystemParams{{
  1021  			Filesystem: state.FilesystemParams{Pool: pool, Size: 1024},
  1022  			Attachment: state.FilesystemAttachmentParams{
  1023  				Location: "/srv",
  1024  			},
  1025  		}},
  1026  	})
  1027  	c.Assert(err, jc.ErrorIsNil)
  1028  	attachments, err := s.State.MachineFilesystemAttachments(machine.MachineTag())
  1029  	c.Assert(err, jc.ErrorIsNil)
  1030  	c.Assert(attachments, gc.HasLen, 1)
  1031  	c.Assert(err, jc.ErrorIsNil)
  1032  	assertMachineStorageRefs(c, s.State, machine.MachineTag())
  1033  	return s.filesystem(c, attachments[0].Filesystem()), machine
  1034  }