github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/cleanup_test.go (about)

     1  // Copyright 2014-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"bytes"
     8  
     9  	"github.com/juju/errors"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	"gopkg.in/juju/charm.v6-unstable"
    13  	"gopkg.in/juju/names.v2"
    14  
    15  	"github.com/juju/juju/instance"
    16  	"github.com/juju/juju/state"
    17  	"github.com/juju/juju/state/storage"
    18  	"github.com/juju/juju/testing/factory"
    19  )
    20  
    21  type CleanupSuite struct {
    22  	ConnSuite
    23  }
    24  
    25  var _ = gc.Suite(&CleanupSuite{})
    26  
    27  func (s *CleanupSuite) SetUpTest(c *gc.C) {
    28  	s.ConnSuite.SetUpTest(c)
    29  	s.assertDoesNotNeedCleanup(c)
    30  }
    31  
    32  func (s *CleanupSuite) TestCleanupDyingServiceUnits(c *gc.C) {
    33  	// Create a service with some units.
    34  	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
    35  	units := make([]*state.Unit, 3)
    36  	for i := range units {
    37  		unit, err := mysql.AddUnit()
    38  		c.Assert(err, jc.ErrorIsNil)
    39  		units[i] = unit
    40  	}
    41  	preventUnitDestroyRemove(c, units[0])
    42  	s.assertDoesNotNeedCleanup(c)
    43  
    44  	// Destroy the service and check the units are unaffected, but a cleanup
    45  	// has been scheduled.
    46  	err := mysql.Destroy()
    47  	c.Assert(err, jc.ErrorIsNil)
    48  	for _, unit := range units {
    49  		err := unit.Refresh()
    50  		c.Assert(err, jc.ErrorIsNil)
    51  	}
    52  	s.assertNeedsCleanup(c)
    53  
    54  	// Run the cleanup, and check that units are all destroyed as appropriate.
    55  	s.assertCleanupRuns(c)
    56  	err = units[0].Refresh()
    57  	c.Assert(err, jc.ErrorIsNil)
    58  	c.Assert(units[0].Life(), gc.Equals, state.Dying)
    59  	err = units[1].Refresh()
    60  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    61  	err = units[2].Refresh()
    62  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    63  
    64  	// Run a final cleanup to clear the cleanup scheduled for the unit that
    65  	// became dying.
    66  	s.assertCleanupCount(c, 1)
    67  }
    68  
    69  func (s *CleanupSuite) TestCleanupDyingServiceCharm(c *gc.C) {
    70  	// Create a service and  a charm.
    71  	ch := s.AddTestingCharm(c, "mysql")
    72  	mysql := s.AddTestingService(c, "mysql", ch)
    73  
    74  	// Create a dummy archive blob.
    75  	stor := storage.NewStorage(s.State.ModelUUID(), s.State.MongoSession())
    76  	storagePath := "dummy-path"
    77  	err := stor.Put(storagePath, bytes.NewReader([]byte("data")), 4)
    78  	c.Assert(err, jc.ErrorIsNil)
    79  
    80  	// Destroy the service and check that a cleanup has been scheduled.
    81  	err = mysql.Destroy()
    82  	c.Assert(err, jc.ErrorIsNil)
    83  	s.assertNeedsCleanup(c)
    84  
    85  	// Run the cleanup, and check that the charm is removed.
    86  	s.assertCleanupRuns(c)
    87  	_, _, err = stor.Get(storagePath)
    88  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    89  }
    90  
    91  func (s *CleanupSuite) TestCleanupControllerModels(c *gc.C) {
    92  	s.assertDoesNotNeedCleanup(c)
    93  
    94  	// Create a non-empty hosted model.
    95  	otherSt := s.Factory.MakeModel(c, nil)
    96  	defer otherSt.Close()
    97  	factory.NewFactory(otherSt).MakeApplication(c, nil)
    98  	otherEnv, err := otherSt.Model()
    99  	c.Assert(err, jc.ErrorIsNil)
   100  
   101  	s.assertDoesNotNeedCleanup(c)
   102  
   103  	// Destroy the controller and check the model is unaffected, but a
   104  	// cleanup for the model and services has been scheduled.
   105  	controllerEnv, err := s.State.Model()
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	err = controllerEnv.DestroyIncludingHosted()
   108  	c.Assert(err, jc.ErrorIsNil)
   109  
   110  	// Two cleanups should be scheduled. One to destroy the hosted
   111  	// models, the other to destroy the controller model's
   112  	// services.
   113  	s.assertCleanupCount(c, 1)
   114  	err = otherEnv.Refresh()
   115  	c.Assert(err, jc.ErrorIsNil)
   116  	c.Assert(otherEnv.Life(), gc.Equals, state.Dying)
   117  
   118  	s.assertDoesNotNeedCleanup(c)
   119  }
   120  
   121  func (s *CleanupSuite) TestCleanupModelMachines(c *gc.C) {
   122  	// Create a controller machine, and manual and non-manual
   123  	// workload machine.
   124  	stateMachine, err := s.State.AddMachine("quantal", state.JobManageModel)
   125  	c.Assert(err, jc.ErrorIsNil)
   126  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	manualMachine, err := s.State.AddOneMachine(state.MachineTemplate{
   129  		Series:     "quantal",
   130  		Jobs:       []state.MachineJob{state.JobHostUnits},
   131  		InstanceId: "inst-ance",
   132  		Nonce:      "manual:foo",
   133  	})
   134  	c.Assert(err, jc.ErrorIsNil)
   135  
   136  	// Create a relation with a unit in scope and assigned to the hosted machine.
   137  	pr := NewPeerRelation(c, s.State)
   138  	err = pr.u0.AssignToMachine(machine)
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	err = pr.ru0.EnterScope(nil)
   141  	c.Assert(err, jc.ErrorIsNil)
   142  	s.assertDoesNotNeedCleanup(c)
   143  
   144  	// Destroy model, check cleanup queued.
   145  	env, err := s.State.Model()
   146  	c.Assert(err, jc.ErrorIsNil)
   147  	err = env.Destroy()
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	s.assertNeedsCleanup(c)
   150  
   151  	// Clean up, and check that the unit has been removed...
   152  	s.assertCleanupCount(c, 3)
   153  	assertRemoved(c, pr.u0)
   154  
   155  	// ...and the unit has departed relation scope...
   156  	assertNotJoined(c, pr.ru0)
   157  
   158  	// ...but that the machine remains, and is Dead, ready for removal by the
   159  	// provisioner.
   160  	assertLife(c, machine, state.Dead)
   161  	assertLife(c, manualMachine, state.Dying)
   162  	assertLife(c, stateMachine, state.Alive)
   163  }
   164  
   165  func (s *CleanupSuite) TestCleanupModelServices(c *gc.C) {
   166  	s.assertDoesNotNeedCleanup(c)
   167  
   168  	// Create a service with some units.
   169  	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
   170  	units := make([]*state.Unit, 3)
   171  	for i := range units {
   172  		unit, err := mysql.AddUnit()
   173  		c.Assert(err, jc.ErrorIsNil)
   174  		units[i] = unit
   175  	}
   176  	s.assertDoesNotNeedCleanup(c)
   177  
   178  	// Destroy the model and check the service and units are
   179  	// unaffected, but a cleanup for the application has been scheduled.
   180  	env, err := s.State.Model()
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	err = env.Destroy()
   183  	c.Assert(err, jc.ErrorIsNil)
   184  	s.assertNeedsCleanup(c)
   185  	s.assertCleanupRuns(c)
   186  	err = mysql.Refresh()
   187  	c.Assert(err, jc.ErrorIsNil)
   188  	c.Assert(mysql.Life(), gc.Equals, state.Dying)
   189  	for _, unit := range units {
   190  		err = unit.Refresh()
   191  		c.Assert(err, jc.ErrorIsNil)
   192  		c.Assert(unit.Life(), gc.Equals, state.Alive)
   193  	}
   194  
   195  	// The first cleanup Destroys the service, which
   196  	// schedules another cleanup to destroy the units,
   197  	// then we need another pass for the actions cleanup
   198  	// which is queued on the next pass
   199  	s.assertCleanupCount(c, 2)
   200  	for _, unit := range units {
   201  		err = unit.Refresh()
   202  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
   203  	}
   204  
   205  	// Now we should have all the cleanups done
   206  	s.assertDoesNotNeedCleanup(c)
   207  }
   208  
   209  func (s *CleanupSuite) TestCleanupRelationSettings(c *gc.C) {
   210  	// Create a relation with a unit in scope.
   211  	pr := NewPeerRelation(c, s.State)
   212  	rel := pr.ru0.Relation()
   213  	err := pr.ru0.EnterScope(map[string]interface{}{"some": "settings"})
   214  	c.Assert(err, jc.ErrorIsNil)
   215  	s.assertDoesNotNeedCleanup(c)
   216  
   217  	// Destroy the service, check the relation's still around.
   218  	err = pr.svc.Destroy()
   219  	c.Assert(err, jc.ErrorIsNil)
   220  	s.assertCleanupCount(c, 2)
   221  	err = rel.Refresh()
   222  	c.Assert(err, jc.ErrorIsNil)
   223  	c.Assert(rel.Life(), gc.Equals, state.Dying)
   224  
   225  	// The unit leaves scope, triggering relation removal.
   226  	err = pr.ru0.LeaveScope()
   227  	c.Assert(err, jc.ErrorIsNil)
   228  	s.assertNeedsCleanup(c)
   229  
   230  	// Settings are not destroyed yet...
   231  	settings, err := pr.ru1.ReadSettings("riak/0")
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	c.Assert(settings, gc.DeepEquals, map[string]interface{}{"some": "settings"})
   234  
   235  	// ...but they are on cleanup.
   236  	s.assertCleanupCount(c, 1)
   237  	_, err = pr.ru1.ReadSettings("riak/0")
   238  	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": settings not found`)
   239  }
   240  
   241  func (s *CleanupSuite) TestForceDestroyMachineErrors(c *gc.C) {
   242  	manager, err := s.State.AddMachine("quantal", state.JobManageModel)
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	s.assertDoesNotNeedCleanup(c)
   245  	err = manager.ForceDestroy()
   246  	c.Assert(err, gc.ErrorMatches, "machine is required by the model")
   247  	s.assertDoesNotNeedCleanup(c)
   248  	assertLife(c, manager, state.Alive)
   249  }
   250  
   251  func (s *CleanupSuite) TestCleanupForceDestroyedMachineUnit(c *gc.C) {
   252  	// Create a machine.
   253  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   254  	c.Assert(err, jc.ErrorIsNil)
   255  
   256  	// Create a relation with a unit in scope and assigned to the machine.
   257  	pr := NewPeerRelation(c, s.State)
   258  	err = pr.u0.AssignToMachine(machine)
   259  	c.Assert(err, jc.ErrorIsNil)
   260  	err = pr.ru0.EnterScope(nil)
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	s.assertDoesNotNeedCleanup(c)
   263  
   264  	// Force machine destruction, check cleanup queued.
   265  	err = machine.ForceDestroy()
   266  	c.Assert(err, jc.ErrorIsNil)
   267  	s.assertNeedsCleanup(c)
   268  
   269  	// Clean up, and check that the unit has been removed...
   270  	s.assertCleanupCount(c, 2)
   271  	assertRemoved(c, pr.u0)
   272  
   273  	// ...and the unit has departed relation scope...
   274  	assertNotJoined(c, pr.ru0)
   275  
   276  	// ...but that the machine remains, and is Dead, ready for removal by the
   277  	// provisioner.
   278  	assertLife(c, machine, state.Dead)
   279  }
   280  
   281  func (s *CleanupSuite) TestCleanupForceDestroyMachineCleansStorageAttachments(c *gc.C) {
   282  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   283  	c.Assert(err, jc.ErrorIsNil)
   284  	s.assertDoesNotNeedCleanup(c)
   285  
   286  	ch := s.AddTestingCharm(c, "storage-block")
   287  	storage := map[string]state.StorageConstraints{
   288  		"data": makeStorageCons("loop", 1024, 1),
   289  	}
   290  	service := s.AddTestingServiceWithStorage(c, "storage-block", ch, storage)
   291  	u, err := service.AddUnit()
   292  	c.Assert(err, jc.ErrorIsNil)
   293  	err = u.AssignToMachine(machine)
   294  	c.Assert(err, jc.ErrorIsNil)
   295  
   296  	// check no cleanups
   297  	s.assertDoesNotNeedCleanup(c)
   298  
   299  	// this tag matches the storage instance created for the unit above.
   300  	storageTag := names.NewStorageTag("data/0")
   301  
   302  	sa, err := s.State.StorageAttachment(storageTag, u.UnitTag())
   303  	c.Assert(err, jc.ErrorIsNil)
   304  	c.Assert(sa.Life(), gc.Equals, state.Alive)
   305  
   306  	// destroy machine and run cleanups
   307  	err = machine.ForceDestroy()
   308  	c.Assert(err, jc.ErrorIsNil)
   309  	s.assertCleanupCount(c, 2)
   310  
   311  	// After running the cleanup, the storage attachment and instance
   312  	// should both be removed.
   313  	_, err = s.State.StorageAttachment(storageTag, u.UnitTag())
   314  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   315  	_, err = s.State.StorageInstance(storageTag)
   316  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   317  
   318  	// Check that the unit has been removed.
   319  	assertRemoved(c, u)
   320  
   321  	// check no cleanups
   322  	s.assertDoesNotNeedCleanup(c)
   323  }
   324  
   325  func (s *CleanupSuite) TestCleanupForceDestroyedMachineWithContainer(c *gc.C) {
   326  	// Create a machine with a container.
   327  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   328  	c.Assert(err, jc.ErrorIsNil)
   329  	container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{
   330  		Series: "quantal",
   331  		Jobs:   []state.MachineJob{state.JobHostUnits},
   332  	}, machine.Id(), instance.LXD)
   333  	c.Assert(err, jc.ErrorIsNil)
   334  
   335  	// Create active units (in relation scope, with subordinates).
   336  	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeContainer)
   337  	err = prr.pru0.EnterScope(nil)
   338  	c.Assert(err, jc.ErrorIsNil)
   339  	err = prr.pru1.EnterScope(nil)
   340  	c.Assert(err, jc.ErrorIsNil)
   341  	err = prr.rru0.EnterScope(nil)
   342  	c.Assert(err, jc.ErrorIsNil)
   343  	err = prr.rru1.EnterScope(nil)
   344  	c.Assert(err, jc.ErrorIsNil)
   345  
   346  	// Assign the various units to machines.
   347  	err = prr.pu0.AssignToMachine(machine)
   348  	c.Assert(err, jc.ErrorIsNil)
   349  	err = prr.pu1.AssignToMachine(container)
   350  	c.Assert(err, jc.ErrorIsNil)
   351  	s.assertDoesNotNeedCleanup(c)
   352  
   353  	// Force removal of the top-level machine.
   354  	err = machine.ForceDestroy()
   355  	c.Assert(err, jc.ErrorIsNil)
   356  	s.assertNeedsCleanup(c)
   357  
   358  	// And do it again, just to check that the second cleanup doc for the same
   359  	// machine doesn't cause problems down the line.
   360  	err = machine.ForceDestroy()
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	s.assertNeedsCleanup(c)
   363  
   364  	// Clean up, and check that the container has been removed...
   365  	s.assertCleanupCount(c, 2)
   366  	err = container.Refresh()
   367  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   368  
   369  	// ...and so have all the units...
   370  	assertRemoved(c, prr.pu0)
   371  	assertRemoved(c, prr.pu1)
   372  	assertRemoved(c, prr.ru0)
   373  	assertRemoved(c, prr.ru1)
   374  
   375  	// ...and none of the units have left relation scopes occupied...
   376  	assertNotInScope(c, prr.pru0)
   377  	assertNotInScope(c, prr.pru1)
   378  	assertNotInScope(c, prr.rru0)
   379  	assertNotInScope(c, prr.rru1)
   380  
   381  	// ...but that the machine remains, and is Dead, ready for removal by the
   382  	// provisioner.
   383  	assertLife(c, machine, state.Dead)
   384  }
   385  
   386  func (s *CleanupSuite) TestCleanupDyingUnit(c *gc.C) {
   387  	// Create active unit, in a relation.
   388  	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)
   389  	err := prr.pru0.EnterScope(nil)
   390  	c.Assert(err, jc.ErrorIsNil)
   391  
   392  	// Destroy provider unit 0; check it's Dying, and a cleanup has been scheduled.
   393  	err = prr.pu0.Destroy()
   394  	c.Assert(err, jc.ErrorIsNil)
   395  	err = prr.pu0.Refresh()
   396  	c.Assert(err, jc.ErrorIsNil)
   397  	assertLife(c, prr.pu0, state.Dying)
   398  	s.assertNeedsCleanup(c)
   399  
   400  	// Check it's reported in scope until cleaned up.
   401  	assertJoined(c, prr.pru0)
   402  	s.assertCleanupCount(c, 1)
   403  	assertInScope(c, prr.pru0)
   404  	assertNotJoined(c, prr.pru0)
   405  
   406  	// Destroy the relation, and check it sticks around...
   407  	err = prr.rel.Destroy()
   408  	c.Assert(err, jc.ErrorIsNil)
   409  	assertLife(c, prr.rel, state.Dying)
   410  
   411  	// ...until the unit is removed, and really leaves scope.
   412  	err = prr.pu0.EnsureDead()
   413  	c.Assert(err, jc.ErrorIsNil)
   414  	err = prr.pu0.Remove()
   415  	c.Assert(err, jc.ErrorIsNil)
   416  	assertNotInScope(c, prr.pru0)
   417  	assertRemoved(c, prr.rel)
   418  }
   419  
   420  func (s *CleanupSuite) TestCleanupDyingUnitAlreadyRemoved(c *gc.C) {
   421  	// Create active unit, in a relation.
   422  	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)
   423  	err := prr.pru0.EnterScope(nil)
   424  	c.Assert(err, jc.ErrorIsNil)
   425  
   426  	// Destroy provider unit 0; check it's Dying, and a cleanup has been scheduled.
   427  	err = prr.pu0.Destroy()
   428  	c.Assert(err, jc.ErrorIsNil)
   429  	err = prr.pu0.Refresh()
   430  	c.Assert(err, jc.ErrorIsNil)
   431  	assertLife(c, prr.pu0, state.Dying)
   432  	s.assertNeedsCleanup(c)
   433  
   434  	// Remove the unit, and the relation.
   435  	err = prr.pu0.EnsureDead()
   436  	c.Assert(err, jc.ErrorIsNil)
   437  	err = prr.pu0.Remove()
   438  	c.Assert(err, jc.ErrorIsNil)
   439  	err = prr.rel.Destroy()
   440  	c.Assert(err, jc.ErrorIsNil)
   441  	assertRemoved(c, prr.rel)
   442  
   443  	// Check the cleanup still runs happily.
   444  	s.assertCleanupCount(c, 1)
   445  }
   446  
   447  func (s *CleanupSuite) TestCleanupActions(c *gc.C) {
   448  	// Create a service with a unit.
   449  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   450  	unit, err := dummy.AddUnit()
   451  	c.Assert(err, jc.ErrorIsNil)
   452  
   453  	// check no cleanups
   454  	s.assertDoesNotNeedCleanup(c)
   455  
   456  	// Add a couple actions to the unit
   457  	_, err = unit.AddAction("snapshot", nil)
   458  	c.Assert(err, jc.ErrorIsNil)
   459  	_, err = unit.AddAction("snapshot", nil)
   460  	c.Assert(err, jc.ErrorIsNil)
   461  
   462  	// make sure unit still has actions
   463  	actions, err := unit.PendingActions()
   464  	c.Assert(err, jc.ErrorIsNil)
   465  	c.Assert(len(actions), gc.Equals, 2)
   466  
   467  	// destroy unit and run cleanups
   468  	err = dummy.Destroy()
   469  	c.Assert(err, jc.ErrorIsNil)
   470  	s.assertCleanupRuns(c)
   471  
   472  	// make sure unit still has actions, after first cleanup pass
   473  	actions, err = unit.PendingActions()
   474  	c.Assert(err, jc.ErrorIsNil)
   475  	c.Assert(len(actions), gc.Equals, 2)
   476  
   477  	// second cleanup pass
   478  	s.assertCleanupRuns(c)
   479  
   480  	// make sure unit has no actions, after second cleanup pass
   481  	actions, err = unit.PendingActions()
   482  	c.Assert(err, jc.ErrorIsNil)
   483  	c.Assert(len(actions), gc.Equals, 0)
   484  
   485  	// check no cleanups
   486  	s.assertDoesNotNeedCleanup(c)
   487  }
   488  
   489  func (s *CleanupSuite) TestCleanupStorageAttachments(c *gc.C) {
   490  	s.assertDoesNotNeedCleanup(c)
   491  
   492  	ch := s.AddTestingCharm(c, "storage-block")
   493  	storage := map[string]state.StorageConstraints{
   494  		"data": makeStorageCons("loop", 1024, 1),
   495  	}
   496  	service := s.AddTestingServiceWithStorage(c, "storage-block", ch, storage)
   497  	u, err := service.AddUnit()
   498  	c.Assert(err, jc.ErrorIsNil)
   499  
   500  	// check no cleanups
   501  	s.assertDoesNotNeedCleanup(c)
   502  
   503  	// this tag matches the storage instance created for the unit above.
   504  	storageTag := names.NewStorageTag("data/0")
   505  
   506  	sa, err := s.State.StorageAttachment(storageTag, u.UnitTag())
   507  	c.Assert(err, jc.ErrorIsNil)
   508  	c.Assert(sa.Life(), gc.Equals, state.Alive)
   509  
   510  	// destroy unit and run cleanups; the attachment should be marked dying
   511  	err = u.Destroy()
   512  	c.Assert(err, jc.ErrorIsNil)
   513  	s.assertCleanupRuns(c)
   514  
   515  	// After running the cleanup, the attachment should be dying.
   516  	sa, err = s.State.StorageAttachment(storageTag, u.UnitTag())
   517  	c.Assert(err, jc.ErrorIsNil)
   518  	c.Assert(sa.Life(), gc.Equals, state.Dying)
   519  
   520  	// check no cleanups
   521  	s.assertDoesNotNeedCleanup(c)
   522  }
   523  
   524  func (s *CleanupSuite) TestCleanupStorageInstances(c *gc.C) {
   525  	ch := s.AddTestingCharm(c, "storage-block")
   526  	storage := map[string]state.StorageConstraints{
   527  		"data": makeStorageCons("loop", 1024, 1),
   528  	}
   529  	service := s.AddTestingServiceWithStorage(c, "storage-block", ch, storage)
   530  	u, err := service.AddUnit()
   531  	c.Assert(err, jc.ErrorIsNil)
   532  
   533  	// check no cleanups
   534  	s.assertDoesNotNeedCleanup(c)
   535  
   536  	// this tag matches the storage instance created for the unit above.
   537  	storageTag := names.NewStorageTag("data/0")
   538  
   539  	si, err := s.State.StorageInstance(storageTag)
   540  	c.Assert(err, jc.ErrorIsNil)
   541  	c.Assert(si.Life(), gc.Equals, state.Alive)
   542  
   543  	// destroy storage instance and run cleanups
   544  	err = s.State.DestroyStorageInstance(storageTag)
   545  	c.Assert(err, jc.ErrorIsNil)
   546  	si, err = s.State.StorageInstance(storageTag)
   547  	c.Assert(err, jc.ErrorIsNil)
   548  	c.Assert(si.Life(), gc.Equals, state.Dying)
   549  	sa, err := s.State.UnitStorageAttachments(u.UnitTag())
   550  	c.Assert(err, jc.ErrorIsNil)
   551  	c.Assert(sa, gc.HasLen, 1)
   552  	c.Assert(sa[0].Life(), gc.Equals, state.Alive)
   553  	s.assertCleanupRuns(c)
   554  
   555  	// After running the cleanup, the attachment should be dying.
   556  	sa, err = s.State.UnitStorageAttachments(u.UnitTag())
   557  	c.Assert(err, jc.ErrorIsNil)
   558  	c.Assert(sa, gc.HasLen, 1)
   559  	c.Assert(sa[0].Life(), gc.Equals, state.Dying)
   560  
   561  	// check no cleanups
   562  	s.assertDoesNotNeedCleanup(c)
   563  }
   564  
   565  func (s *CleanupSuite) TestCleanupMachineStorage(c *gc.C) {
   566  	ch := s.AddTestingCharm(c, "storage-filesystem")
   567  	storage := map[string]state.StorageConstraints{
   568  		"data": makeStorageCons("loop", 1024, 1),
   569  	}
   570  	service := s.AddTestingServiceWithStorage(c, "storage-filesystem", ch, storage)
   571  	unit, err := service.AddUnit()
   572  	c.Assert(err, jc.ErrorIsNil)
   573  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
   574  	c.Assert(err, jc.ErrorIsNil)
   575  	machineId, err := unit.AssignedMachineId()
   576  	c.Assert(err, jc.ErrorIsNil)
   577  	machine, err := s.State.Machine(machineId)
   578  	c.Assert(err, jc.ErrorIsNil)
   579  
   580  	// Destroy the service, so we can destroy the machine.
   581  	err = unit.Destroy()
   582  	s.assertCleanupRuns(c)
   583  	err = service.Destroy()
   584  	c.Assert(err, jc.ErrorIsNil)
   585  	s.assertCleanupRuns(c)
   586  
   587  	// check no cleanups
   588  	s.assertDoesNotNeedCleanup(c)
   589  
   590  	// destroy machine and run cleanups; the filesystem attachment
   591  	// should be marked dying, but the volume attachment should not
   592  	// since it contains the filesystem.
   593  	err = machine.Destroy()
   594  	c.Assert(err, jc.ErrorIsNil)
   595  	s.assertCleanupRuns(c)
   596  
   597  	fas, err := s.State.MachineFilesystemAttachments(machine.MachineTag())
   598  	c.Assert(err, jc.ErrorIsNil)
   599  	c.Assert(fas, gc.HasLen, 1)
   600  	c.Assert(fas[0].Life(), gc.Equals, state.Dying)
   601  	vas, err := s.State.MachineVolumeAttachments(machine.MachineTag())
   602  	c.Assert(err, jc.ErrorIsNil)
   603  	c.Assert(vas, gc.HasLen, 1)
   604  	c.Assert(vas[0].Life(), gc.Equals, state.Alive)
   605  
   606  	// check no cleanups
   607  	s.assertDoesNotNeedCleanup(c)
   608  }
   609  
   610  func (s *CleanupSuite) TestCleanupVolumeAttachments(c *gc.C) {
   611  	_, err := s.State.AddOneMachine(state.MachineTemplate{
   612  		Series: "quantal",
   613  		Jobs:   []state.MachineJob{state.JobHostUnits},
   614  		Volumes: []state.MachineVolumeParams{{
   615  			Volume: state.VolumeParams{Pool: "loop", Size: 1024},
   616  		}},
   617  	})
   618  	c.Assert(err, jc.ErrorIsNil)
   619  	s.assertDoesNotNeedCleanup(c)
   620  
   621  	err = s.State.DestroyVolume(names.NewVolumeTag("0/0"))
   622  	c.Assert(err, jc.ErrorIsNil)
   623  	s.assertCleanupRuns(c)
   624  
   625  	attachment, err := s.State.VolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0"))
   626  	c.Assert(err, jc.ErrorIsNil)
   627  	c.Assert(attachment.Life(), gc.Equals, state.Dying)
   628  }
   629  
   630  func (s *CleanupSuite) TestCleanupFilesystemAttachments(c *gc.C) {
   631  	_, err := s.State.AddOneMachine(state.MachineTemplate{
   632  		Series: "quantal",
   633  		Jobs:   []state.MachineJob{state.JobHostUnits},
   634  		Filesystems: []state.MachineFilesystemParams{{
   635  			Filesystem: state.FilesystemParams{Pool: "rootfs", Size: 1024},
   636  		}},
   637  	})
   638  	c.Assert(err, jc.ErrorIsNil)
   639  	s.assertDoesNotNeedCleanup(c)
   640  
   641  	err = s.State.DestroyFilesystem(names.NewFilesystemTag("0/0"))
   642  	c.Assert(err, jc.ErrorIsNil)
   643  	s.assertCleanupRuns(c)
   644  
   645  	attachment, err := s.State.FilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("0/0"))
   646  	c.Assert(err, jc.ErrorIsNil)
   647  	c.Assert(attachment.Life(), gc.Equals, state.Dying)
   648  }
   649  
   650  func (s *CleanupSuite) TestNothingToCleanup(c *gc.C) {
   651  	s.assertDoesNotNeedCleanup(c)
   652  	s.assertCleanupRuns(c)
   653  	s.assertDoesNotNeedCleanup(c)
   654  }
   655  
   656  func (s *CleanupSuite) assertCleanupRuns(c *gc.C) {
   657  	err := s.State.Cleanup()
   658  	c.Assert(err, jc.ErrorIsNil)
   659  }
   660  
   661  func (s *CleanupSuite) assertNeedsCleanup(c *gc.C) {
   662  	actual, err := s.State.NeedsCleanup()
   663  	c.Assert(err, jc.ErrorIsNil)
   664  	c.Assert(actual, jc.IsTrue)
   665  }
   666  
   667  func (s *CleanupSuite) assertDoesNotNeedCleanup(c *gc.C) {
   668  	actual, err := s.State.NeedsCleanup()
   669  	c.Assert(err, jc.ErrorIsNil)
   670  	c.Assert(actual, jc.IsFalse)
   671  }
   672  
   673  // assertCleanupCount is useful because certain cleanups cause other cleanups
   674  // to be queued; it makes more sense to just run cleanup again than to unpick
   675  // object destruction so that we run the cleanups inline while running cleanups.
   676  func (s *CleanupSuite) assertCleanupCount(c *gc.C, count int) {
   677  	for i := 0; i < count; i++ {
   678  		c.Logf("checking cleanups %d", i)
   679  		s.assertNeedsCleanup(c)
   680  		s.assertCleanupRuns(c)
   681  	}
   682  	s.assertDoesNotNeedCleanup(c)
   683  }