github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/names"
     9  	jc "github.com/juju/testing/checkers"
    10  	"github.com/juju/utils/set"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/constraints"
    14  	"github.com/juju/juju/instance"
    15  	"github.com/juju/juju/state"
    16  	"github.com/juju/juju/state/testing"
    17  	"github.com/juju/juju/storage/poolmanager"
    18  	"github.com/juju/juju/storage/provider"
    19  )
    20  
    21  type VolumeStateSuite struct {
    22  	StorageStateSuiteBase
    23  }
    24  
    25  var _ = gc.Suite(&VolumeStateSuite{})
    26  
    27  func (s *VolumeStateSuite) TestAddMachine(c *gc.C) {
    28  	_, unit, _ := s.setupSingleStorage(c, "block", "loop-pool")
    29  	err := s.State.AssignUnit(unit, state.AssignCleanEmpty)
    30  	c.Assert(err, jc.ErrorIsNil)
    31  	s.assertMachineVolume(c, unit)
    32  }
    33  
    34  func (s *VolumeStateSuite) TestAssignToMachine(c *gc.C) {
    35  	_, unit, _ := s.setupSingleStorage(c, "block", "loop-pool")
    36  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
    37  	c.Assert(err, jc.ErrorIsNil)
    38  	err = unit.AssignToMachine(machine)
    39  	c.Assert(err, jc.ErrorIsNil)
    40  	s.assertMachineVolume(c, unit)
    41  }
    42  
    43  func (s *VolumeStateSuite) assertMachineVolume(c *gc.C, unit *state.Unit) {
    44  	assignedMachineId, err := unit.AssignedMachineId()
    45  	c.Assert(err, jc.ErrorIsNil)
    46  
    47  	storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag())
    48  	c.Assert(err, jc.ErrorIsNil)
    49  	c.Assert(storageAttachments, gc.HasLen, 1)
    50  	storageInstance, err := s.State.StorageInstance(storageAttachments[0].StorageInstance())
    51  	c.Assert(err, jc.ErrorIsNil)
    52  
    53  	volume := s.storageInstanceVolume(c, storageInstance.StorageTag())
    54  	c.Assert(volume.VolumeTag(), gc.Equals, names.NewVolumeTag("0/0"))
    55  	volumeStorageTag, err := volume.StorageInstance()
    56  	c.Assert(err, jc.ErrorIsNil)
    57  	c.Assert(volumeStorageTag, gc.Equals, storageInstance.StorageTag())
    58  	_, err = volume.Info()
    59  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
    60  	_, ok := volume.Params()
    61  	c.Assert(ok, jc.IsTrue)
    62  
    63  	machine, err := s.State.Machine(assignedMachineId)
    64  	c.Assert(err, jc.ErrorIsNil)
    65  	volumeAttachments, err := s.State.MachineVolumeAttachments(machine.MachineTag())
    66  	c.Assert(err, jc.ErrorIsNil)
    67  	c.Assert(volumeAttachments, gc.HasLen, 1)
    68  	c.Assert(volumeAttachments[0].Volume(), gc.Equals, volume.VolumeTag())
    69  	c.Assert(volumeAttachments[0].Machine(), gc.Equals, machine.MachineTag())
    70  	_, err = volumeAttachments[0].Info()
    71  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
    72  	_, ok = volumeAttachments[0].Params()
    73  	c.Assert(ok, jc.IsTrue)
    74  
    75  	_, err = s.State.VolumeAttachment(machine.MachineTag(), volume.VolumeTag())
    76  	c.Assert(err, jc.ErrorIsNil)
    77  
    78  	assertMachineStorageRefs(c, s.State, machine.MachineTag())
    79  }
    80  
    81  func (s *VolumeStateSuite) TestAddServiceInvalidPool(c *gc.C) {
    82  	ch := s.AddTestingCharm(c, "storage-block")
    83  	storage := map[string]state.StorageConstraints{
    84  		"data": makeStorageCons("invalid-pool", 1024, 1),
    85  	}
    86  	_, err := s.State.AddService(state.AddServiceArgs{Name: "storage-block", Owner: s.Owner.String(), Charm: ch, Storage: storage})
    87  	c.Assert(err, gc.ErrorMatches, `.* pool "invalid-pool" not found`)
    88  }
    89  
    90  func (s *VolumeStateSuite) TestAddServiceNoUserDefaultPool(c *gc.C) {
    91  	ch := s.AddTestingCharm(c, "storage-block")
    92  	storage := map[string]state.StorageConstraints{
    93  		"data": makeStorageCons("", 1024, 1),
    94  	}
    95  	service, err := s.State.AddService(state.AddServiceArgs{Name: "storage-block", Owner: s.Owner.String(), Charm: ch, Storage: storage})
    96  	c.Assert(err, jc.ErrorIsNil)
    97  	cons, err := service.StorageConstraints()
    98  	c.Assert(err, jc.ErrorIsNil)
    99  	c.Assert(cons, jc.DeepEquals, map[string]state.StorageConstraints{
   100  		"data": state.StorageConstraints{
   101  			Pool:  "loop",
   102  			Size:  1024,
   103  			Count: 1,
   104  		},
   105  		"allecto": state.StorageConstraints{
   106  			Pool:  "loop",
   107  			Size:  1024,
   108  			Count: 0,
   109  		},
   110  	})
   111  }
   112  
   113  func (s *VolumeStateSuite) TestAddServiceDefaultPool(c *gc.C) {
   114  	// Register a default pool.
   115  	pm := poolmanager.New(state.NewStateSettings(s.State))
   116  	_, err := pm.Create("default-block", provider.LoopProviderType, map[string]interface{}{})
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	err = s.State.UpdateModelConfig(map[string]interface{}{
   119  		"storage-default-block-source": "default-block",
   120  	}, nil, nil)
   121  	c.Assert(err, jc.ErrorIsNil)
   122  
   123  	ch := s.AddTestingCharm(c, "storage-block")
   124  	storage := map[string]state.StorageConstraints{
   125  		"data": makeStorageCons("", 1024, 1),
   126  	}
   127  	service := s.AddTestingServiceWithStorage(c, "storage-block", ch, storage)
   128  	cons, err := service.StorageConstraints()
   129  	c.Assert(err, jc.ErrorIsNil)
   130  	c.Assert(cons, jc.DeepEquals, map[string]state.StorageConstraints{
   131  		"data": state.StorageConstraints{
   132  			Pool:  "default-block",
   133  			Size:  1024,
   134  			Count: 1,
   135  		},
   136  		"allecto": state.StorageConstraints{
   137  			Pool:  "loop",
   138  			Size:  1024,
   139  			Count: 0,
   140  		},
   141  	})
   142  }
   143  
   144  func (s *VolumeStateSuite) TestSetVolumeInfo(c *gc.C) {
   145  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   146  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   147  	c.Assert(err, jc.ErrorIsNil)
   148  
   149  	volume := s.storageInstanceVolume(c, storageTag)
   150  	volumeTag := volume.VolumeTag()
   151  	s.assertVolumeUnprovisioned(c, volumeTag)
   152  
   153  	volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-ume"}
   154  	err = s.State.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	volumeInfoSet.Pool = "loop-pool" // taken from params
   157  	s.assertVolumeInfo(c, volumeTag, volumeInfoSet)
   158  }
   159  
   160  func (s *VolumeStateSuite) TestSetVolumeInfoNoVolumeId(c *gc.C) {
   161  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   162  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   163  	c.Assert(err, jc.ErrorIsNil)
   164  
   165  	volume := s.storageInstanceVolume(c, storageTag)
   166  	volumeTag := volume.VolumeTag()
   167  	s.assertVolumeUnprovisioned(c, volumeTag)
   168  
   169  	volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true}
   170  	err = s.State.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet)
   171  	c.Assert(err, gc.ErrorMatches, `cannot set info for volume "0/0": volume ID not set`)
   172  }
   173  
   174  func (s *VolumeStateSuite) TestSetVolumeInfoNoStorageAssigned(c *gc.C) {
   175  	oneJob := []state.MachineJob{state.JobHostUnits}
   176  	cons := constraints.MustParse("mem=4G")
   177  	hc := instance.MustParseHardware("mem=2G")
   178  
   179  	volumeParams := state.VolumeParams{
   180  		Pool: "loop-pool",
   181  		Size: 123,
   182  	}
   183  	machineTemplate := state.MachineTemplate{
   184  		Series:                  "precise",
   185  		Constraints:             cons,
   186  		HardwareCharacteristics: hc,
   187  		InstanceId:              "inst-id",
   188  		Nonce:                   "nonce",
   189  		Jobs:                    oneJob,
   190  		Volumes: []state.MachineVolumeParams{{
   191  			Volume: volumeParams,
   192  		}},
   193  	}
   194  	machines, err := s.State.AddMachines(machineTemplate)
   195  	c.Assert(err, jc.ErrorIsNil)
   196  	c.Assert(machines, gc.HasLen, 1)
   197  	m, err := s.State.Machine(machines[0].Id())
   198  	c.Assert(err, jc.ErrorIsNil)
   199  
   200  	volumeAttachments, err := s.State.MachineVolumeAttachments(m.MachineTag())
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	c.Assert(volumeAttachments, gc.HasLen, 1)
   203  	volumeTag := volumeAttachments[0].Volume()
   204  
   205  	volume := s.volume(c, volumeTag)
   206  	_, err = volume.StorageInstance()
   207  	c.Assert(err, jc.Satisfies, errors.IsNotAssigned)
   208  
   209  	s.assertVolumeUnprovisioned(c, volumeTag)
   210  	volumeInfoSet := state.VolumeInfo{Size: 123, VolumeId: "vol-ume"}
   211  	err = s.State.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet)
   212  	c.Assert(err, jc.ErrorIsNil)
   213  	volumeInfoSet.Pool = "loop-pool" // taken from params
   214  	s.assertVolumeInfo(c, volumeTag, volumeInfoSet)
   215  }
   216  
   217  func (s *VolumeStateSuite) TestSetVolumeInfoImmutable(c *gc.C) {
   218  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   219  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   220  	c.Assert(err, jc.ErrorIsNil)
   221  	volume := s.storageInstanceVolume(c, storageTag)
   222  	volumeTag := volume.VolumeTag()
   223  
   224  	volumeInfoSet := state.VolumeInfo{Size: 123, VolumeId: "vol-ume"}
   225  	err = s.State.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet)
   226  	c.Assert(err, jc.ErrorIsNil)
   227  
   228  	// The first call to SetVolumeInfo takes the pool name from
   229  	// the params; the second does not, but it must not change
   230  	// either. Callers are expected to get the existing info and
   231  	// update it, leaving immutable values intact.
   232  	err = s.State.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet)
   233  	c.Assert(err, gc.ErrorMatches, `cannot set info for volume "0/0": cannot change pool from "loop-pool" to ""`)
   234  
   235  	volumeInfoSet.Pool = "loop-pool"
   236  	s.assertVolumeInfo(c, volumeTag, volumeInfoSet)
   237  }
   238  
   239  func (s *VolumeStateSuite) TestWatchVolumeAttachment(c *gc.C) {
   240  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   241  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   242  	c.Assert(err, jc.ErrorIsNil)
   243  	assignedMachineId, err := u.AssignedMachineId()
   244  	c.Assert(err, jc.ErrorIsNil)
   245  	machineTag := names.NewMachineTag(assignedMachineId)
   246  
   247  	volume := s.storageInstanceVolume(c, storageTag)
   248  	volumeTag := volume.VolumeTag()
   249  
   250  	w := s.State.WatchVolumeAttachment(machineTag, volumeTag)
   251  	defer testing.AssertStop(c, w)
   252  	wc := testing.NewNotifyWatcherC(c, s.State, w)
   253  	wc.AssertOneChange()
   254  
   255  	machine, err := s.State.Machine(assignedMachineId)
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	err = machine.SetProvisioned("inst-id", "fake_nonce", nil)
   258  	c.Assert(err, jc.ErrorIsNil)
   259  
   260  	// volume attachment will NOT react to volume changes
   261  	err = s.State.SetVolumeInfo(volumeTag, state.VolumeInfo{VolumeId: "vol-123"})
   262  	c.Assert(err, jc.ErrorIsNil)
   263  	wc.AssertNoChange()
   264  
   265  	err = s.State.SetVolumeAttachmentInfo(
   266  		machineTag, volumeTag, state.VolumeAttachmentInfo{
   267  			DeviceName: "xvdf1",
   268  		},
   269  	)
   270  	c.Assert(err, jc.ErrorIsNil)
   271  	wc.AssertOneChange()
   272  }
   273  
   274  func (s *VolumeStateSuite) TestWatchModelVolumes(c *gc.C) {
   275  	service := s.setupMixedScopeStorageService(c, "block")
   276  	addUnit := func() {
   277  		u, err := service.AddUnit()
   278  		c.Assert(err, jc.ErrorIsNil)
   279  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   280  		c.Assert(err, jc.ErrorIsNil)
   281  	}
   282  	addUnit()
   283  
   284  	w := s.State.WatchModelVolumes()
   285  	defer testing.AssertStop(c, w)
   286  	wc := testing.NewStringsWatcherC(c, s.State, w)
   287  	wc.AssertChangeInSingleEvent("0") // initial
   288  	wc.AssertNoChange()
   289  
   290  	addUnit()
   291  	wc.AssertChangeInSingleEvent("3")
   292  	wc.AssertNoChange()
   293  
   294  	err := s.State.DestroyVolume(names.NewVolumeTag("0"))
   295  	c.Assert(err, jc.ErrorIsNil)
   296  	wc.AssertChangeInSingleEvent("0") // dying
   297  	wc.AssertNoChange()
   298  
   299  	err = s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0"))
   300  	c.Assert(err, jc.ErrorIsNil)
   301  	err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0"))
   302  	c.Assert(err, jc.ErrorIsNil)
   303  
   304  	err = s.State.RemoveVolume(names.NewVolumeTag("0"))
   305  	c.Assert(err, jc.ErrorIsNil)
   306  	wc.AssertChangeInSingleEvent("0") // removed
   307  	wc.AssertNoChange()
   308  }
   309  
   310  func (s *VolumeStateSuite) TestWatchEnvironVolumeAttachments(c *gc.C) {
   311  	service := s.setupMixedScopeStorageService(c, "block")
   312  	addUnit := func() {
   313  		u, err := service.AddUnit()
   314  		c.Assert(err, jc.ErrorIsNil)
   315  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   316  		c.Assert(err, jc.ErrorIsNil)
   317  	}
   318  	addUnit()
   319  
   320  	w := s.State.WatchEnvironVolumeAttachments()
   321  	defer testing.AssertStop(c, w)
   322  	wc := testing.NewStringsWatcherC(c, s.State, w)
   323  	wc.AssertChangeInSingleEvent("0:0") // initial
   324  	wc.AssertNoChange()
   325  
   326  	addUnit()
   327  	wc.AssertChangeInSingleEvent("1:3")
   328  	wc.AssertNoChange()
   329  
   330  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0"))
   331  	c.Assert(err, jc.ErrorIsNil)
   332  	wc.AssertChangeInSingleEvent("0:0") // dying
   333  	wc.AssertNoChange()
   334  
   335  	err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0"))
   336  	c.Assert(err, jc.ErrorIsNil)
   337  	wc.AssertChangeInSingleEvent("0:0") // removed
   338  	wc.AssertNoChange()
   339  }
   340  
   341  func (s *VolumeStateSuite) TestWatchMachineVolumes(c *gc.C) {
   342  	service := s.setupMixedScopeStorageService(c, "block")
   343  	addUnit := func() {
   344  		u, err := service.AddUnit()
   345  		c.Assert(err, jc.ErrorIsNil)
   346  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   347  		c.Assert(err, jc.ErrorIsNil)
   348  	}
   349  	addUnit()
   350  
   351  	w := s.State.WatchMachineVolumes(names.NewMachineTag("0"))
   352  	defer testing.AssertStop(c, w)
   353  	wc := testing.NewStringsWatcherC(c, s.State, w)
   354  	wc.AssertChangeInSingleEvent("0/1", "0/2") // initial
   355  	wc.AssertNoChange()
   356  
   357  	addUnit()
   358  	// no change, since we're only interested in the one machine.
   359  	wc.AssertNoChange()
   360  
   361  	err := s.State.DestroyVolume(names.NewVolumeTag("0/1"))
   362  	c.Assert(err, jc.ErrorIsNil)
   363  	wc.AssertChangeInSingleEvent("0/1") // dying
   364  	wc.AssertNoChange()
   365  
   366  	err = s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0/1"))
   367  	c.Assert(err, jc.ErrorIsNil)
   368  	err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/1"))
   369  	c.Assert(err, jc.ErrorIsNil)
   370  
   371  	err = s.State.RemoveVolume(names.NewVolumeTag("0/1"))
   372  	c.Assert(err, jc.ErrorIsNil)
   373  	wc.AssertChangeInSingleEvent("0/1") // removed
   374  	wc.AssertNoChange()
   375  }
   376  
   377  func (s *VolumeStateSuite) TestWatchMachineVolumeAttachments(c *gc.C) {
   378  	service := s.setupMixedScopeStorageService(c, "block")
   379  	addUnit := func(to *state.Machine) (u *state.Unit, m *state.Machine) {
   380  		var err error
   381  		u, err = service.AddUnit()
   382  		c.Assert(err, jc.ErrorIsNil)
   383  		if to != nil {
   384  			err = u.AssignToMachine(to)
   385  			c.Assert(err, jc.ErrorIsNil)
   386  			return u, to
   387  		}
   388  		err = s.State.AssignUnit(u, state.AssignCleanEmpty)
   389  		c.Assert(err, jc.ErrorIsNil)
   390  		mid, err := u.AssignedMachineId()
   391  		c.Assert(err, jc.ErrorIsNil)
   392  		m, err = s.State.Machine(mid)
   393  		c.Assert(err, jc.ErrorIsNil)
   394  		return u, m
   395  	}
   396  	_, m0 := addUnit(nil)
   397  
   398  	w := s.State.WatchMachineVolumeAttachments(names.NewMachineTag("0"))
   399  	defer testing.AssertStop(c, w)
   400  	wc := testing.NewStringsWatcherC(c, s.State, w)
   401  	wc.AssertChangeInSingleEvent("0:0/1", "0:0/2") // initial
   402  	wc.AssertNoChange()
   403  
   404  	addUnit(nil)
   405  	// no change, since we're only interested in the one machine.
   406  	wc.AssertNoChange()
   407  
   408  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0"))
   409  	c.Assert(err, jc.ErrorIsNil)
   410  	// no change, since we're only interested in attachments of
   411  	// machine-scoped volumes.
   412  	wc.AssertNoChange()
   413  
   414  	err = s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0/1"))
   415  	c.Assert(err, jc.ErrorIsNil)
   416  	wc.AssertChangeInSingleEvent("0:0/1") // dying
   417  	wc.AssertNoChange()
   418  
   419  	err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/1"))
   420  	c.Assert(err, jc.ErrorIsNil)
   421  	wc.AssertChangeInSingleEvent("0:0/1") // removed
   422  	wc.AssertNoChange()
   423  
   424  	addUnit(m0)
   425  	wc.AssertChangeInSingleEvent("0:0/7", "0:0/8") // added
   426  }
   427  
   428  func (s *VolumeStateSuite) TestParseVolumeAttachmentId(c *gc.C) {
   429  	assertValid := func(id string, m names.MachineTag, v names.VolumeTag) {
   430  		machineTag, volumeTag, err := state.ParseVolumeAttachmentId(id)
   431  		c.Assert(err, jc.ErrorIsNil)
   432  		c.Assert(machineTag, gc.Equals, m)
   433  		c.Assert(volumeTag, gc.Equals, v)
   434  	}
   435  	assertValid("0:0", names.NewMachineTag("0"), names.NewVolumeTag("0"))
   436  	assertValid("0:0/1", names.NewMachineTag("0"), names.NewVolumeTag("0/1"))
   437  	assertValid("0/lxc/0:1", names.NewMachineTag("0/lxc/0"), names.NewVolumeTag("1"))
   438  }
   439  
   440  func (s *VolumeStateSuite) TestParseVolumeAttachmentIdError(c *gc.C) {
   441  	assertError := func(id, expect string) {
   442  		_, _, err := state.ParseVolumeAttachmentId(id)
   443  		c.Assert(err, gc.ErrorMatches, expect)
   444  	}
   445  	assertError("", `invalid volume attachment ID ""`)
   446  	assertError("0", `invalid volume attachment ID "0"`)
   447  	assertError("0:foo", `invalid volume attachment ID "0:foo"`)
   448  	assertError("bar:0", `invalid volume attachment ID "bar:0"`)
   449  }
   450  
   451  func (s *VolumeStateSuite) TestAllVolumes(c *gc.C) {
   452  	_, expected, _ := s.assertCreateVolumes(c)
   453  
   454  	volumes, err := s.State.AllVolumes()
   455  	c.Assert(err, jc.ErrorIsNil)
   456  	tags := make([]names.VolumeTag, len(volumes))
   457  	for i, v := range volumes {
   458  		tags[i] = v.VolumeTag()
   459  	}
   460  	c.Assert(tags, jc.SameContents, expected)
   461  }
   462  
   463  func (s *VolumeStateSuite) assertCreateVolumes(c *gc.C) (_ *state.Machine, all, persistent []names.VolumeTag) {
   464  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
   465  		Series: "quantal",
   466  		Jobs:   []state.MachineJob{state.JobHostUnits},
   467  		Volumes: []state.MachineVolumeParams{{
   468  			Volume: state.VolumeParams{Pool: "persistent-block", Size: 1024},
   469  		}, {
   470  			Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048},
   471  		}, {
   472  			Volume: state.VolumeParams{Pool: "static", Size: 2048},
   473  		}},
   474  	})
   475  	c.Assert(err, jc.ErrorIsNil)
   476  	assertMachineStorageRefs(c, s.State, machine.MachineTag())
   477  
   478  	volume1 := s.volume(c, names.NewVolumeTag("0"))
   479  	volume2 := s.volume(c, names.NewVolumeTag("0/1"))
   480  	volume3 := s.volume(c, names.NewVolumeTag("2"))
   481  
   482  	c.Assert(volume1.LifeBinding(), gc.Equals, machine.MachineTag())
   483  	c.Assert(volume2.LifeBinding(), gc.Equals, machine.MachineTag())
   484  	c.Assert(volume3.LifeBinding(), gc.Equals, machine.MachineTag())
   485  
   486  	volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-1"}
   487  	err = s.State.SetVolumeInfo(volume1.VolumeTag(), volumeInfoSet)
   488  	c.Assert(err, jc.ErrorIsNil)
   489  
   490  	volumeInfoSet = state.VolumeInfo{Size: 456, Persistent: false, VolumeId: "vol-2"}
   491  	err = s.State.SetVolumeInfo(volume2.VolumeTag(), volumeInfoSet)
   492  	c.Assert(err, jc.ErrorIsNil)
   493  
   494  	all = []names.VolumeTag{
   495  		volume1.VolumeTag(),
   496  		volume2.VolumeTag(),
   497  		volume3.VolumeTag(),
   498  	}
   499  	persistent = []names.VolumeTag{
   500  		volume1.VolumeTag(),
   501  	}
   502  	return machine, all, persistent
   503  }
   504  
   505  func (s *VolumeStateSuite) TestRemoveStorageInstanceUnassignsVolume(c *gc.C) {
   506  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   507  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   508  	c.Assert(err, jc.ErrorIsNil)
   509  	volume := s.storageInstanceVolume(c, storageTag)
   510  	c.Assert(err, jc.ErrorIsNil)
   511  	volumeTag := volume.VolumeTag()
   512  
   513  	err = s.State.DestroyStorageInstance(storageTag)
   514  	c.Assert(err, jc.ErrorIsNil)
   515  	err = s.State.DestroyStorageAttachment(storageTag, u.UnitTag())
   516  	c.Assert(err, jc.ErrorIsNil)
   517  
   518  	// The storage instance and attachment are dying, but not yet
   519  	// removed from state. The volume should still be assigned.
   520  	s.storageInstanceVolume(c, storageTag)
   521  
   522  	err = s.State.RemoveStorageAttachment(storageTag, u.UnitTag())
   523  	c.Assert(err, jc.ErrorIsNil)
   524  
   525  	// The storage instance is now gone; the volume should no longer
   526  	// be assigned to the storage.
   527  	_, err = s.State.StorageInstanceVolume(storageTag)
   528  	c.Assert(err, gc.ErrorMatches, `volume for storage instance "data/0" not found`)
   529  
   530  	// The volume should not have been removed, though.
   531  	s.volume(c, volumeTag)
   532  }
   533  
   534  func (s *VolumeStateSuite) TestSetVolumeAttachmentInfoVolumeNotProvisioned(c *gc.C) {
   535  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   536  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   537  	c.Assert(err, jc.ErrorIsNil)
   538  	assignedMachineId, err := u.AssignedMachineId()
   539  	c.Assert(err, jc.ErrorIsNil)
   540  	machineTag := names.NewMachineTag(assignedMachineId)
   541  
   542  	volume := s.storageInstanceVolume(c, storageTag)
   543  	volumeTag := volume.VolumeTag()
   544  
   545  	err = s.State.SetVolumeAttachmentInfo(
   546  		machineTag, volumeTag, state.VolumeAttachmentInfo{
   547  			DeviceName: "xvdf1",
   548  		},
   549  	)
   550  	c.Assert(err, gc.ErrorMatches, `cannot set info for volume attachment 0/0:0: volume "0/0" not provisioned`)
   551  }
   552  
   553  func (s *VolumeStateSuite) TestDestroyVolume(c *gc.C) {
   554  	volume, _ := s.setupVolumeAttachment(c)
   555  	assertDestroy := func() {
   556  		err := s.State.DestroyVolume(volume.VolumeTag())
   557  		c.Assert(err, jc.ErrorIsNil)
   558  		volume = s.volume(c, volume.VolumeTag())
   559  		c.Assert(volume.Life(), gc.Equals, state.Dying)
   560  	}
   561  	defer state.SetBeforeHooks(c, s.State, assertDestroy).Check()
   562  	assertDestroy()
   563  }
   564  
   565  func (s *VolumeStateSuite) TestDestroyVolumeNoAttachments(c *gc.C) {
   566  	volume, machine := s.setupVolumeAttachment(c)
   567  
   568  	err := s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag())
   569  	c.Assert(err, jc.ErrorIsNil)
   570  
   571  	defer state.SetBeforeHooks(c, s.State, func() {
   572  		err := s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag())
   573  		c.Assert(err, jc.ErrorIsNil)
   574  	}).Check()
   575  
   576  	err = s.State.DestroyVolume(volume.VolumeTag())
   577  	c.Assert(err, jc.ErrorIsNil)
   578  	volume = s.volume(c, volume.VolumeTag())
   579  
   580  	// There are no more attachments, so the volume should
   581  	// have been progressed directly to Dead.
   582  	c.Assert(volume.Life(), gc.Equals, state.Dead)
   583  }
   584  
   585  func (s *VolumeStateSuite) TestRemoveVolume(c *gc.C) {
   586  	volume, machine := s.setupVolumeAttachment(c)
   587  	err := s.State.DestroyVolume(volume.VolumeTag())
   588  	c.Assert(err, jc.ErrorIsNil)
   589  	err = s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag())
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	err = s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag())
   592  	c.Assert(err, jc.ErrorIsNil)
   593  	assertRemove := func() {
   594  		err = s.State.RemoveVolume(volume.VolumeTag())
   595  		c.Assert(err, jc.ErrorIsNil)
   596  		_, err = s.State.Volume(volume.VolumeTag())
   597  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
   598  	}
   599  	defer state.SetBeforeHooks(c, s.State, assertRemove).Check()
   600  	assertRemove()
   601  }
   602  
   603  func (s *VolumeStateSuite) TestRemoveVolumeNotFound(c *gc.C) {
   604  	err := s.State.RemoveVolume(names.NewVolumeTag("42"))
   605  	c.Assert(err, jc.ErrorIsNil)
   606  }
   607  
   608  func (s *VolumeStateSuite) TestRemoveVolumeNotDead(c *gc.C) {
   609  	volume, _ := s.setupVolumeAttachment(c)
   610  	err := s.State.RemoveVolume(volume.VolumeTag())
   611  	c.Assert(err, gc.ErrorMatches, "removing volume 0/0: volume is not dead")
   612  	err = s.State.DestroyVolume(volume.VolumeTag())
   613  	c.Assert(err, jc.ErrorIsNil)
   614  	err = s.State.RemoveVolume(volume.VolumeTag())
   615  	c.Assert(err, gc.ErrorMatches, "removing volume 0/0: volume is not dead")
   616  }
   617  
   618  func (s *VolumeStateSuite) TestDetachVolume(c *gc.C) {
   619  	volume, machine := s.setupVolumeAttachment(c)
   620  	assertDetach := func() {
   621  		err := s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag())
   622  		c.Assert(err, jc.ErrorIsNil)
   623  		attachment := s.volumeAttachment(c, machine.MachineTag(), volume.VolumeTag())
   624  		c.Assert(attachment.Life(), gc.Equals, state.Dying)
   625  	}
   626  	defer state.SetBeforeHooks(c, s.State, assertDetach).Check()
   627  	assertDetach()
   628  }
   629  
   630  func (s *VolumeStateSuite) TestRemoveLastVolumeAttachment(c *gc.C) {
   631  	volume, machine := s.setupVolumeAttachment(c)
   632  
   633  	err := s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag())
   634  	c.Assert(err, jc.ErrorIsNil)
   635  
   636  	err = s.State.DestroyVolume(volume.VolumeTag())
   637  	c.Assert(err, jc.ErrorIsNil)
   638  	volume = s.volume(c, volume.VolumeTag())
   639  	c.Assert(volume.Life(), gc.Equals, state.Dying)
   640  
   641  	err = s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag())
   642  	c.Assert(err, jc.ErrorIsNil)
   643  
   644  	// The volume was Dying when the last attachment was
   645  	// removed, so the volume should now be Dead.
   646  	volume = s.volume(c, volume.VolumeTag())
   647  	c.Assert(volume.Life(), gc.Equals, state.Dead)
   648  }
   649  
   650  func (s *VolumeStateSuite) TestRemoveLastVolumeAttachmentConcurrently(c *gc.C) {
   651  	volume, machine := s.setupVolumeAttachment(c)
   652  
   653  	err := s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag())
   654  	c.Assert(err, jc.ErrorIsNil)
   655  
   656  	defer state.SetBeforeHooks(c, s.State, func() {
   657  		err := s.State.DestroyVolume(volume.VolumeTag())
   658  		c.Assert(err, jc.ErrorIsNil)
   659  		volume := s.volume(c, volume.VolumeTag())
   660  		c.Assert(volume.Life(), gc.Equals, state.Dying)
   661  	}).Check()
   662  
   663  	err = s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag())
   664  	c.Assert(err, jc.ErrorIsNil)
   665  
   666  	// Last attachment was removed, and the volume was (concurrently)
   667  	// destroyed, so the volume should be Dead.
   668  	volume = s.volume(c, volume.VolumeTag())
   669  	c.Assert(volume.Life(), gc.Equals, state.Dead)
   670  }
   671  
   672  func (s *VolumeStateSuite) TestRemoveVolumeAttachmentNotFound(c *gc.C) {
   673  	err := s.State.RemoveVolumeAttachment(names.NewMachineTag("42"), names.NewVolumeTag("42"))
   674  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   675  	c.Assert(err, gc.ErrorMatches, `removing attachment of volume 42 from machine 42: volume "42" on machine "42" not found`)
   676  }
   677  
   678  func (s *VolumeStateSuite) TestRemoveVolumeAttachmentConcurrently(c *gc.C) {
   679  	volume, machine := s.setupVolumeAttachment(c)
   680  	err := s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag())
   681  	c.Assert(err, jc.ErrorIsNil)
   682  	remove := func() {
   683  		err := s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag())
   684  		c.Assert(err, jc.ErrorIsNil)
   685  		assertMachineStorageRefs(c, s.State, machine.MachineTag())
   686  	}
   687  	defer state.SetBeforeHooks(c, s.State, remove).Check()
   688  	remove()
   689  }
   690  
   691  func (s *VolumeStateSuite) TestRemoveVolumeAttachmentAlive(c *gc.C) {
   692  	volume, machine := s.setupVolumeAttachment(c)
   693  	err := s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag())
   694  	c.Assert(err, gc.ErrorMatches, "removing attachment of volume 0/0 from machine 0: volume attachment is not dying")
   695  }
   696  
   697  func (s *VolumeStateSuite) TestRemoveMachineRemovesVolumes(c *gc.C) {
   698  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
   699  		Series: "quantal",
   700  		Jobs:   []state.MachineJob{state.JobHostUnits},
   701  		Volumes: []state.MachineVolumeParams{{
   702  			Volume: state.VolumeParams{Pool: "persistent-block", Size: 1024}, // unprovisioned
   703  		}, {
   704  			Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // provisioned
   705  		}, {
   706  			Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // unprovisioned
   707  		}, {
   708  			Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // provisioned, non-persistent
   709  		}, {
   710  			Volume: state.VolumeParams{Pool: "static", Size: 2048}, // provisioned
   711  		}, {
   712  			Volume: state.VolumeParams{Pool: "static", Size: 2048}, // unprovisioned
   713  		}},
   714  	})
   715  	c.Assert(err, jc.ErrorIsNil)
   716  
   717  	volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-1"}
   718  	err = s.State.SetVolumeInfo(names.NewVolumeTag("0/1"), volumeInfoSet)
   719  	c.Assert(err, jc.ErrorIsNil)
   720  	volumeInfoSet = state.VolumeInfo{Size: 456, Persistent: false, VolumeId: "vol-2"}
   721  	err = s.State.SetVolumeInfo(names.NewVolumeTag("0/3"), volumeInfoSet)
   722  	c.Assert(err, jc.ErrorIsNil)
   723  	volumeInfoSet = state.VolumeInfo{Size: 789, Persistent: false, VolumeId: "vol-3"}
   724  	err = s.State.SetVolumeInfo(names.NewVolumeTag("4"), volumeInfoSet)
   725  	c.Assert(err, jc.ErrorIsNil)
   726  
   727  	allVolumes, err := s.State.AllVolumes()
   728  	c.Assert(err, jc.ErrorIsNil)
   729  
   730  	persistentVolumes := make([]state.Volume, 0, len(allVolumes))
   731  	for _, v := range allVolumes {
   732  		info, err := v.Info()
   733  		if err == nil && info.Persistent {
   734  			persistentVolumes = append(persistentVolumes, v)
   735  		}
   736  	}
   737  	c.Assert(len(allVolumes), jc.GreaterThan, len(persistentVolumes))
   738  
   739  	c.Assert(machine.Destroy(), jc.ErrorIsNil)
   740  
   741  	// Cannot advance to Dead while there are persistent, or
   742  	// unprovisioned dynamic volumes (regardless of scope).
   743  	err = machine.EnsureDead()
   744  	c.Assert(err, jc.Satisfies, state.IsHasAttachmentsError)
   745  	c.Assert(err, gc.ErrorMatches, "machine 0 has attachments \\[volume-0 volume-0-1 volume-0-2\\]")
   746  	s.obliterateVolumeAttachment(c, machine.MachineTag(), names.NewVolumeTag("0"))
   747  	s.obliterateVolumeAttachment(c, machine.MachineTag(), names.NewVolumeTag("0/1"))
   748  	s.obliterateVolumeAttachment(c, machine.MachineTag(), names.NewVolumeTag("0/2"))
   749  	c.Assert(machine.EnsureDead(), jc.ErrorIsNil)
   750  	c.Assert(machine.Remove(), jc.ErrorIsNil)
   751  
   752  	// Machine is gone: non-persistent and unprovisioned static or machine-
   753  	// scoped storage should be gone too.
   754  	allVolumes, err = s.State.AllVolumes()
   755  	c.Assert(err, jc.ErrorIsNil)
   756  	// We should only have the persistent volume and the loop devices remaining.
   757  	remaining := make(set.Strings)
   758  	for _, v := range allVolumes {
   759  		remaining.Add(v.Tag().String())
   760  	}
   761  	c.Assert(remaining.SortedValues(), jc.DeepEquals, []string{
   762  		"volume-0", "volume-0-1", "volume-0-2",
   763  	})
   764  
   765  	attachments, err := s.State.MachineVolumeAttachments(machine.MachineTag())
   766  	c.Assert(err, jc.ErrorIsNil)
   767  	c.Assert(attachments, gc.HasLen, 0)
   768  }
   769  
   770  func (s *VolumeStateSuite) TestEnsureMachineDeadAddVolumeConcurrently(c *gc.C) {
   771  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
   772  		Series: "quantal",
   773  		Jobs:   []state.MachineJob{state.JobHostUnits},
   774  		Volumes: []state.MachineVolumeParams{{
   775  			Volume: state.VolumeParams{Pool: "static", Size: 1024},
   776  		}},
   777  	})
   778  	c.Assert(err, jc.ErrorIsNil)
   779  
   780  	addVolume := func() {
   781  		_, u, _ := s.setupSingleStorage(c, "block", "environscoped")
   782  		err := u.AssignToMachine(machine)
   783  		c.Assert(err, jc.ErrorIsNil)
   784  		s.obliterateUnit(c, u.UnitTag())
   785  	}
   786  	defer state.SetBeforeHooks(c, s.State, addVolume).Check()
   787  
   788  	// The static volume the machine was provisioned with does not matter,
   789  	// but the volume added concurrently does.
   790  	err = machine.EnsureDead()
   791  	c.Assert(err, gc.ErrorMatches, `machine 0 has attachments \[volume-1\]`)
   792  }
   793  
   794  func (s *VolumeStateSuite) TestEnsureMachineDeadRemoveVolumeConcurrently(c *gc.C) {
   795  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
   796  		Series: "quantal",
   797  		Jobs:   []state.MachineJob{state.JobHostUnits},
   798  		Volumes: []state.MachineVolumeParams{{
   799  			Volume: state.VolumeParams{Pool: "static", Size: 1024},
   800  		}},
   801  	})
   802  	c.Assert(err, jc.ErrorIsNil)
   803  
   804  	removeVolume := func() {
   805  		s.obliterateVolume(c, names.NewVolumeTag("0"))
   806  	}
   807  	defer state.SetBeforeHooks(c, s.State, removeVolume).Check()
   808  
   809  	// Removing a volume concurrently does not cause a transaction failure.
   810  	err = machine.EnsureDead()
   811  	c.Assert(err, jc.ErrorIsNil)
   812  }
   813  
   814  func (s *VolumeStateSuite) TestVolumeBindingMachine(c *gc.C) {
   815  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
   816  		Series: "quantal",
   817  		Jobs:   []state.MachineJob{state.JobHostUnits},
   818  		Volumes: []state.MachineVolumeParams{{
   819  			Volume: state.VolumeParams{Pool: "environscoped", Size: 1024},
   820  		}},
   821  	})
   822  	c.Assert(err, jc.ErrorIsNil)
   823  
   824  	// Volumes created unassigned to a storage instance are
   825  	// bound to the initially attached machine.
   826  	volume := s.volume(c, names.NewVolumeTag("0"))
   827  	c.Assert(volume.LifeBinding(), gc.Equals, machine.Tag())
   828  	c.Assert(volume.Life(), gc.Equals, state.Alive)
   829  
   830  	err = s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag())
   831  	c.Assert(err, jc.ErrorIsNil)
   832  	err = s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag())
   833  	c.Assert(err, jc.ErrorIsNil)
   834  	volume = s.volume(c, volume.VolumeTag())
   835  	c.Assert(volume.Life(), gc.Equals, state.Dead)
   836  
   837  	// TODO(axw) when we can assign storage to an existing volume, we
   838  	// should test that a machine-bound volume is not destroyed when
   839  	// its assigned storage instance is removed.
   840  }
   841  
   842  func (s *VolumeStateSuite) TestVolumeBindingStorage(c *gc.C) {
   843  	// Volumes created assigned to a storage instance are bound
   844  	// to the storage instance.
   845  	volume, _ := s.setupVolumeAttachment(c)
   846  	storageTag, err := volume.StorageInstance()
   847  	c.Assert(err, jc.ErrorIsNil)
   848  	c.Assert(volume.LifeBinding(), gc.Equals, storageTag)
   849  
   850  	err = s.State.DestroyStorageInstance(storageTag)
   851  	c.Assert(err, jc.ErrorIsNil)
   852  	attachments, err := s.State.StorageAttachments(storageTag)
   853  	c.Assert(err, jc.ErrorIsNil)
   854  	for _, a := range attachments {
   855  		err = s.State.DestroyStorageAttachment(storageTag, a.Unit())
   856  		c.Assert(err, jc.ErrorIsNil)
   857  		err = s.State.RemoveStorageAttachment(storageTag, a.Unit())
   858  		c.Assert(err, jc.ErrorIsNil)
   859  	}
   860  
   861  	// The storage instance should be removed,
   862  	// and the volume should be Dying.
   863  	_, err = s.State.StorageInstance(storageTag)
   864  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   865  	volume = s.volume(c, volume.VolumeTag())
   866  	c.Assert(volume.Life(), gc.Equals, state.Dying)
   867  }
   868  
   869  func (s *VolumeStateSuite) setupVolumeAttachment(c *gc.C) (state.Volume, *state.Machine) {
   870  	_, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool")
   871  	err := s.State.AssignUnit(u, state.AssignCleanEmpty)
   872  	c.Assert(err, jc.ErrorIsNil)
   873  	assignedMachineId, err := u.AssignedMachineId()
   874  	c.Assert(err, jc.ErrorIsNil)
   875  	return s.storageInstanceVolume(c, storageTag), s.machine(c, assignedMachineId)
   876  }