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