github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/volume_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/mgo/v3/bson"
     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/core/constraints"
    14  	"github.com/juju/juju/core/instance"
    15  	"github.com/juju/juju/state"
    16  	stateerrors "github.com/juju/juju/state/errors"
    17  	"github.com/juju/juju/state/testing"
    18  	"github.com/juju/juju/storage"
    19  	"github.com/juju/juju/storage/poolmanager"
    20  	"github.com/juju/juju/storage/provider"
    21  	dummystorage "github.com/juju/juju/storage/provider/dummy"
    22  )
    23  
    24  type VolumeStateSuite struct {
    25  	StorageStateSuiteBase
    26  }
    27  
    28  var _ = gc.Suite(&VolumeStateSuite{})
    29  
    30  func (s *VolumeStateSuite) TestAddMachine(c *gc.C) {
    31  	_, unit, _ := s.setupSingleStorage(c, "block", "loop-pool")
    32  	err := s.State.AssignUnit(unit, state.AssignCleanEmpty)
    33  	c.Assert(err, jc.ErrorIsNil)
    34  	s.assertMachineVolume(c, unit)
    35  }
    36  
    37  func (s *VolumeStateSuite) TestAssignToMachine(c *gc.C) {
    38  	_, unit, _ := s.setupSingleStorage(c, "block", "loop-pool")
    39  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
    40  	c.Assert(err, jc.ErrorIsNil)
    41  	err = unit.AssignToMachine(machine)
    42  	c.Assert(err, jc.ErrorIsNil)
    43  	s.assertMachineVolume(c, unit)
    44  }
    45  
    46  func (s *VolumeStateSuite) assertMachineVolume(c *gc.C, unit *state.Unit) {
    47  	assignedMachineId, err := unit.AssignedMachineId()
    48  	c.Assert(err, jc.ErrorIsNil)
    49  
    50  	storageAttachments, err := s.storageBackend.UnitStorageAttachments(unit.UnitTag())
    51  	c.Assert(err, jc.ErrorIsNil)
    52  	c.Assert(storageAttachments, gc.HasLen, 1)
    53  	storageInstance, err := s.storageBackend.StorageInstance(storageAttachments[0].StorageInstance())
    54  	c.Assert(err, jc.ErrorIsNil)
    55  
    56  	volume := s.storageInstanceVolume(c, storageInstance.StorageTag())
    57  	c.Assert(volume.VolumeTag(), gc.Equals, names.NewVolumeTag("0/0"))
    58  	volumeStorageTag, err := volume.StorageInstance()
    59  	c.Assert(err, jc.ErrorIsNil)
    60  	c.Assert(volumeStorageTag, gc.Equals, storageInstance.StorageTag())
    61  	_, err = volume.Info()
    62  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
    63  	_, ok := volume.Params()
    64  	c.Assert(ok, jc.IsTrue)
    65  
    66  	machine, err := s.State.Machine(assignedMachineId)
    67  	c.Assert(err, jc.ErrorIsNil)
    68  
    69  	volumeAttachments, err := s.storageBackend.MachineVolumeAttachments(machine.MachineTag())
    70  	c.Assert(err, jc.ErrorIsNil)
    71  	c.Assert(volumeAttachments, gc.HasLen, 1)
    72  	c.Assert(volumeAttachments[0].Volume(), gc.Equals, volume.VolumeTag())
    73  	c.Assert(volumeAttachments[0].Host(), gc.Equals, machine.MachineTag())
    74  	_, err = volumeAttachments[0].Info()
    75  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
    76  	_, ok = volumeAttachments[0].Params()
    77  	c.Assert(ok, jc.IsTrue)
    78  
    79  	_, err = s.storageBackend.VolumeAttachment(machine.MachineTag(), volume.VolumeTag())
    80  	c.Assert(err, jc.ErrorIsNil)
    81  
    82  	assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag())
    83  }
    84  
    85  func (s *VolumeStateSuite) TestAddApplicationInvalidPool(c *gc.C) {
    86  	ch := s.AddTestingCharm(c, "storage-block")
    87  	testStorage := map[string]state.StorageConstraints{
    88  		"data": makeStorageCons("invalid-pool", 1024, 1),
    89  	}
    90  	_, err := s.State.AddApplication(state.AddApplicationArgs{
    91  		Name: "storage-block", Charm: ch,
    92  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
    93  			OS:      "ubuntu",
    94  			Channel: "20.04/stable",
    95  		}},
    96  		Storage: testStorage,
    97  	})
    98  	c.Assert(err, gc.ErrorMatches, `.* pool "invalid-pool" not found`)
    99  }
   100  
   101  func (s *VolumeStateSuite) TestAddApplicationNoUserDefaultPool(c *gc.C) {
   102  	ch := s.AddTestingCharm(c, "storage-block")
   103  	testStorage := map[string]state.StorageConstraints{
   104  		"data": makeStorageCons("", 1024, 1),
   105  	}
   106  	app, err := s.State.AddApplication(state.AddApplicationArgs{
   107  		Name: "storage-block", Charm: ch,
   108  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   109  			OS:      "ubuntu",
   110  			Channel: "20.04/stable",
   111  		}},
   112  		Storage: testStorage,
   113  	})
   114  	c.Assert(err, jc.ErrorIsNil)
   115  	cons, err := app.StorageConstraints()
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	c.Assert(cons, jc.DeepEquals, map[string]state.StorageConstraints{
   118  		"data": {
   119  			Pool:  "loop",
   120  			Size:  1024,
   121  			Count: 1,
   122  		},
   123  		"allecto": {
   124  			Pool:  "loop",
   125  			Size:  1024,
   126  			Count: 0,
   127  		},
   128  	})
   129  }
   130  
   131  func (s *VolumeStateSuite) TestAddApplicationDefaultPool(c *gc.C) {
   132  	// Register a default pool.
   133  	pm := poolmanager.New(state.NewStateSettings(s.State), storage.ChainedProviderRegistry{
   134  		dummystorage.StorageProviders(),
   135  		provider.CommonStorageProviders(),
   136  	})
   137  	_, err := pm.Create("default-block", provider.LoopProviderType, map[string]interface{}{})
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	err = s.Model.UpdateModelConfig(map[string]interface{}{
   140  		"storage-default-block-source": "default-block",
   141  	}, nil)
   142  	c.Assert(err, jc.ErrorIsNil)
   143  
   144  	ch := s.AddTestingCharm(c, "storage-block")
   145  	testStorage := map[string]state.StorageConstraints{
   146  		"data": makeStorageCons("", 1024, 1),
   147  	}
   148  	app := s.AddTestingApplicationWithStorage(c, "storage-block", ch, testStorage)
   149  	cons, err := app.StorageConstraints()
   150  	c.Assert(err, jc.ErrorIsNil)
   151  	c.Assert(cons, jc.DeepEquals, map[string]state.StorageConstraints{
   152  		"data": {
   153  			Pool:  "default-block",
   154  			Size:  1024,
   155  			Count: 1,
   156  		},
   157  		"allecto": {
   158  			Pool:  "loop",
   159  			Size:  1024,
   160  			Count: 0,
   161  		},
   162  	})
   163  }
   164  
   165  func (s *VolumeStateSuite) TestSetVolumeInfo(c *gc.C) {
   166  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   167  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  
   170  	volume := s.storageInstanceVolume(c, storageTag)
   171  	volumeTag := volume.VolumeTag()
   172  	s.assertVolumeUnprovisioned(c, volumeTag)
   173  
   174  	volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-ume"}
   175  	err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet)
   176  	c.Assert(err, jc.ErrorIsNil)
   177  	volumeInfoSet.Pool = "loop-pool" // taken from params
   178  	s.assertVolumeInfo(c, volumeTag, volumeInfoSet)
   179  }
   180  
   181  func (s *VolumeStateSuite) TestSetVolumeInfoNoVolumeId(c *gc.C) {
   182  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   183  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   184  	c.Assert(err, jc.ErrorIsNil)
   185  
   186  	volume := s.storageInstanceVolume(c, storageTag)
   187  	volumeTag := volume.VolumeTag()
   188  	s.assertVolumeUnprovisioned(c, volumeTag)
   189  
   190  	volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true}
   191  	err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet)
   192  	c.Assert(err, gc.ErrorMatches, `cannot set info for volume "0/0": volume ID not set`)
   193  }
   194  
   195  func (s *VolumeStateSuite) TestSetVolumeInfoNoStorageAssigned(c *gc.C) {
   196  	oneJob := []state.MachineJob{state.JobHostUnits}
   197  	cons := constraints.MustParse("mem=4G")
   198  	hc := instance.MustParseHardware("mem=2G")
   199  
   200  	volumeParams := state.VolumeParams{
   201  		Pool: "loop-pool",
   202  		Size: 123,
   203  	}
   204  	machineTemplate := state.MachineTemplate{
   205  		Base:                    state.UbuntuBase("12.10"),
   206  		Constraints:             cons,
   207  		HardwareCharacteristics: hc,
   208  		InstanceId:              "inst-id",
   209  		Nonce:                   "nonce",
   210  		Jobs:                    oneJob,
   211  		Volumes: []state.HostVolumeParams{{
   212  			Volume: volumeParams,
   213  		}},
   214  	}
   215  	machines, err := s.State.AddMachines(machineTemplate)
   216  	c.Assert(err, jc.ErrorIsNil)
   217  	c.Assert(machines, gc.HasLen, 1)
   218  	m, err := s.State.Machine(machines[0].Id())
   219  	c.Assert(err, jc.ErrorIsNil)
   220  
   221  	volumeAttachments, err := s.storageBackend.MachineVolumeAttachments(m.MachineTag())
   222  	c.Assert(err, jc.ErrorIsNil)
   223  	c.Assert(volumeAttachments, gc.HasLen, 1)
   224  	volumeTag := volumeAttachments[0].Volume()
   225  
   226  	volume := s.volume(c, volumeTag)
   227  	_, err = volume.StorageInstance()
   228  	c.Assert(err, jc.Satisfies, errors.IsNotAssigned)
   229  
   230  	s.assertVolumeUnprovisioned(c, volumeTag)
   231  	volumeInfoSet := state.VolumeInfo{Size: 123, VolumeId: "vol-ume"}
   232  	err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet)
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	volumeInfoSet.Pool = "loop-pool" // taken from params
   235  	s.assertVolumeInfo(c, volumeTag, volumeInfoSet)
   236  }
   237  
   238  func (s *VolumeStateSuite) TestSetVolumeInfoImmutable(c *gc.C) {
   239  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   240  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   241  	c.Assert(err, jc.ErrorIsNil)
   242  	volume := s.storageInstanceVolume(c, storageTag)
   243  	volumeTag := volume.VolumeTag()
   244  
   245  	volumeInfoSet := state.VolumeInfo{Size: 123, VolumeId: "vol-ume"}
   246  	err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet)
   247  	c.Assert(err, jc.ErrorIsNil)
   248  
   249  	// The first call to SetVolumeInfo takes the pool name from
   250  	// the params; the second does not, but it must not change
   251  	// either. Callers are expected to get the existing info and
   252  	// update it, leaving immutable values intact.
   253  	err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet)
   254  	c.Assert(err, gc.ErrorMatches, `cannot set info for volume "0/0": cannot change pool from "loop-pool" to ""`)
   255  
   256  	volumeInfoSet.Pool = "loop-pool"
   257  	s.assertVolumeInfo(c, volumeTag, volumeInfoSet)
   258  }
   259  
   260  func (s *VolumeStateSuite) TestWatchVolumeAttachment(c *gc.C) {
   261  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   262  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   263  	c.Assert(err, jc.ErrorIsNil)
   264  	assignedMachineId, err := u.AssignedMachineId()
   265  	c.Assert(err, jc.ErrorIsNil)
   266  	machineTag := names.NewMachineTag(assignedMachineId)
   267  
   268  	volume := s.storageInstanceVolume(c, storageTag)
   269  	volumeTag := volume.VolumeTag()
   270  	// Ensure that all the creation events have flowed through the system.
   271  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   272  
   273  	w := s.storageBackend.WatchVolumeAttachment(machineTag, volumeTag)
   274  	defer testing.AssertStop(c, w)
   275  	wc := testing.NewNotifyWatcherC(c, w)
   276  	wc.AssertOneChange()
   277  
   278  	machine, err := s.State.Machine(assignedMachineId)
   279  	c.Assert(err, jc.ErrorIsNil)
   280  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   281  	c.Assert(err, jc.ErrorIsNil)
   282  
   283  	// volume attachment will NOT react to volume changes
   284  	err = s.storageBackend.SetVolumeInfo(volumeTag, state.VolumeInfo{VolumeId: "vol-123"})
   285  	c.Assert(err, jc.ErrorIsNil)
   286  	wc.AssertNoChange()
   287  
   288  	err = s.storageBackend.SetVolumeAttachmentInfo(
   289  		machineTag, volumeTag, state.VolumeAttachmentInfo{
   290  			DeviceName: "xvdf1",
   291  		},
   292  	)
   293  	c.Assert(err, jc.ErrorIsNil)
   294  	wc.AssertOneChange()
   295  }
   296  
   297  func (s *VolumeStateSuite) TestWatchModelVolumes(c *gc.C) {
   298  	app := s.setupMixedScopeStorageApplication(c, "block")
   299  	addUnit := func() {
   300  		u, err := app.AddUnit(state.AddUnitParams{})
   301  		c.Assert(err, jc.ErrorIsNil)
   302  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   303  		c.Assert(err, jc.ErrorIsNil)
   304  	}
   305  	addUnit()
   306  
   307  	w := s.storageBackend.WatchModelVolumes()
   308  	defer testing.AssertStop(c, w)
   309  	wc := testing.NewStringsWatcherC(c, w)
   310  	wc.AssertChange("0", "1") // initial
   311  	wc.AssertNoChange()
   312  
   313  	addUnit()
   314  	wc.AssertChange("4", "5")
   315  	wc.AssertNoChange()
   316  
   317  	volume, err := s.storageBackend.Volume(names.NewVolumeTag("0"))
   318  	c.Assert(err, jc.ErrorIsNil)
   319  	storageTag, err := volume.StorageInstance()
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	removeStorageInstance(c, s.storageBackend, storageTag)
   322  	err = s.storageBackend.DestroyVolume(names.NewVolumeTag("0"), false)
   323  	c.Assert(err, jc.ErrorIsNil)
   324  	wc.AssertChange("0") // dying
   325  	wc.AssertNoChange()
   326  
   327  	err = s.storageBackend.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0"), false)
   328  	c.Assert(err, jc.ErrorIsNil)
   329  	err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0"), false)
   330  	c.Assert(err, jc.ErrorIsNil)
   331  
   332  	err = s.storageBackend.RemoveVolume(names.NewVolumeTag("0"))
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	wc.AssertChange("0") // removed
   335  	wc.AssertNoChange()
   336  }
   337  
   338  func (s *VolumeStateSuite) TestWatchModelVolumeAttachments(c *gc.C) {
   339  	app := s.setupMixedScopeStorageApplication(c, "block")
   340  	addUnit := func() {
   341  		u, err := app.AddUnit(state.AddUnitParams{})
   342  		c.Assert(err, jc.ErrorIsNil)
   343  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   344  		c.Assert(err, jc.ErrorIsNil)
   345  	}
   346  	addUnit()
   347  
   348  	w := s.storageBackend.WatchModelVolumeAttachments()
   349  	defer testing.AssertStop(c, w)
   350  	wc := testing.NewStringsWatcherC(c, w)
   351  	wc.AssertChange("0:0", "0:1") // initial
   352  	wc.AssertNoChange()
   353  
   354  	addUnit()
   355  	wc.AssertChange("1:4", "1:5")
   356  	wc.AssertNoChange()
   357  
   358  	err := s.storageBackend.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0"), false)
   359  	c.Assert(err, jc.ErrorIsNil)
   360  	wc.AssertChange("0:0") // dying
   361  	wc.AssertNoChange()
   362  
   363  	err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0"), false)
   364  	c.Assert(err, jc.ErrorIsNil)
   365  	wc.AssertChange("0:0") // removed
   366  	wc.AssertNoChange()
   367  }
   368  
   369  func (s *VolumeStateSuite) TestWatchMachineVolumes(c *gc.C) {
   370  	app := s.setupMixedScopeStorageApplication(c, "block", "machinescoped", "modelscoped")
   371  	addUnit := func() {
   372  		u, err := app.AddUnit(state.AddUnitParams{})
   373  		c.Assert(err, jc.ErrorIsNil)
   374  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   375  		c.Assert(err, jc.ErrorIsNil)
   376  	}
   377  	addUnit()
   378  
   379  	w := s.storageBackend.WatchMachineVolumes(names.NewMachineTag("0"))
   380  	defer testing.AssertStop(c, w)
   381  	wc := testing.NewStringsWatcherC(c, w)
   382  	wc.AssertChange("0/0", "0/1") // initial
   383  	wc.AssertNoChange()
   384  
   385  	addUnit()
   386  	// no change, since we're only interested in the one machine.
   387  	wc.AssertNoChange()
   388  
   389  	volume, err := s.storageBackend.Volume(names.NewVolumeTag("0/0"))
   390  	c.Assert(err, jc.ErrorIsNil)
   391  	storageTag, err := volume.StorageInstance()
   392  	c.Assert(err, jc.ErrorIsNil)
   393  	removeStorageInstance(c, s.storageBackend, storageTag)
   394  	err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
   395  	c.Assert(err, jc.ErrorIsNil)
   396  	wc.AssertChange("0/0") // dying
   397  	wc.AssertNoChange()
   398  
   399  	err = s.storageBackend.DestroyVolume(names.NewVolumeTag("0/0"), false)
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0"), false)
   402  	c.Assert(err, jc.ErrorIsNil)
   403  	err = s.storageBackend.RemoveVolume(names.NewVolumeTag("0/0"))
   404  	c.Assert(err, jc.ErrorIsNil)
   405  	wc.AssertChange("0/0") // removed
   406  	wc.AssertNoChange()
   407  }
   408  
   409  func (s *VolumeStateSuite) TestWatchMachineVolumeAttachments(c *gc.C) {
   410  	app := s.setupMixedScopeStorageApplication(c, "block", "machinescoped", "modelscoped")
   411  	addUnit := func(to *state.Machine) (u *state.Unit, m *state.Machine) {
   412  		var err error
   413  		u, err = app.AddUnit(state.AddUnitParams{})
   414  		c.Assert(err, jc.ErrorIsNil)
   415  		if to != nil {
   416  			err = u.AssignToMachine(to)
   417  			c.Assert(err, jc.ErrorIsNil)
   418  			return u, to
   419  		}
   420  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   421  		c.Assert(err, jc.ErrorIsNil)
   422  		m = unitMachine(c, s.State, u)
   423  		return u, m
   424  	}
   425  	_, m0 := addUnit(nil)
   426  
   427  	w := s.storageBackend.WatchMachineVolumeAttachments(names.NewMachineTag("0"))
   428  	defer testing.AssertStop(c, w)
   429  	wc := testing.NewStringsWatcherC(c, w)
   430  	wc.AssertChange("0:0/0", "0:0/1") // initial
   431  	wc.AssertNoChange()
   432  
   433  	addUnit(nil)
   434  	// no change, since we're only interested in the one machine.
   435  	wc.AssertNoChange()
   436  
   437  	err := s.storageBackend.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("2"), false)
   438  	c.Assert(err, jc.ErrorIsNil)
   439  	// no change, since we're only interested in attachments of
   440  	// machine-scoped volumes.
   441  	wc.AssertNoChange()
   442  
   443  	removeVolumeStorageInstance(c, s.storageBackend, names.NewVolumeTag("0/0"))
   444  	err = s.storageBackend.DestroyVolume(names.NewVolumeTag("0/0"), false)
   445  	c.Assert(err, jc.ErrorIsNil)
   446  	wc.AssertChange("0:0/0") // dying
   447  	wc.AssertNoChange()
   448  
   449  	err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0"), false)
   450  	c.Assert(err, jc.ErrorIsNil)
   451  	wc.AssertChange("0:0/0") // removed
   452  	wc.AssertNoChange()
   453  
   454  	addUnit(m0)
   455  	wc.AssertChange("0:0/8", "0:0/9") // added
   456  }
   457  
   458  func (s *VolumeStateSuite) TestParseVolumeAttachmentId(c *gc.C) {
   459  	assertValid := func(id string, m names.Tag, v names.VolumeTag) {
   460  		hostTag, volumeTag, err := state.ParseVolumeAttachmentId(id)
   461  		c.Assert(err, jc.ErrorIsNil)
   462  		c.Assert(hostTag, gc.Equals, m)
   463  		c.Assert(volumeTag, gc.Equals, v)
   464  	}
   465  	assertValid("0:0", names.NewMachineTag("0"), names.NewVolumeTag("0"))
   466  	assertValid("0:0/1", names.NewMachineTag("0"), names.NewVolumeTag("0/1"))
   467  	assertValid("0/lxd/0:1", names.NewMachineTag("0/lxd/0"), names.NewVolumeTag("1"))
   468  	assertValid("some-unit/0:1", names.NewUnitTag("some-unit/0"), names.NewVolumeTag("1"))
   469  }
   470  
   471  func (s *VolumeStateSuite) TestParseVolumeAttachmentIdError(c *gc.C) {
   472  	assertError := func(id, expect string) {
   473  		_, _, err := state.ParseVolumeAttachmentId(id)
   474  		c.Assert(err, gc.ErrorMatches, expect)
   475  	}
   476  	assertError("", `invalid volume attachment ID ""`)
   477  	assertError("0", `invalid volume attachment ID "0"`)
   478  	assertError("0:foo", `invalid volume attachment ID "0:foo"`)
   479  	assertError("bar:0", `invalid volume attachment ID "bar:0"`)
   480  }
   481  
   482  func (s *VolumeStateSuite) TestAllVolumes(c *gc.C) {
   483  	_, expected, _ := s.assertCreateVolumes(c)
   484  
   485  	volumes, err := s.storageBackend.AllVolumes()
   486  	c.Assert(err, jc.ErrorIsNil)
   487  	tags := make([]names.VolumeTag, len(volumes))
   488  	for i, v := range volumes {
   489  		tags[i] = v.VolumeTag()
   490  	}
   491  	c.Assert(tags, jc.SameContents, expected)
   492  }
   493  
   494  func (s *VolumeStateSuite) assertCreateVolumes(c *gc.C) (_ *state.Machine, all, persistent []names.VolumeTag) {
   495  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
   496  		Base: state.UbuntuBase("12.10"),
   497  		Jobs: []state.MachineJob{state.JobHostUnits},
   498  		Volumes: []state.HostVolumeParams{{
   499  			Volume: state.VolumeParams{Pool: "persistent-block", Size: 1024},
   500  		}, {
   501  			Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048},
   502  		}, {
   503  			Volume: state.VolumeParams{Pool: "static", Size: 2048},
   504  		}},
   505  	})
   506  	c.Assert(err, jc.ErrorIsNil)
   507  	assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag())
   508  
   509  	volume1 := s.volume(c, names.NewVolumeTag("0"))
   510  	volume2 := s.volume(c, names.NewVolumeTag("0/1"))
   511  	volume3 := s.volume(c, names.NewVolumeTag("2"))
   512  
   513  	volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-1"}
   514  	err = s.storageBackend.SetVolumeInfo(volume1.VolumeTag(), volumeInfoSet)
   515  	c.Assert(err, jc.ErrorIsNil)
   516  
   517  	volumeInfoSet = state.VolumeInfo{Size: 456, Persistent: false, VolumeId: "vol-2"}
   518  	err = s.storageBackend.SetVolumeInfo(volume2.VolumeTag(), volumeInfoSet)
   519  	c.Assert(err, jc.ErrorIsNil)
   520  
   521  	all = []names.VolumeTag{
   522  		volume1.VolumeTag(),
   523  		volume2.VolumeTag(),
   524  		volume3.VolumeTag(),
   525  	}
   526  	persistent = []names.VolumeTag{
   527  		volume1.VolumeTag(),
   528  	}
   529  	return machine, all, persistent
   530  }
   531  
   532  func (s *VolumeStateSuite) TestRemoveStorageInstanceDestroysAndUnassignsVolume(c *gc.C) {
   533  	_, u, storageTag := s.setupSingleStorage(c, "block", "modelscoped")
   534  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   535  	c.Assert(err, jc.ErrorIsNil)
   536  	volume := s.storageInstanceVolume(c, storageTag)
   537  	c.Assert(err, jc.ErrorIsNil)
   538  
   539  	err = u.Destroy()
   540  	c.Assert(err, jc.ErrorIsNil)
   541  
   542  	// Provision volume attachment so that detaching the storage
   543  	// attachment does not short-circuit.
   544  	defer state.SetBeforeHooks(c, s.State, func() {
   545  		machine := unitMachine(c, s.State, u)
   546  		err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   547  		c.Assert(err, jc.ErrorIsNil)
   548  		err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), state.VolumeInfo{VolumeId: "vol-123"})
   549  		c.Assert(err, jc.ErrorIsNil)
   550  		err = s.storageBackend.SetVolumeAttachmentInfo(
   551  			machine.MachineTag(), volume.VolumeTag(),
   552  			state.VolumeAttachmentInfo{DeviceName: "xvdf1"},
   553  		)
   554  		c.Assert(err, jc.ErrorIsNil)
   555  	}).Check()
   556  
   557  	err = s.storageBackend.DestroyStorageInstance(storageTag, true, false, dontWait)
   558  	c.Assert(err, jc.ErrorIsNil)
   559  	err = s.storageBackend.DetachStorage(storageTag, u.UnitTag(), false, dontWait)
   560  	c.Assert(err, jc.ErrorIsNil)
   561  
   562  	// The storage instance and attachment are dying, but not yet
   563  	// removed from state. The volume should still be assigned.
   564  	s.storageInstanceVolume(c, storageTag)
   565  
   566  	err = s.storageBackend.RemoveStorageAttachment(storageTag, u.UnitTag(), false)
   567  	c.Assert(err, jc.ErrorIsNil)
   568  
   569  	// The storage instance is now gone; the volume should no longer
   570  	// be assigned to the storage.
   571  	_, err = s.storageBackend.StorageInstanceVolume(storageTag)
   572  	c.Assert(err, gc.ErrorMatches, `volume for storage instance "data/0" not found`)
   573  
   574  	// The volume should still exist, but it should be dying.
   575  	v := s.volume(c, volume.VolumeTag())
   576  	c.Assert(v.Life(), gc.Equals, state.Dying)
   577  }
   578  
   579  func (s *VolumeStateSuite) TestReleaseStorageInstanceVolumeReleasing(c *gc.C) {
   580  	_, u, storageTag := s.setupSingleStorage(c, "block", "modelscoped")
   581  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   582  	c.Assert(err, jc.ErrorIsNil)
   583  	volume := s.storageInstanceVolume(c, storageTag)
   584  	c.Assert(err, jc.ErrorIsNil)
   585  	c.Assert(volume.Releasing(), jc.IsFalse)
   586  	err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), state.VolumeInfo{VolumeId: "vol-123"})
   587  	c.Assert(err, jc.ErrorIsNil)
   588  
   589  	err = u.Destroy()
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	err = s.storageBackend.ReleaseStorageInstance(storageTag, true, false, dontWait)
   592  	c.Assert(err, jc.ErrorIsNil)
   593  	err = s.storageBackend.DetachStorage(storageTag, u.UnitTag(), false, dontWait)
   594  	c.Assert(err, jc.ErrorIsNil)
   595  
   596  	// The volume should should be dying, and releasing.
   597  	volume = s.volume(c, volume.VolumeTag())
   598  	c.Assert(volume.Life(), gc.Equals, state.Dying)
   599  	c.Assert(volume.Releasing(), jc.IsTrue)
   600  }
   601  
   602  func (s *VolumeStateSuite) TestReleaseStorageInstanceVolumeUnreleasable(c *gc.C) {
   603  	_, u, storageTag := s.setupSingleStorage(c, "block", "modelscoped-unreleasable")
   604  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   605  	c.Assert(err, jc.ErrorIsNil)
   606  	volume := s.storageInstanceVolume(c, storageTag)
   607  	c.Assert(err, jc.ErrorIsNil)
   608  	c.Assert(volume.Releasing(), jc.IsFalse)
   609  	err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), state.VolumeInfo{VolumeId: "vol-123"})
   610  	c.Assert(err, jc.ErrorIsNil)
   611  
   612  	err = u.Destroy()
   613  	c.Assert(err, jc.ErrorIsNil)
   614  	err = s.storageBackend.ReleaseStorageInstance(storageTag, true, false, dontWait)
   615  	c.Assert(err, gc.ErrorMatches,
   616  		`cannot release storage "data/0": storage provider "modelscoped-unreleasable" does not support releasing storage`,
   617  	)
   618  	err = s.storageBackend.DetachStorage(storageTag, u.UnitTag(), false, dontWait)
   619  	c.Assert(err, jc.ErrorIsNil)
   620  
   621  	// The volume should should still be alive.
   622  	volume = s.volume(c, volume.VolumeTag())
   623  	c.Assert(volume.Life(), gc.Equals, state.Alive)
   624  	c.Assert(volume.Releasing(), jc.IsFalse)
   625  }
   626  
   627  func (s *VolumeStateSuite) TestSetVolumeAttachmentInfoVolumeNotProvisioned(c *gc.C) {
   628  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   629  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   630  	c.Assert(err, jc.ErrorIsNil)
   631  	assignedMachineId, err := u.AssignedMachineId()
   632  	c.Assert(err, jc.ErrorIsNil)
   633  	machineTag := names.NewMachineTag(assignedMachineId)
   634  
   635  	volume := s.storageInstanceVolume(c, storageTag)
   636  	volumeTag := volume.VolumeTag()
   637  
   638  	err = s.storageBackend.SetVolumeAttachmentInfo(
   639  		machineTag, volumeTag, state.VolumeAttachmentInfo{
   640  			DeviceName: "xvdf1",
   641  		},
   642  	)
   643  	c.Assert(err, gc.ErrorMatches, `cannot set info for volume attachment 0/0:0: volume "0/0" not provisioned`)
   644  }
   645  
   646  func (s *VolumeStateSuite) TestDestroyVolume(c *gc.C) {
   647  	volume, _ := s.setupMachineScopedVolumeAttachment(c)
   648  	assertDestroy := func() {
   649  		err := s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
   650  		c.Assert(err, jc.ErrorIsNil)
   651  		volume = s.volume(c, volume.VolumeTag())
   652  		c.Assert(volume.Life(), gc.Equals, state.Dying)
   653  	}
   654  	defer state.SetBeforeHooks(c, s.State, assertDestroy).Check()
   655  	assertDestroy()
   656  }
   657  
   658  func (s *VolumeStateSuite) TestDestroyVolumeNotFound(c *gc.C) {
   659  	err := s.storageBackend.DestroyVolume(names.NewVolumeTag("0"), false)
   660  	c.Assert(err, gc.ErrorMatches, `destroying volume 0: volume "0" not found`)
   661  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   662  }
   663  
   664  func (s *VolumeStateSuite) TestDestroyVolumeStorageAssigned(c *gc.C) {
   665  	volume, _, u := s.setupStorageVolumeAttachment(c)
   666  	storageTag, err := volume.StorageInstance()
   667  	c.Assert(err, jc.ErrorIsNil)
   668  
   669  	err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
   670  	c.Assert(err, gc.ErrorMatches, "destroying volume 0: volume is assigned to storage data/0")
   671  
   672  	err = u.Destroy()
   673  	c.Assert(err, jc.ErrorIsNil)
   674  	removeStorageInstance(c, s.storageBackend, storageTag)
   675  	err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
   676  	c.Assert(err, jc.ErrorIsNil)
   677  }
   678  
   679  func (s *VolumeStateSuite) TestDestroyVolumeNoAttachments(c *gc.C) {
   680  	volume, machine := s.setupModelScopedVolumeAttachment(c)
   681  	err := s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), false)
   682  	c.Assert(err, jc.ErrorIsNil)
   683  
   684  	defer state.SetBeforeHooks(c, s.State, func() {
   685  		err := s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false)
   686  		c.Assert(err, jc.ErrorIsNil)
   687  	}).Check()
   688  
   689  	err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
   690  	c.Assert(err, jc.ErrorIsNil)
   691  	volume = s.volume(c, volume.VolumeTag())
   692  
   693  	// There are no more attachments, so the volume should
   694  	// have been progressed directly to Dead.
   695  	c.Assert(volume.Life(), gc.Equals, state.Dead)
   696  }
   697  
   698  func (s *VolumeStateSuite) TestDetachVolumeDyingAttachment(c *gc.C) {
   699  	volume, machine := s.setupModelScopedVolumeAttachment(c)
   700  	volumeTag := volume.VolumeTag()
   701  	machineTag := machine.MachineTag()
   702  	// Make sure the state is already dying by the time we call DetachVolume
   703  	defer state.SetBeforeHooks(c, s.State, func() {
   704  		err := s.storageBackend.DetachVolume(machineTag, volumeTag, false)
   705  		c.Assert(err, jc.ErrorIsNil)
   706  	}).Check()
   707  
   708  	err := s.storageBackend.DetachVolume(machineTag, volumeTag, false)
   709  	c.Assert(err, jc.ErrorIsNil)
   710  
   711  	volumeAttachment := s.volumeAttachment(c, machineTag, volumeTag)
   712  	c.Assert(volumeAttachment.Life(), gc.Equals, state.Dying)
   713  	volume = s.volume(c, volumeTag)
   714  	c.Assert(volume.Life(), gc.Equals, state.Alive)
   715  	err = s.storageBackend.DestroyVolume(volumeTag, false)
   716  	c.Assert(err, jc.ErrorIsNil)
   717  }
   718  
   719  func (s *VolumeStateSuite) TestDetachVolumeDyingAttachmentPlan(c *gc.C) {
   720  	volume, machine := s.setupModelScopedVolumeAttachment(c)
   721  	machineTag := machine.MachineTag()
   722  	volumeTag := volume.VolumeTag()
   723  	err := machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   724  	c.Assert(err, jc.ErrorIsNil)
   725  	err = s.storageBackend.SetVolumeInfo(volumeTag,
   726  		state.VolumeInfo{
   727  			Size:     1024,
   728  			VolumeId: "vol-ume",
   729  		})
   730  	c.Assert(err, jc.ErrorIsNil)
   731  	err = s.storageBackend.SetVolumeAttachmentInfo(machineTag, volumeTag,
   732  		state.VolumeAttachmentInfo{
   733  			DeviceName: "bogus",
   734  		})
   735  	c.Assert(err, jc.ErrorIsNil)
   736  	// Simulate a machine agent recording its intent to attach the volume
   737  	err = s.storageBackend.CreateVolumeAttachmentPlan(machineTag, volumeTag,
   738  		state.VolumeAttachmentPlanInfo{DeviceType: storage.DeviceTypeLocal})
   739  	c.Assert(err, jc.ErrorIsNil)
   740  
   741  	defer state.SetBeforeHooks(c, s.State, func() {
   742  		err := s.storageBackend.DetachVolume(machineTag, volumeTag, false)
   743  		c.Assert(err, jc.ErrorIsNil)
   744  	}).Check()
   745  
   746  	err = s.storageBackend.DetachVolume(machineTag, volumeTag, false)
   747  	c.Assert(err, jc.ErrorIsNil)
   748  	// The volume attachment shouldn't progress to dying, but its volume attachment plan should have
   749  	volumeAttachment := s.volumeAttachment(c, machineTag, volumeTag)
   750  	c.Assert(volumeAttachment.Life(), gc.Equals, state.Alive)
   751  	volumeAttachmentPlan := s.volumeAttachmentPlan(c, machineTag, volumeTag)
   752  	c.Assert(volumeAttachmentPlan.Life(), gc.Equals, state.Dying)
   753  	volume = s.volume(c, volumeTag)
   754  	// now calling RemoveAttachmentPlan removes the plan and moves the VolumeAttachment to dying
   755  	err = s.storageBackend.RemoveVolumeAttachmentPlan(machineTag, volumeTag, false)
   756  	c.Assert(err, jc.ErrorIsNil)
   757  	_, err = s.storageBackend.VolumeAttachmentPlan(machineTag, volumeTag)
   758  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   759  	volumeAttachment = s.volumeAttachment(c, machineTag, volumeTag)
   760  	c.Assert(volumeAttachment.Life(), gc.Equals, state.Dying)
   761  }
   762  
   763  func (s *VolumeStateSuite) TestRemoveVolume(c *gc.C) {
   764  	volume, machine := s.setupMachineScopedVolumeAttachment(c)
   765  
   766  	err := s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
   767  	c.Assert(err, jc.ErrorIsNil)
   768  	err = s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false)
   769  	c.Assert(err, jc.ErrorIsNil)
   770  	assertRemove := func() {
   771  		err = s.storageBackend.RemoveVolume(volume.VolumeTag())
   772  		c.Assert(err, jc.ErrorIsNil)
   773  		_, err = s.storageBackend.Volume(volume.VolumeTag())
   774  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
   775  	}
   776  	defer state.SetBeforeHooks(c, s.State, assertRemove).Check()
   777  	assertRemove()
   778  }
   779  
   780  func (s *VolumeStateSuite) TestRemoveVolumeNotFound(c *gc.C) {
   781  	err := s.storageBackend.RemoveVolume(names.NewVolumeTag("42"))
   782  	c.Assert(err, jc.ErrorIsNil)
   783  }
   784  
   785  func (s *VolumeStateSuite) TestRemoveVolumeNotDead(c *gc.C) {
   786  	volume, _ := s.setupMachineScopedVolumeAttachment(c)
   787  	err := s.storageBackend.RemoveVolume(volume.VolumeTag())
   788  	c.Assert(err, gc.ErrorMatches, "removing volume 0/0: volume is not dead")
   789  	err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
   790  	c.Assert(err, jc.ErrorIsNil)
   791  	err = s.storageBackend.RemoveVolume(volume.VolumeTag())
   792  	c.Assert(err, gc.ErrorMatches, "removing volume 0/0: volume is not dead")
   793  }
   794  
   795  func (s *VolumeStateSuite) TestDetachVolume(c *gc.C) {
   796  	volume, machine := s.setupModelScopedVolumeAttachment(c)
   797  	assertDetach := func() {
   798  		err := s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), false)
   799  		c.Assert(err, jc.ErrorIsNil)
   800  		attachment := s.volumeAttachment(c, machine.MachineTag(), volume.VolumeTag())
   801  		c.Assert(attachment.Life(), gc.Equals, state.Dying)
   802  	}
   803  	defer state.SetBeforeHooks(c, s.State, assertDetach).Check()
   804  	assertDetach()
   805  }
   806  
   807  func (s *VolumeStateSuite) TestDetachVolumeForce(c *gc.C) {
   808  	volume, machine := s.setupModelScopedVolumeAttachment(c)
   809  	coll, closer := state.GetCollection(s.st, "volumes")
   810  	defer closer()
   811  
   812  	// Set the volume to Dying even though the attachment is still alive.
   813  	err := coll.Writeable().UpdateId(
   814  		state.DocID(s.st, volume.VolumeTag().Id()),
   815  		bson.D{{"$set", bson.D{{"life", state.Dying}}}})
   816  	c.Assert(err, jc.ErrorIsNil)
   817  	volume, err = s.storageBackend.Volume(volume.VolumeTag())
   818  	c.Assert(err, jc.ErrorIsNil)
   819  	c.Assert(volume.Life(), gc.Equals, state.Dying)
   820  
   821  	assertDetach := func() {
   822  		err := s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), true)
   823  		c.Assert(err, jc.ErrorIsNil)
   824  		attachment := s.volumeAttachment(c, machine.MachineTag(), volume.VolumeTag())
   825  		c.Assert(attachment.Life(), gc.Equals, state.Dying)
   826  	}
   827  	defer state.SetBeforeHooks(c, s.State, assertDetach).Check()
   828  	assertDetach()
   829  }
   830  
   831  func (s *VolumeStateSuite) TestRemoveLastVolumeAttachment(c *gc.C) {
   832  	volume, machine := s.setupModelScopedVolumeAttachment(c)
   833  
   834  	err := s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), false)
   835  	c.Assert(err, jc.ErrorIsNil)
   836  
   837  	err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
   838  	c.Assert(err, jc.ErrorIsNil)
   839  	volume = s.volume(c, volume.VolumeTag())
   840  	c.Assert(volume.Life(), gc.Equals, state.Dying)
   841  
   842  	err = s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false)
   843  	c.Assert(err, jc.ErrorIsNil)
   844  
   845  	// The volume was Dying when the last attachment was
   846  	// removed, so the volume should now be Dead.
   847  	volume = s.volume(c, volume.VolumeTag())
   848  	c.Assert(volume.Life(), gc.Equals, state.Dead)
   849  }
   850  
   851  func (s *VolumeStateSuite) TestRemoveLastVolumeAttachmentConcurrently(c *gc.C) {
   852  	volume, machine := s.setupModelScopedVolumeAttachment(c)
   853  
   854  	err := s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), false)
   855  	c.Assert(err, jc.ErrorIsNil)
   856  
   857  	defer state.SetBeforeHooks(c, s.State, func() {
   858  		err := s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
   859  		c.Assert(err, jc.ErrorIsNil)
   860  		volume := s.volume(c, volume.VolumeTag())
   861  		c.Assert(volume.Life(), gc.Equals, state.Dying)
   862  	}).Check()
   863  
   864  	err = s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false)
   865  	c.Assert(err, jc.ErrorIsNil)
   866  
   867  	// Last attachment was removed, and the volume was (concurrently)
   868  	// destroyed, so the volume should be Dead.
   869  	volume = s.volume(c, volume.VolumeTag())
   870  	c.Assert(volume.Life(), gc.Equals, state.Dead)
   871  }
   872  
   873  func (s *VolumeStateSuite) TestRemoveVolumeAttachmentNotFound(c *gc.C) {
   874  	err := s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("42"), names.NewVolumeTag("42"), false)
   875  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   876  	c.Assert(err, gc.ErrorMatches, `removing attachment of volume 42 from machine 42: volume "42" on "machine 42" not found`)
   877  }
   878  
   879  func (s *VolumeStateSuite) TestRemoveVolumeAttachmentConcurrently(c *gc.C) {
   880  	volume, machine := s.setupMachineScopedVolumeAttachment(c)
   881  
   882  	err := s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
   883  	c.Assert(err, jc.ErrorIsNil)
   884  	remove := func() {
   885  		err := s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false)
   886  		c.Assert(err, jc.ErrorIsNil)
   887  		assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag())
   888  	}
   889  	defer state.SetBeforeHooks(c, s.State, remove).Check()
   890  	remove()
   891  }
   892  
   893  func (s *VolumeStateSuite) TestRemoveVolumeAttachmentAlive(c *gc.C) {
   894  	volume, machine := s.setupMachineScopedVolumeAttachment(c)
   895  
   896  	err := s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false)
   897  	c.Assert(err, gc.ErrorMatches, "removing attachment of volume 0/0 from machine 0: volume attachment is not dying")
   898  }
   899  
   900  func (s *VolumeStateSuite) TestRemoveMachineRemovesVolumes(c *gc.C) {
   901  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
   902  		Base: state.UbuntuBase("12.10"),
   903  		Jobs: []state.MachineJob{state.JobHostUnits},
   904  		Volumes: []state.HostVolumeParams{{
   905  			Volume: state.VolumeParams{Pool: "persistent-block", Size: 1024}, // unprovisioned
   906  		}, {
   907  			Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // provisioned
   908  		}, {
   909  			Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // unprovisioned
   910  		}, {
   911  			Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // provisioned, non-persistent
   912  		}, {
   913  			Volume: state.VolumeParams{Pool: "static", Size: 2048}, // provisioned
   914  		}, {
   915  			Volume: state.VolumeParams{Pool: "static", Size: 2048}, // unprovisioned
   916  		}},
   917  	})
   918  	c.Assert(err, jc.ErrorIsNil)
   919  
   920  	volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-1"}
   921  	err = s.storageBackend.SetVolumeInfo(names.NewVolumeTag("0/1"), volumeInfoSet)
   922  	c.Assert(err, jc.ErrorIsNil)
   923  	volumeInfoSet = state.VolumeInfo{Size: 456, Persistent: false, VolumeId: "vol-2"}
   924  	err = s.storageBackend.SetVolumeInfo(names.NewVolumeTag("0/3"), volumeInfoSet)
   925  	c.Assert(err, jc.ErrorIsNil)
   926  	volumeInfoSet = state.VolumeInfo{Size: 789, Persistent: false, VolumeId: "vol-3"}
   927  	err = s.storageBackend.SetVolumeInfo(names.NewVolumeTag("4"), volumeInfoSet)
   928  	c.Assert(err, jc.ErrorIsNil)
   929  
   930  	allVolumes, err := s.storageBackend.AllVolumes()
   931  	c.Assert(err, jc.ErrorIsNil)
   932  
   933  	persistentVolumes := make([]state.Volume, 0, len(allVolumes))
   934  	for _, v := range allVolumes {
   935  		info, err := v.Info()
   936  		if err == nil && info.Persistent {
   937  			persistentVolumes = append(persistentVolumes, v)
   938  		}
   939  	}
   940  	c.Assert(len(allVolumes), jc.GreaterThan, len(persistentVolumes))
   941  
   942  	c.Assert(machine.Destroy(), jc.ErrorIsNil)
   943  
   944  	// Cannot advance to Dead while there are detachable dynamic volumes.
   945  	err = machine.EnsureDead()
   946  	c.Assert(errors.Is(err, stateerrors.HasAttachmentsError), jc.IsTrue)
   947  	c.Assert(err, gc.ErrorMatches, "machine 0 has attachments \\[volume-0\\]")
   948  	s.obliterateVolumeAttachment(c, machine.MachineTag(), names.NewVolumeTag("0"))
   949  	c.Assert(machine.EnsureDead(), jc.ErrorIsNil)
   950  	c.Assert(machine.Remove(), jc.ErrorIsNil)
   951  
   952  	// Machine is gone: non-detachable storage should be done too.
   953  	allVolumes, err = s.storageBackend.AllVolumes()
   954  	c.Assert(err, jc.ErrorIsNil)
   955  	// We should only have the persistent volume remaining.
   956  	c.Assert(allVolumes, gc.HasLen, 1)
   957  	c.Assert(allVolumes[0].Tag().String(), gc.Equals, "volume-0")
   958  
   959  	attachments, err := s.storageBackend.MachineVolumeAttachments(machine.MachineTag())
   960  	c.Assert(err, jc.ErrorIsNil)
   961  	c.Assert(attachments, gc.HasLen, 0)
   962  }
   963  
   964  func (s *VolumeStateSuite) TestEnsureMachineDeadAddVolumeConcurrently(c *gc.C) {
   965  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
   966  		Base: state.UbuntuBase("12.10"),
   967  		Jobs: []state.MachineJob{state.JobHostUnits},
   968  		Volumes: []state.HostVolumeParams{{
   969  			Volume: state.VolumeParams{Pool: "static", Size: 1024},
   970  		}},
   971  	})
   972  	c.Assert(err, jc.ErrorIsNil)
   973  
   974  	addVolume := func() {
   975  		_, u, _ := s.setupSingleStorage(c, "block", "modelscoped")
   976  		err := u.AssignToMachine(machine)
   977  		c.Assert(err, jc.ErrorIsNil)
   978  		s.obliterateUnit(c, u.UnitTag())
   979  	}
   980  	defer state.SetBeforeHooks(c, s.State, addVolume).Check()
   981  
   982  	// The static volume the machine was provisioned with does not matter,
   983  	// but the volume added concurrently does.
   984  	err = machine.EnsureDead()
   985  	c.Assert(err, gc.ErrorMatches, `machine 0 has attachments \[volume-1\]`)
   986  }
   987  
   988  func (s *VolumeStateSuite) TestEnsureMachineDeadRemoveVolumeConcurrently(c *gc.C) {
   989  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
   990  		Base: state.UbuntuBase("12.10"),
   991  		Jobs: []state.MachineJob{state.JobHostUnits},
   992  		Volumes: []state.HostVolumeParams{{
   993  			Volume: state.VolumeParams{Pool: "static", Size: 1024},
   994  		}},
   995  	})
   996  	c.Assert(err, jc.ErrorIsNil)
   997  
   998  	removeVolume := func() {
   999  		s.obliterateVolume(c, names.NewVolumeTag("0"))
  1000  	}
  1001  	defer state.SetBeforeHooks(c, s.State, removeVolume).Check()
  1002  
  1003  	// Removing a volume concurrently does not cause a transaction failure.
  1004  	err = machine.EnsureDead()
  1005  	c.Assert(err, jc.ErrorIsNil)
  1006  }
  1007  
  1008  func (s *VolumeStateSuite) TestVolumeMachineScoped(c *gc.C) {
  1009  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
  1010  		Base: state.UbuntuBase("12.10"),
  1011  		Jobs: []state.MachineJob{state.JobHostUnits},
  1012  		Volumes: []state.HostVolumeParams{{
  1013  			Volume: state.VolumeParams{Pool: "loop", Size: 1024},
  1014  		}},
  1015  	})
  1016  	c.Assert(err, jc.ErrorIsNil)
  1017  
  1018  	volume := s.volume(c, names.NewVolumeTag("0/0"))
  1019  	c.Assert(volume.Life(), gc.Equals, state.Alive)
  1020  
  1021  	err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false)
  1022  	c.Assert(err, jc.ErrorIsNil)
  1023  	err = s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false)
  1024  	c.Assert(err, jc.ErrorIsNil)
  1025  	volume = s.volume(c, volume.VolumeTag())
  1026  	c.Assert(volume.Life(), gc.Equals, state.Dead)
  1027  
  1028  	// Remove the machine: this should remove the volume.
  1029  	err = machine.Destroy()
  1030  	c.Assert(err, jc.ErrorIsNil)
  1031  	err = machine.EnsureDead()
  1032  	c.Assert(err, jc.ErrorIsNil)
  1033  	err = machine.Remove()
  1034  	c.Assert(err, jc.ErrorIsNil)
  1035  	volume, err = s.storageBackend.Volume(volume.VolumeTag())
  1036  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1037  }
  1038  
  1039  func (s *VolumeStateSuite) TestVolumeBindingStorage(c *gc.C) {
  1040  	// Volumes created assigned to a storage instance are bound
  1041  	// to the machine/model, and not the storage. i.e. storage
  1042  	// is persistent by default.
  1043  	volume, _, u := s.setupStorageVolumeAttachment(c)
  1044  	storageTag, err := volume.StorageInstance()
  1045  	c.Assert(err, jc.ErrorIsNil)
  1046  
  1047  	// The volume should transition to Dying when the storage is removed.
  1048  	// We must destroy the unit before we can remove the storage.
  1049  	err = u.Destroy()
  1050  	c.Assert(err, jc.ErrorIsNil)
  1051  	removeStorageInstance(c, s.storageBackend, storageTag)
  1052  	volume = s.volume(c, volume.VolumeTag())
  1053  	c.Assert(volume.Life(), gc.Equals, state.Dying)
  1054  }
  1055  
  1056  func (s *VolumeStateSuite) setupStorageVolumeAttachment(c *gc.C) (state.Volume, *state.Machine, *state.Unit) {
  1057  	_, u, storageTag := s.setupSingleStorage(c, "block", "modelscoped")
  1058  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
  1059  	c.Assert(err, jc.ErrorIsNil)
  1060  	machine := unitMachine(c, s.State, u)
  1061  	return s.storageInstanceVolume(c, storageTag), machine, u
  1062  }
  1063  
  1064  func (s *VolumeStateSuite) setupModelScopedVolumeAttachment(c *gc.C) (state.Volume, *state.Machine) {
  1065  	return s.setupVolumeAttachment(c, "modelscoped")
  1066  }
  1067  
  1068  func (s *VolumeStateSuite) setupMachineScopedVolumeAttachment(c *gc.C) (state.Volume, *state.Machine) {
  1069  	return s.setupVolumeAttachment(c, "loop")
  1070  }
  1071  
  1072  func (s *VolumeStateSuite) setupVolumeAttachment(c *gc.C, pool string) (state.Volume, *state.Machine) {
  1073  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
  1074  		Base: state.UbuntuBase("12.10"),
  1075  		Jobs: []state.MachineJob{state.JobHostUnits},
  1076  		Volumes: []state.HostVolumeParams{{
  1077  			Volume: state.VolumeParams{Pool: pool, Size: 1024},
  1078  		}},
  1079  	})
  1080  	c.Assert(err, jc.ErrorIsNil)
  1081  	volumeAttachments, err := s.storageBackend.MachineVolumeAttachments(machine.MachineTag())
  1082  	c.Assert(err, jc.ErrorIsNil)
  1083  	c.Assert(volumeAttachments, gc.HasLen, 1)
  1084  	volume, err := s.storageBackend.Volume(volumeAttachments[0].Volume())
  1085  	c.Assert(err, jc.ErrorIsNil)
  1086  	return volume, machine
  1087  }
  1088  
  1089  func removeVolumeStorageInstance(c *gc.C, sb *state.StorageBackend, volumeTag names.VolumeTag) {
  1090  	volume, err := sb.Volume(volumeTag)
  1091  	c.Assert(err, jc.ErrorIsNil)
  1092  	storageTag, err := volume.StorageInstance()
  1093  	c.Assert(err, jc.ErrorIsNil)
  1094  	removeStorageInstance(c, sb, storageTag)
  1095  }
  1096  
  1097  func removeStorageInstance(c *gc.C, sb *state.StorageBackend, storageTag names.StorageTag) {
  1098  	err := sb.DestroyStorageInstance(storageTag, true, false, dontWait)
  1099  	c.Assert(err, jc.ErrorIsNil)
  1100  	attachments, err := sb.StorageAttachments(storageTag)
  1101  	c.Assert(err, jc.ErrorIsNil)
  1102  	for _, a := range attachments {
  1103  		err = sb.DetachStorage(storageTag, a.Unit(), false, dontWait)
  1104  		c.Assert(err, jc.ErrorIsNil)
  1105  	}
  1106  	_, err = sb.StorageInstance(storageTag)
  1107  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1108  }