github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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/charm/v12"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/names/v5"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/caas/kubernetes/provider"
    14  	k8stesting "github.com/juju/juju/caas/kubernetes/provider/testing"
    15  	"github.com/juju/juju/core/status"
    16  	"github.com/juju/juju/state"
    17  	"github.com/juju/juju/state/testing"
    18  )
    19  
    20  type FilesystemStateSuite struct {
    21  	StorageStateSuiteBase
    22  }
    23  
    24  type FilesystemIAASModelSuite struct {
    25  	FilesystemStateSuite
    26  }
    27  
    28  type FilesystemCAASModelSuite struct {
    29  	FilesystemStateSuite
    30  }
    31  
    32  var _ = gc.Suite(&FilesystemIAASModelSuite{})
    33  var _ = gc.Suite(&FilesystemCAASModelSuite{})
    34  
    35  func (s *FilesystemCAASModelSuite) SetUpTest(c *gc.C) {
    36  	s.series = "kubernetes"
    37  	s.FilesystemStateSuite.SetUpTest(c)
    38  	s.PatchValue(&provider.NewK8sClients, k8stesting.NoopFakeK8sClients)
    39  }
    40  
    41  func (s *FilesystemStateSuite) TestAddApplicationInvalidPool(c *gc.C) {
    42  	ch := s.AddTestingCharm(c, "storage-filesystem")
    43  	storage := map[string]state.StorageConstraints{
    44  		"data": makeStorageCons("invalid-pool", 1024, 1),
    45  	}
    46  	_, err := s.st.AddApplication(state.AddApplicationArgs{
    47  		Name: "storage-filesystem", Charm: ch,
    48  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
    49  			OS:      "ubuntu",
    50  			Channel: "20.04/stable",
    51  		}},
    52  		Storage: storage,
    53  	})
    54  	c.Assert(err, gc.ErrorMatches, `.* pool "invalid-pool" not found`)
    55  }
    56  
    57  func (s *FilesystemStateSuite) TestAddApplicationNoPoolNoDefault(c *gc.C) {
    58  	// no pool specified, no default configured: use default.
    59  	expected := "rootfs"
    60  	if s.series == "kubernetes" {
    61  		expected = "kubernetes"
    62  	}
    63  	s.testAddApplicationDefaultPool(c, expected, 0)
    64  }
    65  
    66  func (s *FilesystemStateSuite) TestAddApplicationNoPoolNoDefaultWithUnits(c *gc.C) {
    67  	// no pool specified, no default configured: use default, add a unit during
    68  	// app deploy.
    69  	expected := "rootfs"
    70  	if s.series == "kubernetes" {
    71  		expected = "kubernetes"
    72  	}
    73  	s.testAddApplicationDefaultPool(c, expected, 1)
    74  }
    75  
    76  func (s *FilesystemIAASModelSuite) TestAddApplicationNoPoolDefaultFilesystem(c *gc.C) {
    77  	// no pool specified, default filesystem configured: use default
    78  	// filesystem.
    79  	m, err := s.st.Model()
    80  	c.Assert(err, jc.ErrorIsNil)
    81  	err = m.UpdateModelConfig(map[string]interface{}{
    82  		"storage-default-filesystem-source": "machinescoped",
    83  	}, nil)
    84  	c.Assert(err, jc.ErrorIsNil)
    85  	s.testAddApplicationDefaultPool(c, "machinescoped", 0)
    86  }
    87  
    88  func (s *FilesystemIAASModelSuite) TestAddApplicationNoPoolDefaultBlock(c *gc.C) {
    89  	// no pool specified, default block configured: use default
    90  	// block with managed fs on top.
    91  	m, err := s.st.Model()
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	err = m.UpdateModelConfig(map[string]interface{}{
    94  		"storage-default-block-source": "modelscoped-block",
    95  	}, nil)
    96  	c.Assert(err, jc.ErrorIsNil)
    97  	s.testAddApplicationDefaultPool(c, "modelscoped-block", 0)
    98  }
    99  
   100  func (s *FilesystemStateSuite) testAddApplicationDefaultPool(c *gc.C, expectedPool string, numUnits int) {
   101  	ch := s.AddTestingCharm(c, "storage-filesystem")
   102  	storage := map[string]state.StorageConstraints{
   103  		"data": makeStorageCons("", 1024, 1),
   104  	}
   105  
   106  	args := state.AddApplicationArgs{
   107  		Name:  "storage-filesystem",
   108  		Charm: ch,
   109  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   110  			OS:      "ubuntu",
   111  			Channel: "20.04/stable",
   112  		}},
   113  		Storage:  storage,
   114  		NumUnits: numUnits,
   115  	}
   116  	app, err := s.st.AddApplication(args)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	cons, err := app.StorageConstraints()
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	expected := map[string]state.StorageConstraints{
   121  		"data": {
   122  			Pool:  expectedPool,
   123  			Size:  1024,
   124  			Count: 1,
   125  		},
   126  	}
   127  	if s.series == "kubernetes" {
   128  		expected["cache"] = state.StorageConstraints{Count: 0, Size: 1024, Pool: expectedPool}
   129  	}
   130  	c.Assert(cons, jc.DeepEquals, expected)
   131  
   132  	app, err = s.st.Application(args.Name)
   133  	c.Assert(err, jc.ErrorIsNil)
   134  
   135  	units, err := app.AllUnits()
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	c.Assert(units, gc.HasLen, numUnits)
   138  
   139  	for _, unit := range units {
   140  		scons, err := unit.StorageConstraints()
   141  		c.Assert(err, jc.ErrorIsNil)
   142  		c.Assert(scons, gc.DeepEquals, expected)
   143  
   144  		storageAttachments, err := s.storageBackend.UnitStorageAttachments(unit.UnitTag())
   145  		c.Assert(err, jc.ErrorIsNil)
   146  		c.Assert(storageAttachments, gc.HasLen, 1)
   147  		storageInstance, err := s.storageBackend.StorageInstance(storageAttachments[0].StorageInstance())
   148  		c.Assert(err, jc.ErrorIsNil)
   149  		c.Assert(storageInstance.Kind(), gc.Equals, state.StorageKindFilesystem)
   150  	}
   151  }
   152  
   153  func (s *FilesystemStateSuite) TestAddFilesystemWithoutBackingVolume(c *gc.C) {
   154  	s.addUnitWithFilesystem(c, "rootfs", false)
   155  }
   156  
   157  func (s *FilesystemIAASModelSuite) TestAddFilesystemWithBackingVolume(c *gc.C) {
   158  	s.addUnitWithFilesystem(c, "modelscoped-block", true)
   159  }
   160  
   161  func (s *FilesystemStateSuite) TestSetFilesystemInfoImmutable(c *gc.C) {
   162  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "rootfs")
   163  	hostTag := s.maybeAssignUnit(c, u)
   164  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   165  	filesystemTag := filesystem.FilesystemTag()
   166  
   167  	if _, ok := hostTag.(names.MachineTag); ok {
   168  		machine := unitMachine(c, s.st, u)
   169  		err := machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   170  		c.Assert(err, jc.ErrorIsNil)
   171  	}
   172  
   173  	filesystemInfoSet := state.FilesystemInfo{Size: 123, FilesystemId: "fs-id"}
   174  	err := s.storageBackend.SetFilesystemInfo(filesystem.FilesystemTag(), filesystemInfoSet)
   175  	c.Assert(err, jc.ErrorIsNil)
   176  
   177  	// The first call to SetFilesystemInfo takes the pool name from
   178  	// the params; the second does not, but it must not change
   179  	// either. Callers are expected to get the existing info and
   180  	// update it, leaving immutable values intact.
   181  	err = s.storageBackend.SetFilesystemInfo(filesystem.FilesystemTag(), filesystemInfoSet)
   182  	c.Assert(err, gc.ErrorMatches, `cannot set info for filesystem ".*0/0": cannot change pool from "rootfs" to ""`)
   183  
   184  	filesystemInfoSet.Pool = "rootfs"
   185  	s.assertFilesystemInfo(c, filesystemTag, filesystemInfoSet)
   186  }
   187  
   188  func (s *FilesystemStateSuite) maybeAssignUnit(c *gc.C, u *state.Unit) names.Tag {
   189  	m, err := s.st.Model()
   190  	c.Assert(err, jc.ErrorIsNil)
   191  	if m.Type() == state.ModelTypeCAAS {
   192  		return u.UnitTag()
   193  	}
   194  	err = s.st.AssignUnit(u, state.AssignCleanEmpty)
   195  	c.Assert(err, jc.ErrorIsNil)
   196  	machineId, err := u.AssignedMachineId()
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	return names.NewMachineTag(machineId)
   199  }
   200  
   201  func (s *FilesystemStateSuite) TestSetFilesystemInfoNoFilesystemId(c *gc.C) {
   202  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "tmpfs-pool")
   203  	s.maybeAssignUnit(c, u)
   204  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   205  	filesystemTag := filesystem.FilesystemTag()
   206  	s.assertFilesystemUnprovisioned(c, filesystemTag)
   207  
   208  	filesystemInfoSet := state.FilesystemInfo{Size: 123}
   209  	err := s.storageBackend.SetFilesystemInfo(filesystem.FilesystemTag(), filesystemInfoSet)
   210  	c.Assert(err, gc.ErrorMatches, `cannot set info for filesystem ".*0/0": filesystem ID not set`)
   211  }
   212  
   213  func (s *FilesystemIAASModelSuite) TestVolumeFilesystem(c *gc.C) {
   214  	filesystem, _, _ := s.addUnitWithFilesystem(c, "modelscoped-block", true)
   215  	volumeTag, err := filesystem.Volume()
   216  	c.Assert(err, jc.ErrorIsNil)
   217  
   218  	volumeFilesystem := s.volumeFilesystem(c, volumeTag)
   219  	c.Assert(volumeFilesystem.FilesystemTag(), gc.Equals, filesystem.FilesystemTag())
   220  }
   221  
   222  func (s *FilesystemStateSuite) addUnitWithFilesystem(c *gc.C, pool string, withVolume bool) (
   223  	state.Filesystem,
   224  	state.FilesystemAttachment,
   225  	state.StorageAttachment,
   226  ) {
   227  	filesystem, filesystemAttachment, storageAttachment := s.addUnitWithFilesystemUnprovisioned(
   228  		c, pool, withVolume,
   229  	)
   230  
   231  	if machineTag, ok := filesystemAttachment.Host().(names.MachineTag); ok {
   232  		// Machine must be provisioned before either volume or
   233  		// filesystem can be attached.
   234  		machine, err := s.st.Machine(machineTag.Id())
   235  		c.Assert(err, jc.ErrorIsNil)
   236  		err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   237  		c.Assert(err, jc.ErrorIsNil)
   238  	}
   239  
   240  	if withVolume {
   241  		// Volume must be provisioned before the filesystem.
   242  		volume := s.filesystemVolume(c, filesystem.FilesystemTag())
   243  		err := s.storageBackend.SetVolumeInfo(volume.VolumeTag(), state.VolumeInfo{VolumeId: "vol-123"})
   244  		c.Assert(err, jc.ErrorIsNil)
   245  
   246  		// Volume must be attached before the filesystem.
   247  		err = s.storageBackend.SetVolumeAttachmentInfo(
   248  			filesystemAttachment.Host(),
   249  			volume.VolumeTag(),
   250  			state.VolumeAttachmentInfo{DeviceName: "sdc"},
   251  		)
   252  		c.Assert(err, jc.ErrorIsNil)
   253  	}
   254  
   255  	// Filesystem must be provisioned before it can be attached.
   256  	err := s.storageBackend.SetFilesystemInfo(
   257  		filesystem.FilesystemTag(),
   258  		state.FilesystemInfo{FilesystemId: "fs-123"},
   259  	)
   260  	c.Assert(err, jc.ErrorIsNil)
   261  
   262  	err = s.storageBackend.SetFilesystemAttachmentInfo(
   263  		filesystemAttachment.Host(),
   264  		filesystem.FilesystemTag(),
   265  		state.FilesystemAttachmentInfo{MountPoint: "/srv"},
   266  	)
   267  	c.Assert(err, jc.ErrorIsNil)
   268  
   269  	return filesystem, filesystemAttachment, storageAttachment
   270  }
   271  
   272  func (s *FilesystemStateSuite) addUnitWithFilesystemUnprovisioned(c *gc.C, pool string, withVolume bool) (
   273  	state.Filesystem,
   274  	state.FilesystemAttachment,
   275  	state.StorageAttachment,
   276  ) {
   277  	ch := s.AddTestingCharm(c, "storage-filesystem")
   278  	storage := map[string]state.StorageConstraints{
   279  		"data": makeStorageCons(pool, 1024, 1),
   280  	}
   281  	app := s.AddTestingApplicationWithStorage(c, "storage-filesystem", ch, storage)
   282  	unit, err := app.AddUnit(state.AddUnitParams{})
   283  	c.Assert(err, jc.ErrorIsNil)
   284  	hostTag := s.maybeAssignUnit(c, unit)
   285  
   286  	storageAttachments, err := s.storageBackend.UnitStorageAttachments(unit.UnitTag())
   287  	c.Assert(err, jc.ErrorIsNil)
   288  	c.Assert(storageAttachments, gc.HasLen, 1)
   289  	storageInstance, err := s.storageBackend.StorageInstance(storageAttachments[0].StorageInstance())
   290  	c.Assert(err, jc.ErrorIsNil)
   291  	c.Assert(storageInstance.Kind(), gc.Equals, state.StorageKindFilesystem)
   292  
   293  	filesystem := s.storageInstanceFilesystem(c, storageInstance.StorageTag())
   294  	filesystemStorageTag, err := filesystem.Storage()
   295  	c.Assert(err, jc.ErrorIsNil)
   296  	c.Assert(filesystemStorageTag, gc.Equals, storageInstance.StorageTag())
   297  	_, err = filesystem.Info()
   298  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
   299  	_, ok := filesystem.Params()
   300  	c.Assert(ok, jc.IsTrue)
   301  
   302  	volume, err := s.storageBackend.StorageInstanceVolume(storageInstance.StorageTag())
   303  	if withVolume {
   304  		c.Assert(err, jc.ErrorIsNil)
   305  		c.Assert(volume.VolumeTag(), gc.Equals, names.NewVolumeTag("0"))
   306  		volumeStorageTag, err := volume.StorageInstance()
   307  		c.Assert(err, jc.ErrorIsNil)
   308  		c.Assert(volumeStorageTag, gc.Equals, storageInstance.StorageTag())
   309  		filesystemVolume, err := filesystem.Volume()
   310  		c.Assert(err, jc.ErrorIsNil)
   311  		c.Assert(filesystemVolume, gc.Equals, volume.VolumeTag())
   312  		_, err = s.storageBackend.VolumeAttachment(hostTag, filesystemVolume)
   313  		c.Assert(err, jc.ErrorIsNil)
   314  	} else {
   315  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
   316  		_, err = filesystem.Volume()
   317  		c.Assert(errors.Cause(err), gc.Equals, state.ErrNoBackingVolume)
   318  	}
   319  
   320  	if s.series != "kubernetes" {
   321  		machineTag := hostTag.(names.MachineTag)
   322  		filesystemAttachments, err := s.storageBackend.MachineFilesystemAttachments(machineTag)
   323  		c.Assert(err, jc.ErrorIsNil)
   324  		c.Assert(filesystemAttachments, gc.HasLen, 1)
   325  		c.Assert(filesystemAttachments[0].Filesystem(), gc.Equals, filesystem.FilesystemTag())
   326  		c.Assert(filesystemAttachments[0].Host(), gc.Equals, hostTag)
   327  		_, err = filesystemAttachments[0].Info()
   328  		c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
   329  		_, ok = filesystemAttachments[0].Params()
   330  		c.Assert(ok, jc.IsTrue)
   331  
   332  		assertMachineStorageRefs(c, s.storageBackend, machineTag)
   333  	}
   334  
   335  	att, err := s.storageBackend.FilesystemAttachment(hostTag, filesystem.FilesystemTag())
   336  	c.Assert(err, jc.ErrorIsNil)
   337  	return filesystem, att, storageAttachments[0]
   338  }
   339  
   340  func (s *FilesystemIAASModelSuite) TestWatchFilesystemAttachment(c *gc.C) {
   341  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "rootfs")
   342  	err := s.st.AssignUnit(u, state.AssignCleanEmpty)
   343  	c.Assert(err, jc.ErrorIsNil)
   344  	assignedMachineId, err := u.AssignedMachineId()
   345  	c.Assert(err, jc.ErrorIsNil)
   346  	machineTag := names.NewMachineTag(assignedMachineId)
   347  
   348  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   349  	filesystemTag := filesystem.FilesystemTag()
   350  	// Ensure that all the creation events have flowed through the system.
   351  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   352  
   353  	w := s.storageBackend.WatchFilesystemAttachment(machineTag, filesystemTag)
   354  	defer testing.AssertStop(c, w)
   355  	wc := testing.NewNotifyWatcherC(c, w)
   356  	wc.AssertOneChange()
   357  
   358  	machine, err := s.st.Machine(assignedMachineId)
   359  	c.Assert(err, jc.ErrorIsNil)
   360  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   361  	c.Assert(err, jc.ErrorIsNil)
   362  
   363  	// filesystem attachment will NOT react to filesystem changes
   364  	err = s.storageBackend.SetFilesystemInfo(filesystemTag, state.FilesystemInfo{
   365  		FilesystemId: "fs-123",
   366  	})
   367  	c.Assert(err, jc.ErrorIsNil)
   368  	wc.AssertNoChange()
   369  
   370  	err = s.storageBackend.SetFilesystemAttachmentInfo(
   371  		machineTag, filesystemTag, state.FilesystemAttachmentInfo{
   372  			MountPoint: "/srv",
   373  		},
   374  	)
   375  	c.Assert(err, jc.ErrorIsNil)
   376  	wc.AssertOneChange()
   377  }
   378  
   379  func (s *FilesystemStateSuite) TestFilesystemInfo(c *gc.C) {
   380  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "rootfs")
   381  	hostTag := s.maybeAssignUnit(c, u)
   382  
   383  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   384  	filesystemTag := filesystem.FilesystemTag()
   385  
   386  	s.assertFilesystemUnprovisioned(c, filesystemTag)
   387  	s.assertFilesystemAttachmentUnprovisioned(c, hostTag, filesystemTag)
   388  
   389  	if _, ok := hostTag.(names.MachineTag); ok {
   390  		machine, err := s.st.Machine(hostTag.Id())
   391  		c.Assert(err, jc.ErrorIsNil)
   392  		err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   393  		c.Assert(err, jc.ErrorIsNil)
   394  	}
   395  
   396  	filesystemInfo := state.FilesystemInfo{FilesystemId: "fs-123", Size: 456}
   397  	err := s.storageBackend.SetFilesystemInfo(filesystemTag, filesystemInfo)
   398  	c.Assert(err, jc.ErrorIsNil)
   399  	filesystemInfo.Pool = "rootfs" // taken from params
   400  	s.assertFilesystemInfo(c, filesystemTag, filesystemInfo)
   401  	s.assertFilesystemAttachmentUnprovisioned(c, hostTag, filesystemTag)
   402  
   403  	filesystemAttachmentInfo := state.FilesystemAttachmentInfo{MountPoint: "/srv"}
   404  	err = s.storageBackend.SetFilesystemAttachmentInfo(hostTag, filesystemTag, filesystemAttachmentInfo)
   405  	c.Assert(err, jc.ErrorIsNil)
   406  	s.assertFilesystemAttachmentInfo(c, hostTag, filesystemTag, filesystemAttachmentInfo)
   407  }
   408  
   409  func (s *FilesystemIAASModelSuite) TestVolumeBackedFilesystemScope(c *gc.C) {
   410  	_, unit, storageTag := s.setupSingleStorage(c, "filesystem", "modelscoped-block")
   411  	err := s.st.AssignUnit(unit, state.AssignCleanEmpty)
   412  	c.Assert(err, jc.ErrorIsNil)
   413  
   414  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   415  	c.Assert(filesystem.Tag(), gc.Equals, names.NewFilesystemTag("0"))
   416  	volumeTag, err := filesystem.Volume()
   417  	c.Assert(err, jc.ErrorIsNil)
   418  	c.Assert(volumeTag, gc.Equals, names.NewVolumeTag("0"))
   419  }
   420  
   421  func (s *FilesystemIAASModelSuite) TestWatchModelFilesystems(c *gc.C) {
   422  	app := s.setupMixedScopeStorageApplication(c, "filesystem")
   423  	addUnit := func() *state.Unit {
   424  		u, err := app.AddUnit(state.AddUnitParams{})
   425  		c.Assert(err, jc.ErrorIsNil)
   426  		err = s.st.AssignUnit(u, state.AssignCleanEmpty)
   427  		c.Assert(err, jc.ErrorIsNil)
   428  		return u
   429  	}
   430  	u := addUnit()
   431  	// Ensure that all the creation events have flowed through the system.
   432  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   433  
   434  	w := s.storageBackend.WatchModelFilesystems()
   435  	defer testing.AssertStop(c, w)
   436  	wc := testing.NewStringsWatcherC(c, w)
   437  	wc.AssertChange("0", "1") // initial
   438  	wc.AssertNoChange()
   439  
   440  	addUnit()
   441  	wc.AssertChange("4", "5")
   442  	wc.AssertNoChange()
   443  
   444  	err := u.Destroy()
   445  	c.Assert(err, jc.ErrorIsNil)
   446  	filesystemTag := names.NewFilesystemTag("0")
   447  	removeFilesystemStorageInstance(c, s.storageBackend, filesystemTag)
   448  
   449  	err = s.storageBackend.DestroyFilesystem(filesystemTag, false)
   450  	c.Assert(err, jc.ErrorIsNil)
   451  	wc.AssertChange("0")
   452  	wc.AssertNoChange()
   453  
   454  	machineTag := names.NewMachineTag("0")
   455  	err = s.storageBackend.DetachFilesystem(machineTag, filesystemTag)
   456  	c.Assert(err, jc.ErrorIsNil)
   457  	wc.AssertNoChange()
   458  
   459  	err = s.storageBackend.RemoveFilesystemAttachment(machineTag, filesystemTag, false)
   460  	c.Assert(err, jc.ErrorIsNil)
   461  	wc.AssertChange("0") // last attachment removed
   462  	wc.AssertNoChange()
   463  }
   464  
   465  func (s *FilesystemIAASModelSuite) TestWatchModelFilesystemAttachments(c *gc.C) {
   466  	app := s.setupMixedScopeStorageApplication(c, "filesystem")
   467  	addUnit := func() *state.Unit {
   468  		u, err := app.AddUnit(state.AddUnitParams{})
   469  		c.Assert(err, jc.ErrorIsNil)
   470  		err = s.st.AssignUnit(u, state.AssignCleanEmpty)
   471  		c.Assert(err, jc.ErrorIsNil)
   472  		return u
   473  	}
   474  	u := addUnit()
   475  	// Ensure that all the creation events have flowed through the system.
   476  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   477  
   478  	w := s.storageBackend.WatchModelFilesystemAttachments()
   479  	defer testing.AssertStop(c, w)
   480  	wc := testing.NewStringsWatcherC(c, w)
   481  	wc.AssertChange("0:0", "0:1") // initial
   482  	wc.AssertNoChange()
   483  
   484  	addUnit()
   485  	wc.AssertChange("1:4", "1:5")
   486  	wc.AssertNoChange()
   487  
   488  	err := u.Destroy()
   489  	c.Assert(err, jc.ErrorIsNil)
   490  	filesystemTag := names.NewFilesystemTag("0")
   491  	removeFilesystemStorageInstance(c, s.storageBackend, filesystemTag)
   492  
   493  	err = s.storageBackend.DestroyFilesystem(filesystemTag, false)
   494  	c.Assert(err, jc.ErrorIsNil)
   495  	wc.AssertNoChange()
   496  
   497  	machineTag := names.NewMachineTag("0")
   498  	err = s.storageBackend.DetachFilesystem(machineTag, filesystemTag)
   499  	c.Assert(err, jc.ErrorIsNil)
   500  	wc.AssertChange("0:0")
   501  	wc.AssertNoChange()
   502  
   503  	err = s.storageBackend.RemoveFilesystemAttachment(machineTag, filesystemTag, false)
   504  	c.Assert(err, jc.ErrorIsNil)
   505  	wc.AssertChange("0:0")
   506  	wc.AssertNoChange()
   507  }
   508  
   509  func (s *FilesystemIAASModelSuite) TestWatchMachineFilesystems(c *gc.C) {
   510  	app := s.setupMixedScopeStorageApplication(c, "filesystem")
   511  	addUnit := func() *state.Unit {
   512  		u, err := app.AddUnit(state.AddUnitParams{})
   513  		c.Assert(err, jc.ErrorIsNil)
   514  		err = s.st.AssignUnit(u, state.AssignCleanEmpty)
   515  		c.Assert(err, jc.ErrorIsNil)
   516  		return u
   517  	}
   518  	u := addUnit()
   519  	// Ensure that all the creation events have flowed through the system.
   520  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   521  
   522  	w := s.storageBackend.WatchMachineFilesystems(names.NewMachineTag("0"))
   523  	defer testing.AssertStop(c, w)
   524  	wc := testing.NewStringsWatcherC(c, w)
   525  	wc.AssertChange("0/2", "0/3") // initial
   526  	wc.AssertNoChange()
   527  
   528  	addUnit()
   529  	// no change, since we're only interested in the one machine.
   530  	wc.AssertNoChange()
   531  
   532  	err := u.Destroy()
   533  	c.Assert(err, jc.ErrorIsNil)
   534  	filesystemTag := names.NewFilesystemTag("0/2")
   535  	removeFilesystemStorageInstance(c, s.storageBackend, filesystemTag)
   536  
   537  	err = s.storageBackend.DestroyFilesystem(filesystemTag, false)
   538  	c.Assert(err, jc.ErrorIsNil)
   539  	wc.AssertChange("0/2")
   540  	wc.AssertNoChange()
   541  
   542  	attachments, err := s.storageBackend.FilesystemAttachments(filesystemTag)
   543  	c.Assert(err, jc.ErrorIsNil)
   544  	for _, a := range attachments {
   545  		err := s.storageBackend.DetachFilesystem(a.Host(), filesystemTag)
   546  		c.Assert(err, jc.ErrorIsNil)
   547  		err = s.storageBackend.RemoveFilesystemAttachment(a.Host(), filesystemTag, false)
   548  		c.Assert(err, jc.ErrorIsNil)
   549  	}
   550  	wc.AssertChange("0/2") // Dying -> Dead
   551  	wc.AssertNoChange()
   552  
   553  	err = s.storageBackend.RemoveFilesystem(filesystemTag)
   554  	c.Assert(err, jc.ErrorIsNil)
   555  	// no more changes after seeing Dead
   556  	wc.AssertNoChange()
   557  }
   558  
   559  func (s *FilesystemIAASModelSuite) TestWatchMachineFilesystemAttachments(c *gc.C) {
   560  	app := s.setupMixedScopeStorageApplication(c, "filesystem", "machinescoped", "modelscoped")
   561  	addUnit := func(to *state.Machine) (u *state.Unit, m *state.Machine) {
   562  		var err error
   563  		u, err = app.AddUnit(state.AddUnitParams{})
   564  		c.Assert(err, jc.ErrorIsNil)
   565  		if to != nil {
   566  			err = u.AssignToMachine(to)
   567  			c.Assert(err, jc.ErrorIsNil)
   568  			return u, to
   569  		}
   570  		err = s.st.AssignUnit(u, state.AssignCleanEmpty)
   571  		c.Assert(err, jc.ErrorIsNil)
   572  		m = unitMachine(c, s.st, u)
   573  		return u, m
   574  	}
   575  	_, m0 := addUnit(nil)
   576  	// Ensure that all the creation events have flowed through the system.
   577  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   578  
   579  	w := s.storageBackend.WatchMachineFilesystemAttachments(names.NewMachineTag("0"))
   580  	defer testing.AssertStop(c, w)
   581  	wc := testing.NewStringsWatcherC(c, w)
   582  	wc.AssertChange("0:0/0", "0:0/1") // initial
   583  	wc.AssertNoChange()
   584  
   585  	addUnit(nil)
   586  	// no change, since we're only interested in the one machine.
   587  	wc.AssertNoChange()
   588  
   589  	err := s.storageBackend.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("2"))
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	// no change, since we're only interested in attachments of
   592  	// machine-scoped volumes.
   593  	wc.AssertNoChange()
   594  
   595  	removeFilesystemStorageInstance(c, s.storageBackend, names.NewFilesystemTag("0/0"))
   596  	err = s.storageBackend.DestroyFilesystem(names.NewFilesystemTag("0/0"), false)
   597  	c.Assert(err, jc.ErrorIsNil)
   598  	wc.AssertChange("0:0/0") // dying
   599  	wc.AssertNoChange()
   600  
   601  	err = s.storageBackend.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("0/0"), false)
   602  	c.Assert(err, jc.ErrorIsNil)
   603  	wc.AssertChange("0:0/0") // removed
   604  	wc.AssertNoChange()
   605  
   606  	addUnit(m0)
   607  	wc.AssertChange("0:0/8", "0:0/9")
   608  	wc.AssertNoChange()
   609  }
   610  
   611  func (s *FilesystemCAASModelSuite) TestWatchUnitFilesystems(c *gc.C) {
   612  	ch := s.AddTestingCharm(c, "storage-filesystem")
   613  	storage := map[string]state.StorageConstraints{
   614  		"data":  {Count: 1, Size: 1024, Pool: "kubernetes"},
   615  		"cache": {Count: 1, Size: 1024, Pool: "rootfs"},
   616  	}
   617  	app, err := s.st.AddApplication(state.AddApplicationArgs{
   618  		Name: "mariadb", Charm: ch,
   619  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   620  			OS:      "ubuntu",
   621  			Channel: "20.04/stable",
   622  		}},
   623  		Storage: storage,
   624  	})
   625  	c.Assert(err, jc.ErrorIsNil)
   626  
   627  	addUnit := func(app *state.Application) *state.Unit {
   628  		var err error
   629  		u, err := app.AddUnit(state.AddUnitParams{})
   630  		c.Assert(err, jc.ErrorIsNil)
   631  		return u
   632  	}
   633  	u := addUnit(app)
   634  	// Ensure that all the creation events have flowed through the system.
   635  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   636  
   637  	w := s.storageBackend.WatchUnitFilesystems(app.ApplicationTag())
   638  	defer testing.AssertStop(c, w)
   639  	wc := testing.NewStringsWatcherC(c, w)
   640  	wc.AssertChange("mariadb/0/0") // initial
   641  	wc.AssertNoChange()
   642  
   643  	app2, err := s.st.AddApplication(state.AddApplicationArgs{
   644  		Name: "another", Charm: ch,
   645  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   646  			OS:      "ubuntu",
   647  			Channel: "20.04/stable",
   648  		}},
   649  		Storage: storage,
   650  	})
   651  	c.Assert(err, jc.ErrorIsNil)
   652  	addUnit(app2)
   653  	// no change, since we're only interested in the one application.
   654  	wc.AssertNoChange()
   655  
   656  	err = u.Destroy()
   657  	c.Assert(err, jc.ErrorIsNil)
   658  	filesystemTag := names.NewFilesystemTag("mariadb/0/0")
   659  	removeFilesystemStorageInstance(c, s.storageBackend, filesystemTag)
   660  
   661  	err = s.storageBackend.DestroyFilesystem(filesystemTag, false)
   662  	c.Assert(err, jc.ErrorIsNil)
   663  	wc.AssertChange("mariadb/0/0")
   664  	wc.AssertNoChange()
   665  
   666  	attachments, err := s.storageBackend.FilesystemAttachments(filesystemTag)
   667  	c.Assert(err, jc.ErrorIsNil)
   668  	for _, a := range attachments {
   669  		err := s.storageBackend.DetachFilesystem(a.Host(), filesystemTag)
   670  		c.Assert(err, jc.ErrorIsNil)
   671  		err = s.storageBackend.RemoveFilesystemAttachment(a.Host(), filesystemTag, false)
   672  		c.Assert(err, jc.ErrorIsNil)
   673  	}
   674  	wc.AssertChange("mariadb/0/0") // Dying -> Dead
   675  	wc.AssertNoChange()
   676  
   677  	err = s.storageBackend.RemoveFilesystem(filesystemTag)
   678  	c.Assert(err, jc.ErrorIsNil)
   679  	// no more changes after seeing Dead
   680  	wc.AssertNoChange()
   681  }
   682  
   683  func (s *FilesystemCAASModelSuite) TestWatchUnitFilesystemAttachments(c *gc.C) {
   684  	ch := s.AddTestingCharm(c, "storage-filesystem")
   685  	storage := map[string]state.StorageConstraints{
   686  		"data":  {Count: 1, Size: 1024, Pool: "kubernetes"},
   687  		"cache": {Count: 1, Size: 1024, Pool: "rootfs"},
   688  	}
   689  	app, err := s.st.AddApplication(state.AddApplicationArgs{
   690  		Name: "mariadb", Charm: ch,
   691  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   692  			OS:      "ubuntu",
   693  			Channel: "20.04/stable",
   694  		}},
   695  		Storage: storage,
   696  	})
   697  	c.Assert(err, jc.ErrorIsNil)
   698  
   699  	addUnit := func(app *state.Application) *state.Unit {
   700  		var err error
   701  		u, err := app.AddUnit(state.AddUnitParams{})
   702  		c.Assert(err, jc.ErrorIsNil)
   703  		return u
   704  	}
   705  	addUnit(app)
   706  	// Ensure that all the creation events have flowed through the system.
   707  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   708  
   709  	w := s.storageBackend.WatchUnitFilesystemAttachments(app.ApplicationTag())
   710  	defer testing.AssertStop(c, w)
   711  	wc := testing.NewStringsWatcherC(c, w)
   712  
   713  	wc.AssertChange("mariadb/0:mariadb/0/0") // initial
   714  	wc.AssertNoChange()
   715  
   716  	app2, err := s.st.AddApplication(state.AddApplicationArgs{
   717  		Name: "another", Charm: ch,
   718  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   719  			OS:      "ubuntu",
   720  			Channel: "20.04/stable",
   721  		}},
   722  		Storage: storage,
   723  	})
   724  	c.Assert(err, jc.ErrorIsNil)
   725  	addUnit(app2)
   726  	// no change, since we're only interested in the one application.
   727  	wc.AssertNoChange()
   728  
   729  	err = s.storageBackend.DetachFilesystem(names.NewUnitTag("mariadb/0"), names.NewFilesystemTag("1"))
   730  	c.Assert(err, jc.ErrorIsNil)
   731  	// no change, since we're only interested in attachments of
   732  	// unit-scoped volumes.
   733  	wc.AssertNoChange()
   734  
   735  	removeFilesystemStorageInstance(c, s.storageBackend, names.NewFilesystemTag("mariadb/0/0"))
   736  	err = s.storageBackend.DestroyFilesystem(names.NewFilesystemTag("mariadb/0/0"), false)
   737  	c.Assert(err, jc.ErrorIsNil)
   738  	wc.AssertChange("mariadb/0:mariadb/0/0") // dying
   739  	wc.AssertNoChange()
   740  
   741  	err = s.storageBackend.RemoveFilesystemAttachment(names.NewUnitTag("mariadb/0"), names.NewFilesystemTag("mariadb/0/0"), false)
   742  	c.Assert(err, jc.ErrorIsNil)
   743  	wc.AssertChange("mariadb/0:mariadb/0/0") // removed
   744  	wc.AssertNoChange()
   745  }
   746  
   747  func (s *FilesystemStateSuite) TestParseFilesystemAttachmentId(c *gc.C) {
   748  	assertValid := func(id string, m names.Tag, v names.FilesystemTag) {
   749  		machineTag, filesystemTag, err := state.ParseFilesystemAttachmentId(id)
   750  		c.Assert(err, jc.ErrorIsNil)
   751  		c.Assert(machineTag, gc.Equals, m)
   752  		c.Assert(filesystemTag, gc.Equals, v)
   753  	}
   754  	assertValid("0:0", names.NewMachineTag("0"), names.NewFilesystemTag("0"))
   755  	assertValid("0:0/1", names.NewMachineTag("0"), names.NewFilesystemTag("0/1"))
   756  	assertValid("0/lxd/0:1", names.NewMachineTag("0/lxd/0"), names.NewFilesystemTag("1"))
   757  	assertValid("some-unit/0:1", names.NewUnitTag("some-unit/0"), names.NewFilesystemTag("1"))
   758  }
   759  
   760  func (s *FilesystemStateSuite) TestParseFilesystemAttachmentIdError(c *gc.C) {
   761  	assertError := func(id, expect string) {
   762  		_, _, err := state.ParseFilesystemAttachmentId(id)
   763  		c.Assert(err, gc.ErrorMatches, expect)
   764  	}
   765  	assertError("", `invalid filesystem attachment ID ""`)
   766  	assertError("0", `invalid filesystem attachment ID "0"`)
   767  	assertError("0:foo", `invalid filesystem attachment ID "0:foo"`)
   768  	assertError("bar:0", `invalid filesystem attachment ID "bar:0"`)
   769  }
   770  
   771  func (s *FilesystemIAASModelSuite) TestRemoveStorageInstanceDestroysAndUnassignsFilesystem(c *gc.C) {
   772  	filesystem, filesystemAttachment, storageAttachment := s.addUnitWithFilesystem(c, "modelscoped-block", true)
   773  	volume := s.filesystemVolume(c, filesystemAttachment.Filesystem())
   774  	storageTag := storageAttachment.StorageInstance()
   775  	unitTag := storageAttachment.Unit()
   776  
   777  	err := s.storageBackend.SetFilesystemAttachmentInfo(
   778  		filesystemAttachment.Host().(names.MachineTag),
   779  		filesystem.FilesystemTag(),
   780  		state.FilesystemAttachmentInfo{},
   781  	)
   782  	c.Assert(err, jc.ErrorIsNil)
   783  
   784  	u, err := s.st.Unit(unitTag.Id())
   785  	c.Assert(err, jc.ErrorIsNil)
   786  	err = u.Destroy()
   787  	c.Assert(err, jc.ErrorIsNil)
   788  	err = s.storageBackend.DestroyStorageInstance(storageTag, true, false, dontWait)
   789  	c.Assert(err, jc.ErrorIsNil)
   790  	err = s.storageBackend.DetachStorage(storageTag, unitTag, false, dontWait)
   791  	c.Assert(err, jc.ErrorIsNil)
   792  
   793  	// The storage instance and attachment are dying, but not yet
   794  	// removed from state. The filesystem should still be assigned.
   795  	s.storageInstanceFilesystem(c, storageTag)
   796  	s.storageInstanceVolume(c, storageTag)
   797  
   798  	err = s.storageBackend.RemoveStorageAttachment(storageTag, unitTag, false)
   799  	c.Assert(err, jc.ErrorIsNil)
   800  
   801  	// The storage instance is now gone; the filesystem should no longer
   802  	// be assigned to the storage.
   803  	_, err = s.storageBackend.StorageInstanceFilesystem(storageTag)
   804  	c.Assert(err, gc.ErrorMatches, `filesystem for storage instance "data/0" not found`)
   805  	_, err = s.storageBackend.StorageInstanceVolume(storageTag)
   806  	c.Assert(err, gc.ErrorMatches, `volume for storage instance "data/0" not found`)
   807  
   808  	// The filesystem and volume should still exist. The filesystem
   809  	// should be dying; the volume will be destroyed only once the
   810  	// filesystem is removed.
   811  	f := s.filesystem(c, filesystem.FilesystemTag())
   812  	c.Assert(f.Life(), gc.Equals, state.Dying)
   813  	v := s.volume(c, volume.VolumeTag())
   814  	c.Assert(v.Life(), gc.Equals, state.Alive)
   815  }
   816  
   817  func (s *FilesystemIAASModelSuite) TestReleaseStorageInstanceFilesystemReleasing(c *gc.C) {
   818  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "modelscoped")
   819  	s.maybeAssignUnit(c, u)
   820  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   821  	c.Assert(filesystem.Releasing(), jc.IsFalse)
   822  	err := s.storageBackend.SetFilesystemInfo(filesystem.FilesystemTag(), state.FilesystemInfo{FilesystemId: "vol-123"})
   823  	c.Assert(err, jc.ErrorIsNil)
   824  
   825  	err = u.Destroy()
   826  	c.Assert(err, jc.ErrorIsNil)
   827  	err = s.storageBackend.ReleaseStorageInstance(storageTag, true, false, dontWait)
   828  	c.Assert(err, jc.ErrorIsNil)
   829  	err = s.storageBackend.DetachStorage(storageTag, u.UnitTag(), false, dontWait)
   830  	c.Assert(err, jc.ErrorIsNil)
   831  
   832  	// The filesystem should should be dying, and releasing.
   833  	filesystem = s.filesystem(c, filesystem.FilesystemTag())
   834  	c.Assert(filesystem.Life(), gc.Equals, state.Dying)
   835  	c.Assert(filesystem.Releasing(), jc.IsTrue)
   836  }
   837  
   838  func (s *FilesystemIAASModelSuite) TestReleaseStorageInstanceFilesystemUnreleasable(c *gc.C) {
   839  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "modelscoped-unreleasable")
   840  	s.maybeAssignUnit(c, u)
   841  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   842  	c.Assert(filesystem.Releasing(), jc.IsFalse)
   843  	err := s.storageBackend.SetFilesystemInfo(filesystem.FilesystemTag(), state.FilesystemInfo{FilesystemId: "vol-123"})
   844  	c.Assert(err, jc.ErrorIsNil)
   845  
   846  	err = u.Destroy()
   847  	c.Assert(err, jc.ErrorIsNil)
   848  	err = s.storageBackend.ReleaseStorageInstance(storageTag, true, false, dontWait)
   849  	c.Assert(err, gc.ErrorMatches,
   850  		`cannot release storage "data/0": storage provider "modelscoped-unreleasable" does not support releasing storage`)
   851  	err = s.storageBackend.DetachStorage(storageTag, u.UnitTag(), false, dontWait)
   852  	c.Assert(err, jc.ErrorIsNil)
   853  
   854  	// The filesystem should should be dying, and releasing.
   855  	filesystem = s.filesystem(c, filesystem.FilesystemTag())
   856  	c.Assert(filesystem.Life(), gc.Equals, state.Alive)
   857  	c.Assert(filesystem.Releasing(), jc.IsFalse)
   858  }
   859  
   860  func (s *FilesystemIAASModelSuite) TestSetFilesystemAttachmentInfoFilesystemNotProvisioned(c *gc.C) {
   861  	_, filesystemAttachment, _ := s.addUnitWithFilesystemUnprovisioned(c, "rootfs", false)
   862  	err := s.storageBackend.SetFilesystemAttachmentInfo(
   863  		filesystemAttachment.Host().(names.MachineTag),
   864  		filesystemAttachment.Filesystem(),
   865  		state.FilesystemAttachmentInfo{},
   866  	)
   867  	c.Assert(err, gc.ErrorMatches, `cannot set info for filesystem attachment 0/0:0: filesystem "0/0" not provisioned`)
   868  }
   869  
   870  func (s *FilesystemIAASModelSuite) TestSetFilesystemAttachmentInfoMachineNotProvisioned(c *gc.C) {
   871  	_, filesystemAttachment, _ := s.addUnitWithFilesystemUnprovisioned(c, "rootfs", false)
   872  	err := s.storageBackend.SetFilesystemInfo(
   873  		filesystemAttachment.Filesystem(),
   874  		state.FilesystemInfo{Size: 123, FilesystemId: "fs-id"},
   875  	)
   876  	c.Assert(err, jc.ErrorIsNil)
   877  	err = s.storageBackend.SetFilesystemAttachmentInfo(
   878  		filesystemAttachment.Host(),
   879  		filesystemAttachment.Filesystem(),
   880  		state.FilesystemAttachmentInfo{},
   881  	)
   882  	c.Assert(err, gc.ErrorMatches, `cannot set info for filesystem attachment 0/0:0: machine 0 not provisioned`)
   883  }
   884  
   885  func (s *FilesystemIAASModelSuite) TestSetFilesystemInfoVolumeAttachmentNotProvisioned(c *gc.C) {
   886  	filesystem, _, _ := s.addUnitWithFilesystemUnprovisioned(c, "modelscoped-block", true)
   887  	err := s.storageBackend.SetFilesystemInfo(
   888  		filesystem.FilesystemTag(),
   889  		state.FilesystemInfo{Size: 123, FilesystemId: "fs-id"},
   890  	)
   891  	c.Assert(err, gc.ErrorMatches, `cannot set info for filesystem "0": backing volume "0" is not attached`)
   892  }
   893  
   894  func (s *FilesystemIAASModelSuite) TestDestroyFilesystem(c *gc.C) {
   895  	filesystem, _ := s.setupFilesystemAttachment(c, "rootfs")
   896  	assertDestroy := func() {
   897  		s.assertDestroyFilesystem(c, filesystem.FilesystemTag(), state.Dying)
   898  	}
   899  	defer state.SetBeforeHooks(c, s.st, assertDestroy).Check()
   900  	assertDestroy()
   901  }
   902  
   903  func (s *FilesystemStateSuite) TestDestroyFilesystemNotFound(c *gc.C) {
   904  	err := s.storageBackend.DestroyFilesystem(names.NewFilesystemTag("0"), false)
   905  	c.Assert(err, gc.ErrorMatches, `destroying filesystem 0: filesystem "0" not found`)
   906  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   907  }
   908  
   909  func (s *FilesystemStateSuite) TestDestroyFilesystemStorageAssignedNoForce(c *gc.C) {
   910  	// Create a filesystem-type storage instance, and show that we
   911  	// cannot destroy the filesystem while there is storage assigned.
   912  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "rootfs")
   913  	s.maybeAssignUnit(c, u)
   914  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   915  
   916  	err := s.storageBackend.DestroyFilesystem(filesystem.FilesystemTag(), false)
   917  	c.Assert(err, gc.ErrorMatches, "destroying filesystem .*0/0: filesystem is assigned to storage data/0")
   918  
   919  	// We must destroy the unit before we can remove the storage.
   920  	err = u.Destroy()
   921  	c.Assert(err, jc.ErrorIsNil)
   922  	removeStorageInstance(c, s.storageBackend, storageTag)
   923  	s.assertDestroyFilesystem(c, filesystem.FilesystemTag(), state.Dying)
   924  }
   925  
   926  func (s *FilesystemStateSuite) TestDestroyFilesystemStorageAssignedWithForce(c *gc.C) {
   927  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "rootfs")
   928  	s.maybeAssignUnit(c, u)
   929  	filesystem := s.storageInstanceFilesystem(c, storageTag)
   930  
   931  	err := s.storageBackend.DestroyFilesystem(filesystem.FilesystemTag(), true)
   932  	c.Assert(err, jc.ErrorIsNil)
   933  	filesystem = s.filesystem(c, filesystem.FilesystemTag())
   934  	c.Assert(filesystem.Life(), gc.Equals, state.Dying)
   935  }
   936  
   937  func (s *FilesystemIAASModelSuite) TestDestroyFilesystemNoAttachments(c *gc.C) {
   938  	filesystem, machine := s.setupFilesystemAttachment(c, "modelscoped")
   939  
   940  	err := s.storageBackend.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   941  	c.Assert(err, jc.ErrorIsNil)
   942  
   943  	defer state.SetBeforeHooks(c, s.st, func() {
   944  		err := s.storageBackend.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag(), false)
   945  		c.Assert(err, jc.ErrorIsNil)
   946  		assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag())
   947  	}).Check()
   948  
   949  	// There are no more attachments, so the filesystem should
   950  	// be progressed directly to Dead.
   951  	s.assertDestroyFilesystem(c, filesystem.FilesystemTag(), state.Dead)
   952  }
   953  
   954  func (s *FilesystemIAASModelSuite) TestRemoveFilesystem(c *gc.C) {
   955  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
   956  	s.assertDestroyFilesystem(c, filesystem.FilesystemTag(), state.Dying)
   957  	err := s.storageBackend.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   958  	c.Assert(err, jc.ErrorIsNil)
   959  	err = s.storageBackend.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag(), false)
   960  	c.Assert(err, jc.ErrorIsNil)
   961  	assertRemove := func() {
   962  		err = s.storageBackend.RemoveFilesystem(filesystem.FilesystemTag())
   963  		c.Assert(err, jc.ErrorIsNil)
   964  		_, err = s.storageBackend.Filesystem(filesystem.FilesystemTag())
   965  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
   966  	}
   967  	defer state.SetBeforeHooks(c, s.st, assertRemove).Check()
   968  	assertRemove()
   969  }
   970  
   971  func (s *FilesystemIAASModelSuite) TestRemoveFilesystemVolumeBacked(c *gc.C) {
   972  	filesystem, machine := s.setupFilesystemAttachment(c, "modelscoped-block")
   973  	volume := s.filesystemVolume(c, filesystem.FilesystemTag())
   974  	assertVolumeLife := func(life state.Life) {
   975  		volume := s.volume(c, volume.VolumeTag())
   976  		c.Assert(volume.Life(), gc.Equals, life)
   977  	}
   978  	assertVolumeAttachmentLife := func(life state.Life) {
   979  		attachment := s.volumeAttachment(c, machine.MachineTag(), volume.VolumeTag())
   980  		c.Assert(attachment.Life(), gc.Equals, life)
   981  	}
   982  
   983  	s.assertDestroyFilesystem(c, filesystem.FilesystemTag(), state.Dying)
   984  	// Destroying the filesystem does not trigger destruction
   985  	// of the volume. It cannot be destroyed until all remnants
   986  	// of the filesystem are gone.
   987  	assertVolumeLife(state.Alive)
   988  
   989  	err := s.storageBackend.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
   990  	c.Assert(err, jc.ErrorIsNil)
   991  	// Likewise for the volume attachment.
   992  	assertVolumeAttachmentLife(state.Alive)
   993  
   994  	err = s.storageBackend.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag(), false)
   995  	c.Assert(err, jc.ErrorIsNil)
   996  	// Removing the filesystem attachment causes the backing-volume
   997  	// to be detached.
   998  	assertVolumeAttachmentLife(state.Dying)
   999  
  1000  	// Removing the last attachment should cause the filesystem
  1001  	// to be removed, since it is volume-backed and dying.
  1002  	_, err = s.storageBackend.Filesystem(filesystem.FilesystemTag())
  1003  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1004  	// Removing the filesystem causes the backing-volume to be
  1005  	// destroyed.
  1006  	assertVolumeLife(state.Dying)
  1007  
  1008  	assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag())
  1009  }
  1010  
  1011  func (s *FilesystemIAASModelSuite) TestFilesystemVolumeBackedDestroyDetachVolumeFail(c *gc.C) {
  1012  	filesystem, machine := s.setupFilesystemAttachment(c, "modelscoped-block")
  1013  	volume := s.filesystemVolume(c, filesystem.FilesystemTag())
  1014  
  1015  	s.assertDestroyFilesystem(c, filesystem.FilesystemTag(), state.Dying)
  1016  	err := s.storageBackend.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
  1017  	c.Assert(err, jc.ErrorIsNil)
  1018  
  1019  	// Can't destroy (detach) volume until the filesystem (attachment) is removed.
  1020  	err = s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), false)
  1021  	c.Assert(err, gc.ErrorMatches, "detaching volume 0 from machine 0: volume contains attached filesystem")
  1022  	c.Assert(err, jc.Satisfies, state.IsContainsFilesystem)
  1023  	err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
  1024  	c.Assert(err, gc.ErrorMatches, "destroying volume 0: volume contains filesystem")
  1025  	c.Assert(err, jc.Satisfies, state.IsContainsFilesystem)
  1026  	assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag())
  1027  
  1028  	err = s.storageBackend.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag(), false)
  1029  	c.Assert(err, jc.ErrorIsNil)
  1030  	err = s.storageBackend.RemoveFilesystem(filesystem.FilesystemTag())
  1031  	c.Assert(err, jc.ErrorIsNil)
  1032  
  1033  	err = s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), false)
  1034  	c.Assert(err, jc.ErrorIsNil)
  1035  	err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
  1036  	c.Assert(err, jc.ErrorIsNil)
  1037  }
  1038  
  1039  func (s *FilesystemStateSuite) TestRemoveFilesystemNotFound(c *gc.C) {
  1040  	err := s.storageBackend.RemoveFilesystem(names.NewFilesystemTag("42"))
  1041  	c.Assert(err, jc.ErrorIsNil)
  1042  }
  1043  
  1044  func (s *FilesystemIAASModelSuite) TestRemoveFilesystemNotDead(c *gc.C) {
  1045  	filesystem, _ := s.setupFilesystemAttachment(c, "rootfs")
  1046  	err := s.storageBackend.RemoveFilesystem(filesystem.FilesystemTag())
  1047  	c.Assert(err, gc.ErrorMatches, "removing filesystem 0/0: filesystem is not dead")
  1048  	s.assertDestroyFilesystem(c, filesystem.FilesystemTag(), state.Dying)
  1049  	err = s.storageBackend.RemoveFilesystem(filesystem.FilesystemTag())
  1050  	c.Assert(err, gc.ErrorMatches, "removing filesystem 0/0: filesystem is not dead")
  1051  }
  1052  
  1053  func (s *FilesystemIAASModelSuite) TestDetachFilesystem(c *gc.C) {
  1054  	filesystem, machine := s.setupFilesystemAttachment(c, "modelscoped")
  1055  	assertDetach := func() {
  1056  		err := s.storageBackend.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
  1057  		c.Assert(err, jc.ErrorIsNil)
  1058  		attachment := s.filesystemAttachment(c, machine.MachineTag(), filesystem.FilesystemTag())
  1059  		c.Assert(attachment.Life(), gc.Equals, state.Dying)
  1060  	}
  1061  	defer state.SetBeforeHooks(c, s.st, assertDetach).Check()
  1062  	assertDetach()
  1063  }
  1064  
  1065  func (s *FilesystemIAASModelSuite) TestRemoveLastFilesystemAttachment(c *gc.C) {
  1066  	filesystem, machine := s.setupFilesystemAttachment(c, "modelscoped")
  1067  
  1068  	err := s.storageBackend.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
  1069  	c.Assert(err, jc.ErrorIsNil)
  1070  
  1071  	err = s.storageBackend.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag(), false)
  1072  	c.Assert(err, jc.ErrorIsNil)
  1073  
  1074  	// The filesystem has no attachments, so it should go straight to Dead.
  1075  	s.assertDestroyFilesystem(c, filesystem.FilesystemTag(), state.Dead)
  1076  	assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag())
  1077  }
  1078  
  1079  func (s *FilesystemIAASModelSuite) TestRemoveLastFilesystemAttachmentConcurrently(c *gc.C) {
  1080  	filesystem, machine := s.setupFilesystemAttachment(c, "modelscoped")
  1081  
  1082  	err := s.storageBackend.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
  1083  	c.Assert(err, jc.ErrorIsNil)
  1084  
  1085  	defer state.SetBeforeHooks(c, s.st, func() {
  1086  		s.assertDestroyFilesystem(c, filesystem.FilesystemTag(), state.Dying)
  1087  	}).Check()
  1088  
  1089  	err = s.storageBackend.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag(), false)
  1090  	c.Assert(err, jc.ErrorIsNil)
  1091  
  1092  	// Last attachment was removed, and the filesystem was (concurrently)
  1093  	// destroyed, so the filesystem should be Dead.
  1094  	filesystem = s.filesystem(c, filesystem.FilesystemTag())
  1095  	c.Assert(filesystem.Life(), gc.Equals, state.Dead)
  1096  	assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag())
  1097  }
  1098  
  1099  func (s *FilesystemStateSuite) TestRemoveFilesystemAttachmentNotFound(c *gc.C) {
  1100  	err := s.storageBackend.RemoveFilesystemAttachment(names.NewMachineTag("42"), names.NewFilesystemTag("42"), false)
  1101  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1102  	c.Assert(err, gc.ErrorMatches, `removing attachment of filesystem 42 from machine 42: filesystem "42" on "machine 42" not found`)
  1103  }
  1104  
  1105  func (s *FilesystemIAASModelSuite) TestRemoveFilesystemAttachmentConcurrently(c *gc.C) {
  1106  	filesystem, machine := s.setupFilesystemAttachment(c, "modelscoped")
  1107  	err := s.storageBackend.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
  1108  	c.Assert(err, jc.ErrorIsNil)
  1109  	remove := func() {
  1110  		err := s.storageBackend.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag(), false)
  1111  		c.Assert(err, jc.ErrorIsNil)
  1112  		assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag())
  1113  	}
  1114  	defer state.SetBeforeHooks(c, s.st, remove).Check()
  1115  	remove()
  1116  }
  1117  
  1118  func (s *FilesystemIAASModelSuite) TestRemoveFilesystemAttachmentAlive(c *gc.C) {
  1119  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
  1120  	err := s.storageBackend.RemoveFilesystemAttachment(machine.MachineTag(), filesystem.FilesystemTag(), false)
  1121  	c.Assert(err, gc.ErrorMatches, "removing attachment of filesystem 0/0 from machine 0: filesystem attachment is not dying")
  1122  }
  1123  
  1124  func (s *FilesystemIAASModelSuite) TestRemoveMachineRemovesFilesystems(c *gc.C) {
  1125  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
  1126  
  1127  	c.Assert(machine.Destroy(), jc.ErrorIsNil)
  1128  	c.Assert(machine.EnsureDead(), jc.ErrorIsNil)
  1129  	c.Assert(machine.Remove(), jc.ErrorIsNil)
  1130  
  1131  	// Machine is gone: filesystem should be gone too.
  1132  	_, err := s.storageBackend.Filesystem(filesystem.FilesystemTag())
  1133  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1134  
  1135  	attachments, err := s.storageBackend.MachineFilesystemAttachments(machine.MachineTag())
  1136  	c.Assert(err, jc.ErrorIsNil)
  1137  	c.Assert(attachments, gc.HasLen, 0)
  1138  }
  1139  
  1140  func (s *FilesystemIAASModelSuite) TestDestroyMachineRemovesNonDetachableFilesystems(c *gc.C) {
  1141  	filesystem, machine := s.setupFilesystemAttachment(c, "loop")
  1142  
  1143  	// Destroy the machine and run cleanups, which should cause the
  1144  	// non-detachable filesystems to be destroyed, detached, and
  1145  	// finally removed.
  1146  	c.Assert(machine.Destroy(), jc.ErrorIsNil)
  1147  	assertCleanupRuns(c, s.st)
  1148  
  1149  	_, err := s.storageBackend.Filesystem(filesystem.FilesystemTag())
  1150  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1151  }
  1152  
  1153  func (s *FilesystemIAASModelSuite) TestDestroyMachineDetachesDetachableFilesystems(c *gc.C) {
  1154  	filesystem, machine := s.setupFilesystemAttachment(c, "modelscoped-block")
  1155  
  1156  	// Destroy the machine and run cleanups, which should cause the
  1157  	// detachable filesystems to be detached, but not destroyed.
  1158  	c.Assert(machine.Destroy(), jc.ErrorIsNil)
  1159  	assertCleanupRuns(c, s.st)
  1160  	s.testfilesystemDetached(
  1161  		c, machine.MachineTag(), filesystem.FilesystemTag(),
  1162  	)
  1163  }
  1164  
  1165  // TODO(caas) - destroy caas storage when unit dies
  1166  func (s *FilesystemIAASModelSuite) TestDestroyHostDetachesDetachableFilesystems(c *gc.C) {
  1167  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "modelscoped-block")
  1168  	hostTag := s.maybeAssignUnit(c, u)
  1169  	filesystem := s.storageInstanceFilesystem(c, storageTag)
  1170  
  1171  	// Destroying the unit should, if necessary, destroy its host machine, which
  1172  	// triggers the detachment of storage.
  1173  	s.obliterateUnit(c, u.UnitTag())
  1174  	assertCleanupRuns(c, s.st)
  1175  
  1176  	s.testfilesystemDetached(
  1177  		c, hostTag, filesystem.FilesystemTag(),
  1178  	)
  1179  }
  1180  
  1181  func (s *FilesystemStateSuite) testfilesystemDetached(
  1182  	c *gc.C,
  1183  	hostTag names.Tag,
  1184  	filesystemTag names.FilesystemTag,
  1185  ) {
  1186  	// Filesystem is still alive...
  1187  	filesystem, err := s.storageBackend.Filesystem(filesystemTag)
  1188  	c.Assert(err, jc.ErrorIsNil)
  1189  	c.Assert(filesystem.Life(), gc.Equals, state.Alive)
  1190  
  1191  	// ... but it has been detached.
  1192  	_, err = s.storageBackend.FilesystemAttachment(hostTag, filesystemTag)
  1193  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1194  
  1195  	filesystemStatus, err := filesystem.Status()
  1196  	c.Assert(err, jc.ErrorIsNil)
  1197  	c.Assert(filesystemStatus.Status, gc.Equals, status.Detached)
  1198  	c.Assert(filesystemStatus.Message, gc.Equals, "")
  1199  }
  1200  
  1201  func (s *FilesystemIAASModelSuite) TestDestroyManualMachineDoesntRemoveNonDetachableFilesystems(c *gc.C) {
  1202  	filesystem, machine := s.setupFilesystemAttachment(c, "loop")
  1203  
  1204  	// Make this a manual machine, so the cleanup.
  1205  	err := machine.SetProvisioned("inst-id", "", "manual:machine", nil)
  1206  	c.Assert(err, jc.ErrorIsNil)
  1207  
  1208  	// Destroy the machine and run cleanups, which should cause the
  1209  	// non-detachable filesystems and attachments to be set to Dying,
  1210  	// but not completely removed.
  1211  	c.Assert(machine.Destroy(), jc.ErrorIsNil)
  1212  	assertCleanupRuns(c, s.st)
  1213  
  1214  	filesystem, err = s.storageBackend.Filesystem(filesystem.FilesystemTag())
  1215  	c.Assert(err, jc.ErrorIsNil)
  1216  	c.Assert(filesystem.Life(), gc.Equals, state.Dying)
  1217  	attachment, err := s.storageBackend.FilesystemAttachment(
  1218  		machine.MachineTag(),
  1219  		filesystem.FilesystemTag(),
  1220  	)
  1221  	c.Assert(err, jc.ErrorIsNil)
  1222  	c.Assert(attachment.Life(), gc.Equals, state.Dying)
  1223  }
  1224  
  1225  func (s *FilesystemIAASModelSuite) TestDestroyManualMachineDoesntDetachDetachableFilesystems(c *gc.C) {
  1226  	filesystem, machine := s.setupFilesystemAttachment(c, "modelscoped-block")
  1227  
  1228  	// Make this a manual machine, so the cleanup.
  1229  	err := machine.SetProvisioned("inst-id", "", "manual:machine", nil)
  1230  	c.Assert(err, jc.ErrorIsNil)
  1231  
  1232  	// Destroy the machine and run cleanups, which should cause the
  1233  	// detachable filesystem attachments to be set to Dying, but not
  1234  	// completely removed. The filesystem itself should be left Alive.
  1235  	c.Assert(machine.Destroy(), jc.ErrorIsNil)
  1236  	assertCleanupRuns(c, s.st)
  1237  
  1238  	filesystem, err = s.storageBackend.Filesystem(filesystem.FilesystemTag())
  1239  	c.Assert(err, jc.ErrorIsNil)
  1240  	c.Assert(filesystem.Life(), gc.Equals, state.Alive)
  1241  	attachment, err := s.storageBackend.FilesystemAttachment(
  1242  		machine.MachineTag(),
  1243  		filesystem.FilesystemTag(),
  1244  	)
  1245  	c.Assert(err, jc.ErrorIsNil)
  1246  	c.Assert(attachment.Life(), gc.Equals, state.Dying)
  1247  }
  1248  
  1249  func (s *FilesystemIAASModelSuite) TestFilesystemMachineScoped(c *gc.C) {
  1250  	// Machine-scoped filesystems created unassigned to a storage
  1251  	// instance are bound to the machine.
  1252  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
  1253  
  1254  	err := s.storageBackend.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
  1255  	c.Assert(err, gc.ErrorMatches, "detaching filesystem 0/0 from machine 0: filesystem is not detachable")
  1256  	err = machine.Destroy()
  1257  	c.Assert(err, jc.ErrorIsNil)
  1258  	err = machine.EnsureDead()
  1259  	c.Assert(err, jc.ErrorIsNil)
  1260  	err = machine.Remove()
  1261  	c.Assert(err, jc.ErrorIsNil)
  1262  
  1263  	_, err = s.storageBackend.Filesystem(filesystem.FilesystemTag())
  1264  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1265  	_, err = s.storageBackend.FilesystemAttachment(
  1266  		machine.MachineTag(),
  1267  		filesystem.FilesystemTag(),
  1268  	)
  1269  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1270  }
  1271  
  1272  func (s *FilesystemStateSuite) TestFilesystemRemoveStorageDestroysFilesystem(c *gc.C) {
  1273  	// Filesystems created assigned to a storage instance are bound
  1274  	// to the machine/model, and not the storage. i.e. storage is
  1275  	// persistent by default.
  1276  	_, u, storageTag := s.setupSingleStorage(c, "filesystem", "rootfs")
  1277  	s.maybeAssignUnit(c, u)
  1278  	filesystem := s.storageInstanceFilesystem(c, storageTag)
  1279  
  1280  	// The filesystem should transition to Dying when the storage is removed.
  1281  	// We must destroy the unit before we can remove the storage.
  1282  	err := u.Destroy()
  1283  	c.Assert(err, jc.ErrorIsNil)
  1284  	removeStorageInstance(c, s.storageBackend, storageTag)
  1285  	filesystem = s.filesystem(c, filesystem.FilesystemTag())
  1286  	c.Assert(filesystem.Life(), gc.Equals, state.Dying)
  1287  }
  1288  
  1289  func (s *FilesystemIAASModelSuite) TestEnsureMachineDeadAddFilesystemConcurrently(c *gc.C) {
  1290  	_, machine := s.setupFilesystemAttachment(c, "rootfs")
  1291  	addFilesystem := func() {
  1292  		_, u, _ := s.setupSingleStorage(c, "filesystem", "rootfs")
  1293  		err := u.AssignToMachine(machine)
  1294  		c.Assert(err, jc.ErrorIsNil)
  1295  		s.obliterateUnit(c, u.UnitTag())
  1296  	}
  1297  	defer state.SetBeforeHooks(c, s.st, addFilesystem).Check()
  1298  
  1299  	// Adding another filesystem to the machine will cause EnsureDead to
  1300  	// retry, but it will succeed because both filesystems are inherently
  1301  	// machine-bound.
  1302  	err := machine.EnsureDead()
  1303  	c.Assert(err, jc.ErrorIsNil)
  1304  }
  1305  
  1306  func (s *FilesystemIAASModelSuite) TestEnsureMachineDeadRemoveFilesystemConcurrently(c *gc.C) {
  1307  	filesystem, machine := s.setupFilesystemAttachment(c, "rootfs")
  1308  	removeFilesystem := func() {
  1309  		s.obliterateFilesystem(c, filesystem.FilesystemTag())
  1310  	}
  1311  	defer state.SetBeforeHooks(c, s.st, removeFilesystem).Check()
  1312  
  1313  	// Removing a filesystem concurrently does not cause a transaction failure.
  1314  	err := machine.EnsureDead()
  1315  	c.Assert(err, jc.ErrorIsNil)
  1316  }
  1317  
  1318  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsSingletonNoLocation(c *gc.C) {
  1319  	s.testFilesystemAttachmentParams(c, 0, 1, "", state.FilesystemAttachmentParams{
  1320  		Location: "/var/lib/juju/storage/data/0",
  1321  	})
  1322  }
  1323  
  1324  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsMultipleNoLocation(c *gc.C) {
  1325  	s.testFilesystemAttachmentParams(c, 0, -1, "", state.FilesystemAttachmentParams{
  1326  		Location: "/var/lib/juju/storage/data/0",
  1327  	})
  1328  }
  1329  
  1330  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsSingletonLocation(c *gc.C) {
  1331  	s.testFilesystemAttachmentParams(c, 0, 1, "/srv", state.FilesystemAttachmentParams{
  1332  		Location: "/srv",
  1333  	})
  1334  }
  1335  
  1336  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsMultipleLocation(c *gc.C) {
  1337  	s.testFilesystemAttachmentParams(c, 0, -1, "/srv", state.FilesystemAttachmentParams{
  1338  		Location: "/srv/data/0",
  1339  	})
  1340  }
  1341  
  1342  func (s *FilesystemStateSuite) testFilesystemAttachmentParams(
  1343  	c *gc.C, countMin, countMax int, location string,
  1344  	expect state.FilesystemAttachmentParams,
  1345  ) {
  1346  	ch := s.createStorageCharmWithSeries(c, "storage-filesystem", charm.Storage{
  1347  		Name:     "data",
  1348  		Type:     charm.StorageFilesystem,
  1349  		CountMin: countMin,
  1350  		CountMax: countMax,
  1351  		Location: location,
  1352  	}, s.series)
  1353  	storage := map[string]state.StorageConstraints{
  1354  		"data": makeStorageCons("rootfs", 1024, 1),
  1355  	}
  1356  
  1357  	app := s.AddTestingApplicationWithStorage(c, "storage-filesystem", ch, storage)
  1358  	unit, err := app.AddUnit(state.AddUnitParams{})
  1359  	c.Assert(err, jc.ErrorIsNil)
  1360  	hostTag := s.maybeAssignUnit(c, unit)
  1361  
  1362  	storageTag := names.NewStorageTag("data/0")
  1363  	filesystem := s.storageInstanceFilesystem(c, storageTag)
  1364  	filesystemAttachment := s.filesystemAttachment(
  1365  		c, hostTag, filesystem.FilesystemTag(),
  1366  	)
  1367  	params, ok := filesystemAttachment.Params()
  1368  	c.Assert(ok, jc.IsTrue)
  1369  	c.Assert(params, jc.DeepEquals, expect)
  1370  }
  1371  
  1372  func (s *FilesystemIAASModelSuite) TestFilesystemAttachmentParamsLocationConflictConcurrent(c *gc.C) {
  1373  	s.testFilesystemAttachmentParamsConcurrent(
  1374  		c, "/srv", "/srv",
  1375  		`cannot assign unit "storage-filesystem-after/0" to machine 0: `+
  1376  			`validating filesystem mount points: `+
  1377  			`mount point "/srv" for "data" storage contains mount point "/srv" for "data" storage`)
  1378  }
  1379  
  1380  func (s *FilesystemIAASModelSuite) TestFilesystemAttachmentParamsLocationAutoConcurrent(c *gc.C) {
  1381  	s.testFilesystemAttachmentParamsConcurrent(c, "", "", "")
  1382  }
  1383  
  1384  func (s *FilesystemIAASModelSuite) TestFilesystemAttachmentParamsLocationAutoAndManualConcurrent(c *gc.C) {
  1385  	s.testFilesystemAttachmentParamsConcurrent(c, "", "/srv", "")
  1386  }
  1387  
  1388  func (s *FilesystemStateSuite) testFilesystemAttachmentParamsConcurrent(c *gc.C, locBefore, locAfter, expectErr string) {
  1389  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1390  	c.Assert(err, jc.ErrorIsNil)
  1391  
  1392  	storage := map[string]state.StorageConstraints{
  1393  		"data": makeStorageCons("rootfs", 1024, 1),
  1394  	}
  1395  
  1396  	deploy := func(rev int, location, applicationname string) error {
  1397  		ch := s.createStorageCharmRev(c, "storage-filesystem", charm.Storage{
  1398  			Name:     "data",
  1399  			Type:     charm.StorageFilesystem,
  1400  			CountMin: 1,
  1401  			CountMax: 1,
  1402  			Location: location,
  1403  		}, rev)
  1404  		app := s.AddTestingApplicationWithStorage(c, applicationname, ch, storage)
  1405  		unit, err := app.AddUnit(state.AddUnitParams{})
  1406  		c.Assert(err, jc.ErrorIsNil)
  1407  		return unit.AssignToMachine(machine)
  1408  	}
  1409  
  1410  	defer state.SetBeforeHooks(c, s.st, func() {
  1411  		err := deploy(1, locBefore, "storage-filesystem-before")
  1412  		c.Assert(err, jc.ErrorIsNil)
  1413  	}).Check()
  1414  
  1415  	err = deploy(2, locAfter, "storage-filesystem-after")
  1416  	if expectErr != "" {
  1417  		c.Assert(err, gc.ErrorMatches, expectErr)
  1418  	} else {
  1419  		c.Assert(err, jc.ErrorIsNil)
  1420  	}
  1421  }
  1422  
  1423  func (s *FilesystemIAASModelSuite) TestFilesystemAttachmentParamsConcurrentRemove(c *gc.C) {
  1424  	// this creates a filesystem mounted at "/srv".
  1425  	filesystem, machine := s.setupFilesystemAttachment(c, "modelscoped")
  1426  
  1427  	ch := s.createStorageCharm(c, "storage-filesystem", charm.Storage{
  1428  		Name:     "data",
  1429  		Type:     charm.StorageFilesystem,
  1430  		CountMin: 1,
  1431  		CountMax: 1,
  1432  		Location: "/not/in/srv",
  1433  	})
  1434  	app := s.AddTestingApplication(c, "storage-filesystem", ch)
  1435  	unit, err := app.AddUnit(state.AddUnitParams{})
  1436  	c.Assert(err, jc.ErrorIsNil)
  1437  
  1438  	defer state.SetBeforeHooks(c, s.st, func() {
  1439  		err := s.storageBackend.DetachFilesystem(machine.MachineTag(), filesystem.FilesystemTag())
  1440  		c.Assert(err, jc.ErrorIsNil)
  1441  		err = s.storageBackend.RemoveFilesystemAttachment(
  1442  			machine.MachineTag(), filesystem.FilesystemTag(), false,
  1443  		)
  1444  		c.Assert(err, jc.ErrorIsNil)
  1445  	}).Check()
  1446  
  1447  	err = unit.AssignToMachine(machine)
  1448  	c.Assert(err, jc.ErrorIsNil)
  1449  }
  1450  
  1451  func (s *FilesystemStateSuite) TestFilesystemAttachmentParamsLocationStorageDir(c *gc.C) {
  1452  	ch := s.createStorageCharmWithSeries(c, "storage-filesystem", charm.Storage{
  1453  		Name:     "data",
  1454  		Type:     charm.StorageFilesystem,
  1455  		CountMin: 1,
  1456  		CountMax: 1,
  1457  		Location: "/var/lib/juju/storage",
  1458  	}, s.series)
  1459  	app := s.AddTestingApplication(c, "storage-filesystem", ch)
  1460  	unit, err := app.AddUnit(state.AddUnitParams{})
  1461  	if s.series != "kubernetes" {
  1462  		c.Assert(err, jc.ErrorIsNil)
  1463  		err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
  1464  	}
  1465  	c.Assert(err, gc.ErrorMatches, `.*`+
  1466  		`getting filesystem mount point for storage data: `+
  1467  		`invalid location "/var/lib/juju/storage": `+
  1468  		`must not fall within "/var/lib/juju/storage"`)
  1469  }
  1470  
  1471  func (s *FilesystemIAASModelSuite) TestFilesystemAttachmentLocationConflict(c *gc.C) {
  1472  	// this creates a filesystem mounted at "/srv".
  1473  	_, machine := s.setupFilesystemAttachment(c, "rootfs")
  1474  
  1475  	ch := s.createStorageCharm(c, "storage-filesystem", charm.Storage{
  1476  		Name:     "data",
  1477  		Type:     charm.StorageFilesystem,
  1478  		CountMin: 1,
  1479  		CountMax: 1,
  1480  		Location: "/srv/within",
  1481  	})
  1482  	app := s.AddTestingApplication(c, "storage-filesystem", ch)
  1483  
  1484  	u, err := app.AddUnit(state.AddUnitParams{})
  1485  	c.Assert(err, jc.ErrorIsNil)
  1486  	err = u.AssignToMachine(machine)
  1487  	c.Assert(err, gc.ErrorMatches,
  1488  		`cannot assign unit "storage-filesystem/0" to machine 0: `+
  1489  			`validating filesystem mount points: `+
  1490  			`mount point "/srv" for filesystem 0/0 contains `+
  1491  			`mount point "/srv/within" for "data" storage`)
  1492  }
  1493  
  1494  func (s *FilesystemIAASModelSuite) TestAddExistingFilesystem(c *gc.C) {
  1495  	fsInfoIn := state.FilesystemInfo{
  1496  		Pool:         "modelscoped",
  1497  		Size:         123,
  1498  		FilesystemId: "foo",
  1499  	}
  1500  	storageTag, err := s.storageBackend.AddExistingFilesystem(fsInfoIn, nil, "pgdata")
  1501  	c.Assert(err, jc.ErrorIsNil)
  1502  	c.Assert(storageTag, gc.Equals, names.NewStorageTag("pgdata/0"))
  1503  
  1504  	filesystem, err := s.storageBackend.StorageInstanceFilesystem(storageTag)
  1505  	c.Assert(err, jc.ErrorIsNil)
  1506  	fsInfoOut, err := filesystem.Info()
  1507  	c.Assert(err, jc.ErrorIsNil)
  1508  	c.Assert(fsInfoOut, jc.DeepEquals, fsInfoIn)
  1509  
  1510  	fsStatus, err := filesystem.Status()
  1511  	c.Assert(err, jc.ErrorIsNil)
  1512  	c.Assert(fsStatus.Status, gc.Equals, status.Detached)
  1513  }
  1514  
  1515  func (s *FilesystemIAASModelSuite) TestAddExistingFilesystemEmptyFilesystemId(c *gc.C) {
  1516  	fsInfoIn := state.FilesystemInfo{
  1517  		Pool: "modelscoped",
  1518  		Size: 123,
  1519  	}
  1520  	_, err := s.storageBackend.AddExistingFilesystem(fsInfoIn, nil, "pgdata")
  1521  	c.Assert(err, gc.ErrorMatches, "cannot add existing filesystem: empty filesystem ID not valid")
  1522  }
  1523  
  1524  func (s *FilesystemIAASModelSuite) TestAddExistingFilesystemVolumeBacked(c *gc.C) {
  1525  	fsInfoIn := state.FilesystemInfo{
  1526  		Pool: "modelscoped-block",
  1527  		Size: 123,
  1528  	}
  1529  	volInfoIn := state.VolumeInfo{
  1530  		Pool:     "modelscoped-block",
  1531  		Size:     123,
  1532  		VolumeId: "foo",
  1533  	}
  1534  	storageTag, err := s.storageBackend.AddExistingFilesystem(fsInfoIn, &volInfoIn, "pgdata")
  1535  	c.Assert(err, jc.ErrorIsNil)
  1536  	c.Assert(storageTag, gc.Equals, names.NewStorageTag("pgdata/0"))
  1537  
  1538  	filesystem, err := s.storageBackend.StorageInstanceFilesystem(storageTag)
  1539  	c.Assert(err, jc.ErrorIsNil)
  1540  	fsInfoOut, err := filesystem.Info()
  1541  	c.Assert(err, jc.ErrorIsNil)
  1542  	fsInfoIn.FilesystemId = "filesystem-0" // set by AddExistingFilesystem
  1543  	c.Assert(fsInfoOut, jc.DeepEquals, fsInfoIn)
  1544  
  1545  	fsStatus, err := filesystem.Status()
  1546  	c.Assert(err, jc.ErrorIsNil)
  1547  	c.Assert(fsStatus.Status, gc.Equals, status.Detached)
  1548  
  1549  	volume, err := s.storageBackend.StorageInstanceVolume(storageTag)
  1550  	c.Assert(err, jc.ErrorIsNil)
  1551  	volInfoOut, err := volume.Info()
  1552  	c.Assert(err, jc.ErrorIsNil)
  1553  	c.Assert(volInfoOut, jc.DeepEquals, volInfoIn)
  1554  
  1555  	volStatus, err := volume.Status()
  1556  	c.Assert(err, jc.ErrorIsNil)
  1557  	c.Assert(volStatus.Status, gc.Equals, status.Detached)
  1558  }
  1559  
  1560  func (s *FilesystemIAASModelSuite) TestAddExistingFilesystemVolumeBackedVolumeInfoMissing(c *gc.C) {
  1561  	fsInfo := state.FilesystemInfo{
  1562  		Pool:         "modelscoped-block",
  1563  		Size:         123,
  1564  		FilesystemId: "foo",
  1565  	}
  1566  	_, err := s.storageBackend.AddExistingFilesystem(fsInfo, nil, "pgdata")
  1567  	c.Assert(err, gc.ErrorMatches, "cannot add existing filesystem: backing volume info missing")
  1568  }
  1569  
  1570  func (s *FilesystemStateSuite) TestAddExistingFilesystemVolumeBackedFilesystemIdSupplied(c *gc.C) {
  1571  	fsInfo := state.FilesystemInfo{
  1572  		Pool:         "modelscoped-block",
  1573  		Size:         123,
  1574  		FilesystemId: "foo",
  1575  	}
  1576  	volInfo := state.VolumeInfo{
  1577  		Pool:     "modelscoped-block",
  1578  		Size:     123,
  1579  		VolumeId: "foo",
  1580  	}
  1581  	_, err := s.storageBackend.AddExistingFilesystem(fsInfo, &volInfo, "pgdata")
  1582  	c.Assert(err, gc.ErrorMatches, "cannot add existing filesystem: non-empty filesystem ID with backing volume not valid")
  1583  }
  1584  
  1585  func (s *FilesystemStateSuite) TestAddExistingFilesystemVolumeBackedEmptyVolumeId(c *gc.C) {
  1586  	fsInfo := state.FilesystemInfo{
  1587  		Pool: "modelscoped-block",
  1588  		Size: 123,
  1589  	}
  1590  	volInfo := state.VolumeInfo{
  1591  		Pool: "modelscoped-block",
  1592  		Size: 123,
  1593  	}
  1594  	_, err := s.storageBackend.AddExistingFilesystem(fsInfo, &volInfo, "pgdata")
  1595  	c.Assert(err, gc.ErrorMatches, "cannot add existing filesystem: empty backing volume ID not valid")
  1596  }
  1597  
  1598  func (s *FilesystemStateSuite) setupFilesystemAttachment(c *gc.C, pool string) (state.Filesystem, *state.Machine) {
  1599  	machine, err := s.st.AddOneMachine(state.MachineTemplate{
  1600  		Base: state.UbuntuBase("12.10"),
  1601  		Jobs: []state.MachineJob{state.JobHostUnits},
  1602  		Filesystems: []state.HostFilesystemParams{{
  1603  			Filesystem: state.FilesystemParams{Pool: pool, Size: 1024},
  1604  			Attachment: state.FilesystemAttachmentParams{
  1605  				Location: "/srv",
  1606  			},
  1607  		}},
  1608  	})
  1609  	c.Assert(err, jc.ErrorIsNil)
  1610  	sb, err := state.NewStorageBackend(s.st)
  1611  	c.Assert(err, jc.ErrorIsNil)
  1612  	attachments, err := sb.MachineFilesystemAttachments(machine.MachineTag())
  1613  	c.Assert(err, jc.ErrorIsNil)
  1614  	c.Assert(attachments, gc.HasLen, 1)
  1615  	c.Assert(err, jc.ErrorIsNil)
  1616  	assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag())
  1617  	return s.filesystem(c, attachments[0].Filesystem()), machine
  1618  }
  1619  
  1620  func removeFilesystemStorageInstance(c *gc.C, sb *state.StorageBackend, filesystemTag names.FilesystemTag) {
  1621  	filesystem, err := sb.Filesystem(filesystemTag)
  1622  	c.Assert(err, jc.ErrorIsNil)
  1623  	storageTag, err := filesystem.Storage()
  1624  	c.Assert(err, jc.ErrorIsNil)
  1625  	removeStorageInstance(c, sb, storageTag)
  1626  }
  1627  
  1628  func (s *FilesystemStateSuite) assertDestroyFilesystem(c *gc.C, tag names.FilesystemTag, life state.Life) {
  1629  	err := s.storageBackend.DestroyFilesystem(tag, false)
  1630  	c.Assert(err, jc.ErrorIsNil)
  1631  	filesystem := s.filesystem(c, tag)
  1632  	c.Assert(filesystem.Life(), gc.Equals, life)
  1633  }