github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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  	"sort"
     9  	"strconv"
    10  	"time"
    11  
    12  	"github.com/juju/charm/v12"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/names/v5"
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	"github.com/juju/juju/caas"
    19  	k8sprovider "github.com/juju/juju/caas/kubernetes/provider"
    20  	k8stesting "github.com/juju/juju/caas/kubernetes/provider/testing"
    21  	"github.com/juju/juju/core/constraints"
    22  	"github.com/juju/juju/core/crossmodel"
    23  	"github.com/juju/juju/core/instance"
    24  	resourcetesting "github.com/juju/juju/core/resources/testing"
    25  	"github.com/juju/juju/core/status"
    26  	"github.com/juju/juju/state"
    27  	"github.com/juju/juju/state/stateenvirons"
    28  	"github.com/juju/juju/state/storage"
    29  	"github.com/juju/juju/state/testing"
    30  	corestorage "github.com/juju/juju/storage"
    31  	"github.com/juju/juju/testing/factory"
    32  )
    33  
    34  type CleanupSuite struct {
    35  	ConnSuite
    36  	storageBackend *state.StorageBackend
    37  }
    38  
    39  var _ = gc.Suite(&CleanupSuite{})
    40  
    41  func (s *CleanupSuite) SetUpTest(c *gc.C) {
    42  	s.ConnSuite.SetUpTest(c)
    43  	s.assertDoesNotNeedCleanup(c)
    44  	var err error
    45  	s.storageBackend, err = state.NewStorageBackend(s.State)
    46  	c.Assert(err, jc.ErrorIsNil)
    47  
    48  }
    49  
    50  func (s *CleanupSuite) TestCleanupDyingApplicationNoUnits(c *gc.C) {
    51  	mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
    52  	c.Assert(mysql.Destroy(), jc.ErrorIsNil)
    53  	c.Assert(mysql.Refresh(), jc.Satisfies, errors.IsNotFound)
    54  }
    55  
    56  func (s *CleanupSuite) TestCleanupDyingApplicationUnits(c *gc.C) {
    57  	// Create a application with some units.
    58  	mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
    59  	units := make([]*state.Unit, 3)
    60  	for i := range units {
    61  		unit, err := mysql.AddUnit(state.AddUnitParams{})
    62  		c.Assert(err, jc.ErrorIsNil)
    63  		units[i] = unit
    64  	}
    65  	preventUnitDestroyRemove(c, units[0])
    66  	s.assertDoesNotNeedCleanup(c)
    67  
    68  	// Destroy the application and check the units are unaffected, but a cleanup
    69  	// has been scheduled.
    70  	err := mysql.Destroy()
    71  	c.Assert(err, jc.ErrorIsNil)
    72  	for _, unit := range units {
    73  		err := unit.Refresh()
    74  		c.Assert(err, jc.ErrorIsNil)
    75  	}
    76  	s.assertNeedsCleanup(c)
    77  
    78  	// Run the cleanup, and check that units are all destroyed as appropriate.
    79  	s.assertCleanupRuns(c)
    80  	err = units[0].Refresh()
    81  	c.Assert(err, jc.ErrorIsNil)
    82  	c.Assert(units[0].Life(), gc.Equals, state.Dying)
    83  	err = units[1].Refresh()
    84  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    85  	err = units[2].Refresh()
    86  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    87  
    88  	// Run a final cleanup to clear the cleanup scheduled for the unit that
    89  	// became dying.
    90  	s.assertCleanupCount(c, 1)
    91  }
    92  
    93  func (s *CleanupSuite) TestCleanupDyingApplicationCharm(c *gc.C) {
    94  	// Create a application and a charm.
    95  	ch := s.AddTestingCharm(c, "mysql")
    96  	mysql := s.AddTestingApplication(c, "mysql", ch)
    97  
    98  	// Create a dummy archive blob.
    99  	stor := storage.NewStorage(s.State.ModelUUID(), s.State.MongoSession())
   100  	storagePath := "dummy-path"
   101  	err := stor.Put(storagePath, bytes.NewReader([]byte("data")), 4)
   102  	c.Assert(err, jc.ErrorIsNil)
   103  
   104  	// Destroy the application and check that a cleanup has been scheduled.
   105  	err = mysql.Destroy()
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	s.assertNeedsCleanup(c)
   108  
   109  	// Run the cleanup, and check that the charm is removed.
   110  	s.assertCleanupRuns(c)
   111  	_, _, err = stor.Get(storagePath)
   112  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   113  }
   114  
   115  func (s *CleanupSuite) TestCleanupRemoteApplication(c *gc.C) {
   116  	app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{
   117  		Name:        "remote-app",
   118  		SourceModel: names.NewModelTag("test"),
   119  		Token:       "token",
   120  	})
   121  	c.Assert(err, jc.ErrorIsNil)
   122  
   123  	err = app.Destroy()
   124  	c.Assert(err, jc.ErrorIsNil)
   125  	// Removed immediately since there are no relations yet.
   126  	s.assertDoesNotNeedCleanup(c)
   127  	_, err = s.State.RemoteApplication("remote-app")
   128  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   129  }
   130  
   131  func (s *CleanupSuite) TestCleanupRemoteApplicationWithRelations(c *gc.C) {
   132  	mysqlEps := []charm.Relation{
   133  		{
   134  			Interface: "mysql",
   135  			Name:      "db",
   136  			Role:      charm.RoleProvider,
   137  			Scope:     charm.ScopeGlobal,
   138  		},
   139  	}
   140  	remoteApp, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{
   141  		Name:        "mysql",
   142  		SourceModel: s.Model.ModelTag(),
   143  		Token:       "t0",
   144  		Endpoints:   mysqlEps,
   145  	})
   146  	c.Assert(err, jc.ErrorIsNil)
   147  
   148  	wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   149  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
   150  	c.Assert(err, jc.ErrorIsNil)
   151  	_, err = s.State.AddRelation(eps[0], eps[1])
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	c.Assert(remoteApp.Refresh(), jc.ErrorIsNil)
   154  	c.Assert(wordpress.Refresh(), jc.ErrorIsNil)
   155  
   156  	err = remoteApp.Destroy()
   157  	c.Assert(err, jc.ErrorIsNil)
   158  	s.assertNeedsCleanup(c)
   159  
   160  	// Run the cleanup, and check that the remote app is removed.
   161  	s.assertCleanupRuns(c)
   162  	_, err = s.State.RemoteApplication("mysql")
   163  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   164  }
   165  
   166  func (s *CleanupSuite) TestCleanupControllerModels(c *gc.C) {
   167  	s.assertDoesNotNeedCleanup(c)
   168  
   169  	// Create a non-empty hosted model.
   170  	otherSt := s.Factory.MakeModel(c, nil)
   171  	defer otherSt.Close()
   172  	factory.NewFactory(otherSt, s.StatePool).MakeApplication(c, nil)
   173  	otherModel, err := otherSt.Model()
   174  	c.Assert(err, jc.ErrorIsNil)
   175  
   176  	s.assertDoesNotNeedCleanup(c)
   177  
   178  	// Destroy the controller and check the model is unaffected, but a
   179  	// cleanup for the model and applications has been scheduled.
   180  	controllerModel, err := s.State.Model()
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	err = controllerModel.Destroy(state.DestroyModelParams{
   183  		DestroyHostedModels: true,
   184  	})
   185  	c.Assert(err, jc.ErrorIsNil)
   186  
   187  	// Two cleanups should be scheduled. One to destroy the hosted
   188  	// models, the other to destroy the controller model's
   189  	// applications.
   190  	s.assertCleanupCount(c, 1)
   191  	err = otherModel.Refresh()
   192  	c.Assert(err, jc.ErrorIsNil)
   193  	c.Assert(otherModel.Life(), gc.Equals, state.Dying)
   194  
   195  	s.assertDoesNotNeedCleanup(c)
   196  }
   197  
   198  func (s *CleanupSuite) TestCleanupModelMachinesForce(c *gc.C) {
   199  	s.testCleanupModelMachines(c, true)
   200  }
   201  
   202  func (s *CleanupSuite) TestCleanupModelMachines(c *gc.C) {
   203  	s.testCleanupModelMachines(c, false)
   204  }
   205  
   206  func (s *CleanupSuite) testCleanupModelMachines(c *gc.C, force bool) {
   207  	// Create a controller machine, and manual and non-manual
   208  	// workload machine, the latter with a container workload machine.
   209  	stateMachine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobManageModel)
   210  	c.Assert(err, jc.ErrorIsNil)
   211  	modelMachine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   212  	c.Assert(err, jc.ErrorIsNil)
   213  	container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{
   214  		Base: state.UbuntuBase("12.10"),
   215  		Jobs: []state.MachineJob{state.JobHostUnits},
   216  	}, modelMachine.Id(), instance.LXD)
   217  	c.Assert(err, jc.ErrorIsNil)
   218  	manualMachine, err := s.State.AddOneMachine(state.MachineTemplate{
   219  		Base:       state.UbuntuBase("12.10"),
   220  		Jobs:       []state.MachineJob{state.JobHostUnits},
   221  		InstanceId: "inst-ance",
   222  		Nonce:      "manual:foo",
   223  	})
   224  	c.Assert(err, jc.ErrorIsNil)
   225  
   226  	// Create a relation with a unit in scope and assigned to the hosted machine.
   227  	pr := newPeerRelation(c, s.State)
   228  	err = pr.u0.AssignToMachine(modelMachine)
   229  	c.Assert(err, jc.ErrorIsNil)
   230  	preventPeerUnitsDestroyRemove(c, pr)
   231  
   232  	err = pr.ru0.EnterScope(nil)
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	s.assertDoesNotNeedCleanup(c)
   235  
   236  	// Destroy model, check cleanup queued.
   237  	model, err := s.State.Model()
   238  	c.Assert(err, jc.ErrorIsNil)
   239  	err = model.Destroy(state.DestroyModelParams{Force: &force})
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	s.assertNeedsCleanup(c)
   242  
   243  	// Clean up, and check that the unit has been removed...
   244  	if force {
   245  		// There are 4 jobs for the destroy and then the model
   246  		// cleanup task queues another set because force is used.
   247  		s.assertCleanupCountDirty(c, 4)
   248  		assertRemoved(c, pr.u0)
   249  		// ...and the unit has departed relation scope...
   250  		assertNotJoined(c, pr.ru0)
   251  		// ...and the machine has been removed (since model destroy does a
   252  		// force-destroy on the machine).
   253  		c.Assert(modelMachine.Refresh(), jc.Satisfies, errors.IsNotFound)
   254  		c.Assert(container.Refresh(), jc.Satisfies, errors.IsNotFound)
   255  	} else {
   256  		// Without force, in this test, the machines are not marked Dead,
   257  		// as no call is made to EnsureDead here, but from the machiner.
   258  		s.assertCleanupCountDirty(c, 2)
   259  		assertLife(c, modelMachine, state.Dying)
   260  		assertLife(c, container, state.Dying)
   261  	}
   262  
   263  	assertLife(c, manualMachine, state.Dying)
   264  	assertLife(c, stateMachine, state.Alive)
   265  }
   266  
   267  func (s *CleanupSuite) TestCleanupModelApplications(c *gc.C) {
   268  	s.assertDoesNotNeedCleanup(c)
   269  
   270  	// Create a application with some units.
   271  	mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
   272  	units := make([]*state.Unit, 3)
   273  	for i := range units {
   274  		unit, err := mysql.AddUnit(state.AddUnitParams{})
   275  		c.Assert(err, jc.ErrorIsNil)
   276  		units[i] = unit
   277  	}
   278  	s.assertDoesNotNeedCleanup(c)
   279  
   280  	// Destroy the model and check the application and units are
   281  	// unaffected, but a cleanup for the application has been scheduled.
   282  	model, err := s.State.Model()
   283  	c.Assert(err, jc.ErrorIsNil)
   284  	err = model.Destroy(state.DestroyModelParams{})
   285  	c.Assert(err, jc.ErrorIsNil)
   286  	s.assertNeedsCleanup(c)
   287  	s.assertCleanupRuns(c)
   288  	err = mysql.Refresh()
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	c.Assert(mysql.Life(), gc.Equals, state.Dying)
   291  	for _, unit := range units {
   292  		err = unit.Refresh()
   293  		c.Assert(err, jc.ErrorIsNil)
   294  		c.Assert(unit.Life(), gc.Equals, state.Alive)
   295  	}
   296  
   297  	// The first cleanup removes the units, which schedules
   298  	// the application to be removed. This removes the application
   299  	// queing up a change for actions and charms.
   300  	s.assertCleanupCount(c, 3)
   301  	for _, unit := range units {
   302  		err = unit.Refresh()
   303  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
   304  	}
   305  
   306  	// Now we should have all the cleanups done
   307  	s.assertDoesNotNeedCleanup(c)
   308  }
   309  
   310  func (s *CleanupSuite) TestCleanupModelOffers(c *gc.C) {
   311  	wordpressEps := []charm.Relation{
   312  		{
   313  			Interface: "mysql",
   314  			Name:      "database",
   315  			Role:      charm.RoleRequirer,
   316  			Scope:     charm.ScopeGlobal,
   317  		},
   318  	}
   319  	remoteApp, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{
   320  		Name:            "remote-wordpress",
   321  		SourceModel:     s.Model.ModelTag(),
   322  		IsConsumerProxy: true,
   323  		Token:           "t0",
   324  		Endpoints:       wordpressEps,
   325  	})
   326  	c.Assert(err, jc.ErrorIsNil)
   327  
   328  	mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
   329  	units := make([]*state.Unit, 3)
   330  	for i := range units {
   331  		unit, err := mysql.AddUnit(state.AddUnitParams{})
   332  		c.Assert(err, jc.ErrorIsNil)
   333  		units[i] = unit
   334  	}
   335  
   336  	eps, err := s.State.InferEndpoints("remote-wordpress", "mysql")
   337  	c.Assert(err, jc.ErrorIsNil)
   338  	rel, err := s.State.AddRelation(eps[0], eps[1])
   339  	c.Assert(err, jc.ErrorIsNil)
   340  	c.Assert(remoteApp.Refresh(), jc.ErrorIsNil)
   341  	c.Assert(mysql.Refresh(), jc.ErrorIsNil)
   342  
   343  	// Prevent short circuit of offer removal.
   344  	ru, err := rel.Unit(units[0])
   345  	c.Assert(err, jc.ErrorIsNil)
   346  	err = ru.EnterScope(nil)
   347  	c.Assert(err, jc.ErrorIsNil)
   348  
   349  	offers := state.NewApplicationOffers(s.State)
   350  	offer, err := offers.AddOffer(crossmodel.AddApplicationOfferArgs{
   351  		OfferName:       "hosted-mysql",
   352  		ApplicationName: "mysql",
   353  		Endpoints:       map[string]string{"database": "server"},
   354  		Owner:           s.Owner.Name(),
   355  	})
   356  	c.Assert(err, jc.ErrorIsNil)
   357  
   358  	_, err = s.State.AddOfferConnection(state.AddOfferConnectionParams{
   359  		OfferUUID:       offer.OfferUUID,
   360  		RelationId:      rel.Id(),
   361  		RelationKey:     rel.Tag().Id(),
   362  		Username:        s.Owner.Name(),
   363  		SourceModelUUID: s.State.ModelUUID(),
   364  	})
   365  	c.Assert(err, jc.ErrorIsNil)
   366  
   367  	s.assertDoesNotNeedCleanup(c)
   368  
   369  	// Destroy the model and check the application and units are
   370  	// unaffected, but a cleanup for the application has been scheduled,
   371  	// and the offer has been removed.
   372  	model, err := s.State.Model()
   373  	c.Assert(err, jc.ErrorIsNil)
   374  	err = model.Destroy(state.DestroyModelParams{})
   375  	c.Assert(err, jc.ErrorIsNil)
   376  	s.assertNeedsCleanup(c)
   377  	s.assertCleanupRuns(c)
   378  	err = mysql.Refresh()
   379  	c.Assert(err, jc.ErrorIsNil)
   380  	c.Assert(mysql.Life(), gc.Equals, state.Dying)
   381  	for _, unit := range units {
   382  		err = unit.Refresh()
   383  		c.Assert(err, jc.ErrorIsNil)
   384  		c.Assert(unit.Life(), gc.Equals, state.Alive)
   385  	}
   386  
   387  	s.assertCleanupCount(c, 3)
   388  	allOffers, err := offers.ListOffers()
   389  	c.Assert(err, jc.ErrorIsNil)
   390  	c.Assert(allOffers, gc.HasLen, 0)
   391  	_, err = s.State.RemoteApplication("remote-wordpress")
   392  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   393  }
   394  
   395  func (s *CleanupSuite) TestCleanupRelationSettings(c *gc.C) {
   396  	// Create a relation with a unit in scope.
   397  	pr := newPeerRelation(c, s.State)
   398  	preventPeerUnitsDestroyRemove(c, pr)
   399  	rel := pr.ru0.Relation()
   400  	err := pr.ru0.EnterScope(map[string]interface{}{"some": "settings"})
   401  	c.Assert(err, jc.ErrorIsNil)
   402  	s.assertDoesNotNeedCleanup(c)
   403  
   404  	// Destroy the application, check the relation's still around.
   405  	err = pr.app.Destroy()
   406  	c.Assert(err, jc.ErrorIsNil)
   407  	s.assertCleanupCount(c, 2)
   408  	err = rel.Refresh()
   409  	c.Assert(err, jc.ErrorIsNil)
   410  	c.Assert(rel.Life(), gc.Equals, state.Dying)
   411  
   412  	// The unit leaves scope, triggering relation removal.
   413  	err = pr.ru0.LeaveScope()
   414  	c.Assert(err, jc.ErrorIsNil)
   415  	s.assertNeedsCleanup(c)
   416  
   417  	// Settings are not destroyed yet...
   418  	settings, err := pr.ru1.ReadSettings("riak/0")
   419  	c.Assert(err, jc.ErrorIsNil)
   420  	c.Assert(settings, gc.DeepEquals, map[string]interface{}{"some": "settings"})
   421  
   422  	// ...but they are on cleanup.
   423  	s.assertCleanupCount(c, 1)
   424  	_, err = pr.ru1.ReadSettings("riak/0")
   425  	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": unit "riak/0": settings not found`)
   426  }
   427  
   428  func (s *CleanupSuite) TestCleanupModelBranches(c *gc.C) {
   429  	s.assertDoesNotNeedCleanup(c)
   430  
   431  	// Create a branch.
   432  	c.Assert(s.Model.AddBranch(newBranchName, newBranchCreator), jc.ErrorIsNil)
   433  	branches, err := s.State.Branches()
   434  	c.Assert(err, jc.ErrorIsNil)
   435  	c.Check(branches, gc.HasLen, 1)
   436  	s.assertDoesNotNeedCleanup(c)
   437  
   438  	// Destroy the model and check the branches unaffected, but a cleanup for
   439  	// the branches has been scheduled.
   440  	model, err := s.State.Model()
   441  	c.Assert(err, jc.ErrorIsNil)
   442  	err = model.Destroy(state.DestroyModelParams{})
   443  	c.Assert(err, jc.ErrorIsNil)
   444  	s.assertNeedsCleanup(c)
   445  	s.assertCleanupCount(c, 1)
   446  
   447  	s.assertCleanupRuns(c)
   448  	err = model.Refresh()
   449  	c.Assert(err, jc.ErrorIsNil)
   450  	s.assertCleanupCount(c, 0)
   451  
   452  	_, err = s.Model.Branch(newBranchName)
   453  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   454  
   455  	branches, err = s.State.Branches()
   456  	c.Assert(err, jc.ErrorIsNil)
   457  	c.Check(branches, gc.HasLen, 0)
   458  
   459  	// Now we should have all the cleanups done
   460  	s.assertDoesNotNeedCleanup(c)
   461  }
   462  
   463  func (s *CleanupSuite) TestDestroyControllerMachineErrors(c *gc.C) {
   464  	manager, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobManageModel)
   465  	c.Assert(err, jc.ErrorIsNil)
   466  	node, err := s.State.ControllerNode(manager.Id())
   467  	c.Assert(err, jc.ErrorIsNil)
   468  	node.SetHasVote(true)
   469  	c.Assert(err, jc.ErrorIsNil)
   470  	s.assertDoesNotNeedCleanup(c)
   471  	err = manager.Destroy()
   472  	c.Assert(err, gc.ErrorMatches, "controller 0 is the only controller")
   473  	s.assertDoesNotNeedCleanup(c)
   474  	assertLife(c, manager, state.Alive)
   475  }
   476  
   477  func (s *CleanupSuite) TestDestroyControllerMachineHAWithControllerCharm(c *gc.C) {
   478  	cons := constraints.Value{
   479  		Mem: newUint64(100),
   480  	}
   481  	changes, err := s.State.EnableHA(3, cons, state.UbuntuBase("12.04"), nil)
   482  	c.Assert(err, jc.ErrorIsNil)
   483  	c.Assert(changes.Added, gc.HasLen, 3)
   484  
   485  	ch := s.AddTestingCharmWithSeries(c, "juju-controller", "")
   486  	app := s.AddTestingApplicationForBase(c, state.UbuntuBase("12.04"), "controller", ch)
   487  
   488  	var machines []*state.Machine
   489  	var units []*state.Unit
   490  	for i := 0; i < 3; i++ {
   491  		m, err := s.State.Machine(strconv.Itoa(i))
   492  		c.Assert(err, jc.ErrorIsNil)
   493  		c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{
   494  			state.JobHostUnits,
   495  			state.JobManageModel,
   496  		})
   497  
   498  		if i == 0 {
   499  			node, err := s.State.ControllerNode(m.Id())
   500  			c.Assert(err, jc.ErrorIsNil)
   501  			node.SetHasVote(true)
   502  		}
   503  
   504  		u, err := app.AddUnit(state.AddUnitParams{})
   505  		c.Assert(err, jc.ErrorIsNil)
   506  		err = u.SetCharmURL(ch.URL())
   507  		c.Assert(err, jc.ErrorIsNil)
   508  		err = u.AssignToMachine(m)
   509  		c.Assert(err, jc.ErrorIsNil)
   510  
   511  		machines = append(machines, m)
   512  		units = append(units, u)
   513  	}
   514  
   515  	for _, m := range machines {
   516  		assertLife(c, m, state.Alive)
   517  	}
   518  	for _, u := range units {
   519  		assertLife(c, u, state.Alive)
   520  	}
   521  
   522  	s.assertDoesNotNeedCleanup(c)
   523  	err = machines[2].Destroy()
   524  	c.Assert(err, jc.ErrorIsNil)
   525  
   526  	s.assertNeedsCleanup(c)
   527  	s.assertNextCleanup(c, "evacuateMachine(2)")
   528  	s.assertNeedsCleanup(c)
   529  	s.assertNextCleanup(c, `removedUnit("controller/2")`)
   530  	s.assertNeedsCleanup(c)
   531  	s.assertNextCleanup(c, "evacuateMachine(2)")
   532  	s.assertDoesNotNeedCleanup(c)
   533  }
   534  
   535  const dontWait = time.Duration(0)
   536  
   537  func (s *CleanupSuite) TestCleanupForceDestroyedMachineUnit(c *gc.C) {
   538  	// Create a machine.
   539  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   540  	c.Assert(err, jc.ErrorIsNil)
   541  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   542  	c.Assert(err, jc.ErrorIsNil)
   543  
   544  	// Create a relation with a unit in scope and assigned to the machine.
   545  	pr := newPeerRelation(c, s.State)
   546  	err = pr.u0.AssignToMachine(machine)
   547  	c.Assert(err, jc.ErrorIsNil)
   548  	preventPeerUnitsDestroyRemove(c, pr)
   549  	err = pr.ru0.EnterScope(nil)
   550  	c.Assert(err, jc.ErrorIsNil)
   551  	s.assertDoesNotNeedCleanup(c)
   552  
   553  	// Force machine destruction, check cleanup queued.
   554  	err = machine.ForceDestroy(time.Minute)
   555  	c.Assert(err, jc.ErrorIsNil)
   556  	s.assertNeedsCleanup(c)
   557  
   558  	// Clean up, and check that the unit has been removed...
   559  	s.assertCleanupCountDirty(c, 2)
   560  	assertRemoved(c, pr.u0)
   561  
   562  	// ...and the unit has departed relation scope...
   563  	assertNotJoined(c, pr.ru0)
   564  
   565  	// ...but that the machine remains, and is Dead, ready for removal by the
   566  	// provisioner.
   567  	assertLife(c, machine, state.Dead)
   568  }
   569  
   570  func (s *CleanupSuite) TestCleanupForceDestroyedControllerMachine(c *gc.C) {
   571  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobManageModel)
   572  	c.Assert(err, jc.ErrorIsNil)
   573  	node, err := s.State.ControllerNode(machine.Id())
   574  	c.Assert(err, jc.ErrorIsNil)
   575  	err = node.SetHasVote(true)
   576  	c.Assert(err, jc.ErrorIsNil)
   577  	changes, err := s.State.EnableHA(3, constraints.Value{}, state.UbuntuBase("12.04"), nil)
   578  	c.Assert(err, jc.ErrorIsNil)
   579  	c.Check(changes.Added, gc.HasLen, 2)
   580  	c.Check(changes.Removed, gc.HasLen, 0)
   581  	c.Check(changes.Maintained, gc.HasLen, 1)
   582  	c.Check(changes.Converted, gc.HasLen, 0)
   583  	for _, mid := range changes.Added {
   584  		m, err := s.State.Machine(mid)
   585  		c.Assert(err, jc.ErrorIsNil)
   586  		node, err := s.State.ControllerNode(m.Id())
   587  		c.Assert(err, jc.ErrorIsNil)
   588  		c.Assert(node.SetHasVote(true), jc.ErrorIsNil)
   589  	}
   590  	s.assertDoesNotNeedCleanup(c)
   591  	err = machine.ForceDestroy(time.Minute)
   592  	c.Assert(err, jc.ErrorIsNil)
   593  	// The machine should no longer want the vote, should be forced to not have the vote, and forced to not be a
   594  	// controller member anymore
   595  	c.Assert(machine.Refresh(), jc.ErrorIsNil)
   596  	c.Check(machine.Life(), gc.Equals, state.Dying)
   597  	node, err = s.State.ControllerNode(machine.Id())
   598  	c.Assert(err, jc.ErrorIsNil)
   599  	c.Check(node.WantsVote(), jc.IsFalse)
   600  	c.Check(node.HasVote(), jc.IsTrue)
   601  	c.Check(machine.Jobs(), jc.DeepEquals, []state.MachineJob{state.JobManageModel})
   602  	controllerIds, err := s.State.ControllerIds()
   603  	c.Assert(err, jc.ErrorIsNil)
   604  	c.Check(controllerIds, gc.DeepEquals, append([]string{machine.Id()}, changes.Added...))
   605  	// ForceDestroy still won't kill the controller if it is flagged as having a vote
   606  	// We don't see the error because it is logged, but not returned.
   607  	s.assertCleanupRuns(c)
   608  	c.Assert(node.SetHasVote(false), jc.ErrorIsNil)
   609  	// However, if we remove the vote, it can be cleaned up.
   610  	// ForceDestroy sets up a cleanupForceDestroyedMachine, which
   611  	// calls advanceLifecycle(Dead) which sets up a
   612  	// cleanupDyingMachine, which in turn creates a delayed
   613  	// cleanupForceRemoveMachine.
   614  	// Run the first two.
   615  	s.assertCleanupCountDirty(c, 2)
   616  	// After we've run the cleanup for the controller machine, the machine should be dead, and it should not be
   617  	// present in the other documents.
   618  	assertLife(c, machine, state.Dead)
   619  	controllerIds, err = s.State.ControllerIds()
   620  	c.Assert(err, jc.ErrorIsNil)
   621  	sort.Strings(controllerIds)
   622  	sort.Strings(changes.Added)
   623  	// Only the machines that were added should still be part of the controller
   624  	c.Check(controllerIds, gc.DeepEquals, changes.Added)
   625  }
   626  
   627  func (s *CleanupSuite) TestCleanupForceDestroyMachineCleansStorageAttachments(c *gc.C) {
   628  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   629  	c.Assert(err, jc.ErrorIsNil)
   630  	s.assertDoesNotNeedCleanup(c)
   631  
   632  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   633  	c.Assert(err, jc.ErrorIsNil)
   634  
   635  	ch := s.AddTestingCharm(c, "storage-block")
   636  	storage := map[string]state.StorageConstraints{
   637  		"data": makeStorageCons("loop", 1024, 1),
   638  	}
   639  	application := s.AddTestingApplicationWithStorage(c, "storage-block", ch, storage)
   640  	u, err := application.AddUnit(state.AddUnitParams{})
   641  	c.Assert(err, jc.ErrorIsNil)
   642  	err = u.AssignToMachine(machine)
   643  	c.Assert(err, jc.ErrorIsNil)
   644  
   645  	// check no cleanups
   646  	s.assertDoesNotNeedCleanup(c)
   647  
   648  	// this tag matches the storage instance created for the unit above.
   649  	storageTag := names.NewStorageTag("data/0")
   650  
   651  	sa, err := s.storageBackend.StorageAttachment(storageTag, u.UnitTag())
   652  	c.Assert(err, jc.ErrorIsNil)
   653  	c.Assert(sa.Life(), gc.Equals, state.Alive)
   654  
   655  	// destroy machine and run cleanups
   656  	err = machine.ForceDestroy(time.Minute)
   657  	c.Assert(err, jc.ErrorIsNil)
   658  
   659  	// Run cleanups to remove the unit and make the machine dead.
   660  	s.assertCleanupCountDirty(c, 2)
   661  
   662  	// After running the cleanups, the storage attachment should
   663  	// have been removed; the storage instance should be floating,
   664  	// and will be removed along with the machine.
   665  	_, err = s.storageBackend.StorageAttachment(storageTag, u.UnitTag())
   666  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   667  	si, err := s.storageBackend.StorageInstance(storageTag)
   668  	c.Assert(err, jc.ErrorIsNil)
   669  	_, hasOwner := si.Owner()
   670  	c.Assert(hasOwner, jc.IsFalse)
   671  
   672  	// Check that the unit has been removed.
   673  	assertRemoved(c, u)
   674  
   675  	s.Clock.Advance(time.Minute)
   676  	// Check that the last cleanup to remove the machine runs.
   677  	s.assertCleanupCount(c, 1)
   678  }
   679  
   680  func (s *CleanupSuite) TestCleanupForceDestroyedMachineWithContainer(c *gc.C) {
   681  	// Create a machine with a container.
   682  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   683  	c.Assert(err, jc.ErrorIsNil)
   684  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   685  	c.Assert(err, jc.ErrorIsNil)
   686  	container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{
   687  		Base: state.UbuntuBase("12.10"),
   688  		Jobs: []state.MachineJob{state.JobHostUnits},
   689  	}, machine.Id(), instance.LXD)
   690  	c.Assert(err, jc.ErrorIsNil)
   691  	err = container.SetProvisioned("inst-id", "", "fake_nonce", nil)
   692  	c.Assert(err, jc.ErrorIsNil)
   693  
   694  	// Create active units (in relation scope, with subordinates).
   695  	prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeContainer, machine, container)
   696  	prr.allEnterScope(c)
   697  
   698  	preventProReqUnitsDestroyRemove(c, prr)
   699  	s.assertDoesNotNeedCleanup(c)
   700  
   701  	// Force removal of the top-level machine.
   702  	err = machine.ForceDestroy(time.Minute)
   703  	c.Assert(err, jc.ErrorIsNil)
   704  	s.assertNeedsCleanup(c)
   705  
   706  	// And do it again, just to check that the second cleanup doc for the same
   707  	// machine doesn't cause problems down the line.
   708  	err = machine.ForceDestroy(time.Minute)
   709  	c.Assert(err, jc.ErrorIsNil)
   710  	s.assertNeedsCleanup(c)
   711  
   712  	// Clean up, and check that the container has been removed...
   713  	s.assertCleanupCountDirty(c, 2)
   714  	err = container.Refresh()
   715  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   716  
   717  	// ...and so have all the units...
   718  	assertRemoved(c, prr.pu0)
   719  	assertRemoved(c, prr.pu1)
   720  	assertRemoved(c, prr.ru0)
   721  	assertRemoved(c, prr.ru1)
   722  
   723  	// ...and none of the units have left relation scopes occupied...
   724  	assertNotInScope(c, prr.pru0)
   725  	assertNotInScope(c, prr.pru1)
   726  	assertNotInScope(c, prr.rru0)
   727  	assertNotInScope(c, prr.rru1)
   728  
   729  	// ...but that the machine remains, and is Dead, ready for removal by the
   730  	// provisioner.
   731  	assertLife(c, machine, state.Dead)
   732  }
   733  
   734  func (s *CleanupSuite) TestForceDestroyMachineSchedulesRemove(c *gc.C) {
   735  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   736  	c.Assert(err, jc.ErrorIsNil)
   737  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   738  	c.Assert(err, jc.ErrorIsNil)
   739  
   740  	s.assertDoesNotNeedCleanup(c)
   741  
   742  	err = machine.ForceDestroy(time.Minute)
   743  	c.Assert(err, jc.ErrorIsNil)
   744  	s.assertNeedsCleanup(c)
   745  
   746  	s.assertCleanupRuns(c)
   747  
   748  	assertLifeIs(c, machine, state.Dead)
   749  
   750  	// Running a cleanup pass succeeds but doesn't get rid of cleanups
   751  	// because there's a scheduled one.
   752  	s.assertCleanupRuns(c)
   753  	assertLifeIs(c, machine, state.Dead)
   754  	s.assertNeedsCleanup(c)
   755  
   756  	s.Clock.Advance(time.Minute)
   757  	s.assertCleanupCount(c, 1)
   758  	err = machine.Refresh()
   759  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   760  }
   761  
   762  func (s *CleanupSuite) TestRemoveApplicationRemovesAllCleanUps(c *gc.C) {
   763  	ch := s.AddTestingCharm(c, "dummy")
   764  	app := s.AddTestingApplication(c, "dummy", ch)
   765  	unit, err := app.AddUnit(state.AddUnitParams{})
   766  	c.Assert(err, jc.ErrorIsNil)
   767  	c.Assert(app.Refresh(), jc.ErrorIsNil)
   768  	c.Assert(app.UnitCount(), gc.Equals, 1)
   769  	s.assertDoesNotNeedCleanup(c)
   770  
   771  	// app `dummyfoo` and its units should not be impacted after app `dummy` was destroyed.
   772  	appfoo := s.AddTestingApplication(c, "dummyfoo", ch)
   773  	unitfoo, err := appfoo.AddUnit(state.AddUnitParams{})
   774  	c.Assert(err, jc.ErrorIsNil)
   775  	c.Assert(appfoo.Refresh(), jc.ErrorIsNil)
   776  	c.Assert(appfoo.UnitCount(), gc.Equals, 1)
   777  	s.assertDoesNotNeedCleanup(c)
   778  
   779  	s.State.ScheduleForceCleanup(state.CleanupForceDestroyedUnit, unit.Name(), 1*time.Minute)
   780  	s.State.ScheduleForceCleanup(state.CleanupForceRemoveUnit, unit.Name(), 1*time.Minute)
   781  	s.State.ScheduleForceCleanup(state.CleanupForceApplication, app.Name(), 1*time.Minute)
   782  	s.assertNeedsCleanup(c)
   783  
   784  	op := app.DestroyOperation()
   785  	op.DestroyStorage = false
   786  	op.Force = true
   787  	op.MaxWait = 1 * time.Minute
   788  	err = s.State.ApplyOperation(op)
   789  	c.Assert(err, jc.ErrorIsNil)
   790  
   791  	s.assertNeedsCleanup(c)
   792  	s.assertCleanupCount(c, 3)
   793  
   794  	c.Assert(unit.Refresh(), jc.Satisfies, errors.IsNotFound)
   795  	c.Assert(app.Refresh(), jc.Satisfies, errors.IsNotFound)
   796  
   797  	c.Assert(unitfoo.Refresh(), jc.ErrorIsNil)
   798  	c.Assert(appfoo.Refresh(), jc.ErrorIsNil)
   799  	c.Assert(appfoo.UnitCount(), gc.Equals, 1)
   800  }
   801  
   802  func (s *CleanupSuite) TestForceDestroyMachineRemovesUpgradeSeriesLock(c *gc.C) {
   803  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   804  	c.Assert(err, jc.ErrorIsNil)
   805  
   806  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   807  	c.Assert(err, jc.ErrorIsNil)
   808  	s.assertDoesNotNeedCleanup(c)
   809  
   810  	err = machine.CreateUpgradeSeriesLock(nil, state.UbuntuBase("16.04"))
   811  	c.Assert(err, jc.ErrorIsNil)
   812  
   813  	err = machine.ForceDestroy(time.Minute)
   814  	c.Assert(err, jc.ErrorIsNil)
   815  	s.assertNeedsCleanup(c)
   816  	s.assertCleanupRuns(c)
   817  
   818  	assertLifeIs(c, machine, state.Dead)
   819  
   820  	// Running a cleanup pass succeeds but doesn't get rid of cleanups
   821  	// because there's a scheduled one.
   822  	s.assertCleanupRuns(c)
   823  	assertLifeIs(c, machine, state.Dead)
   824  	s.assertNeedsCleanup(c)
   825  
   826  	locked, err := machine.IsLockedForSeriesUpgrade()
   827  	c.Assert(err, jc.ErrorIsNil)
   828  	c.Assert(locked, jc.IsFalse)
   829  
   830  	s.Clock.Advance(time.Minute)
   831  	s.assertCleanupCount(c, 1)
   832  	err = machine.Refresh()
   833  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   834  }
   835  
   836  func (s *CleanupSuite) TestDestroyMachineAssertsNoUpgradeSeriesLock(c *gc.C) {
   837  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   838  	c.Assert(err, jc.ErrorIsNil)
   839  
   840  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   841  	c.Assert(err, jc.ErrorIsNil)
   842  	s.assertDoesNotNeedCleanup(c)
   843  
   844  	// Simulate a race by adding a lock after the check has been run,
   845  	// but before the destruction transaction assertions execute.
   846  	defer state.SetBeforeHooks(c, s.State, func() {
   847  		c.Assert(machine.CreateUpgradeSeriesLock(nil, state.UbuntuBase("16.04")), gc.IsNil)
   848  	}).Check()
   849  
   850  	// Check that we get an error, but for the transaction assertion failure,
   851  	// and not for the initial check, which passes.
   852  	err = machine.Destroy()
   853  	c.Assert(err, gc.NotNil)
   854  	c.Assert(err, gc.Not(gc.ErrorMatches), `machine 1 is locked for series upgrade`)
   855  
   856  	assertLifeIs(c, machine, state.Alive)
   857  }
   858  
   859  func (s *CleanupSuite) TestForceDestroyMachineRemovesLinkLayerDevices(c *gc.C) {
   860  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   861  	c.Assert(err, jc.ErrorIsNil)
   862  
   863  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
   864  	c.Assert(err, jc.ErrorIsNil)
   865  	s.assertDoesNotNeedCleanup(c)
   866  
   867  	// Add a NIC with an address.
   868  	ops, err := machine.AddLinkLayerDeviceOps(
   869  		state.LinkLayerDeviceArgs{
   870  			Name: "eth0",
   871  		},
   872  		state.LinkLayerDeviceAddress{
   873  			DeviceName:  "eth0",
   874  			CIDRAddress: "192.168.0.9/24",
   875  		},
   876  	)
   877  	c.Assert(err, jc.ErrorIsNil)
   878  	state.RunTransaction(c, s.State, ops)
   879  
   880  	nics, err := machine.AllLinkLayerDevices()
   881  	c.Assert(err, jc.ErrorIsNil)
   882  	c.Assert(nics, gc.HasLen, 1)
   883  
   884  	err = machine.ForceDestroy(time.Minute)
   885  	c.Assert(err, jc.ErrorIsNil)
   886  	s.assertNeedsCleanup(c)
   887  	s.assertCleanupRuns(c)
   888  
   889  	assertLifeIs(c, machine, state.Dead)
   890  
   891  	// Nics should be gone.
   892  	nics, err = machine.AllLinkLayerDevices()
   893  	c.Assert(err, jc.ErrorIsNil)
   894  	c.Assert(nics, gc.HasLen, 0)
   895  }
   896  
   897  func (s *CleanupSuite) TestCleanupDyingUnit(c *gc.C) {
   898  	// Create active unit, in a relation.
   899  	prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)
   900  	preventProReqUnitsDestroyRemove(c, prr)
   901  	err := prr.pru0.EnterScope(nil)
   902  	c.Assert(err, jc.ErrorIsNil)
   903  
   904  	// Destroy provider unit 0; check it's Dying, and a cleanup has been scheduled.
   905  	err = prr.pu0.Destroy()
   906  	c.Assert(err, jc.ErrorIsNil)
   907  	err = prr.pu0.Refresh()
   908  	c.Assert(err, jc.ErrorIsNil)
   909  	assertLife(c, prr.pu0, state.Dying)
   910  	s.assertNeedsCleanup(c)
   911  
   912  	// Check it's reported in scope until cleaned up.
   913  	assertJoined(c, prr.pru0)
   914  	s.assertCleanupCount(c, 1)
   915  	assertInScope(c, prr.pru0)
   916  	assertNotJoined(c, prr.pru0)
   917  
   918  	// Destroy the relation, and check it sticks around...
   919  	err = prr.rel.Refresh()
   920  	c.Assert(err, jc.ErrorIsNil)
   921  	err = prr.rel.Destroy()
   922  	c.Assert(err, jc.ErrorIsNil)
   923  	assertLife(c, prr.rel, state.Dying)
   924  
   925  	// ...until the unit is removed, and really leaves scope.
   926  	err = prr.pu0.EnsureDead()
   927  	c.Assert(err, jc.ErrorIsNil)
   928  	err = prr.pu0.Remove()
   929  	c.Assert(err, jc.ErrorIsNil)
   930  	assertNotInScope(c, prr.pru0)
   931  	assertRemoved(c, prr.rel)
   932  }
   933  
   934  func (s *CleanupSuite) TestCleanupDyingUnitAlreadyRemoved(c *gc.C) {
   935  	// Create active unit, in a relation.
   936  	prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)
   937  	preventProReqUnitsDestroyRemove(c, prr)
   938  	err := prr.pru0.EnterScope(nil)
   939  	c.Assert(err, jc.ErrorIsNil)
   940  
   941  	// Destroy provider unit 0; check it's Dying, and a cleanup has been scheduled.
   942  	err = prr.pu0.Destroy()
   943  	c.Assert(err, jc.ErrorIsNil)
   944  	err = prr.pu0.Refresh()
   945  	c.Assert(err, jc.ErrorIsNil)
   946  	assertLife(c, prr.pu0, state.Dying)
   947  	s.assertNeedsCleanup(c)
   948  
   949  	// Remove the unit, and the relation.
   950  	err = prr.pu0.EnsureDead()
   951  	c.Assert(err, jc.ErrorIsNil)
   952  	err = prr.pu0.Remove()
   953  	c.Assert(err, jc.ErrorIsNil)
   954  	err = prr.rel.Destroy()
   955  	c.Assert(err, jc.ErrorIsNil)
   956  	assertRemoved(c, prr.rel)
   957  
   958  	// Check the cleanup still runs happily.
   959  	s.assertCleanupCount(c, 1)
   960  	s.assertCleanupRuns(c)
   961  }
   962  
   963  func (s *CleanupSuite) TestCleanupActions(c *gc.C) {
   964  	// Create a application with a unit.
   965  	dummy := s.AddTestingApplication(c, "dummy", s.AddTestingCharm(c, "dummy"))
   966  	unit, err := dummy.AddUnit(state.AddUnitParams{})
   967  	c.Assert(err, jc.ErrorIsNil)
   968  
   969  	// check no cleanups
   970  	s.assertDoesNotNeedCleanup(c)
   971  
   972  	operationID, err := s.Model.EnqueueOperation("a test", 2)
   973  	c.Assert(err, jc.ErrorIsNil)
   974  	// Add a couple actions to the unit
   975  	_, err = s.Model.AddAction(unit, operationID, "snapshot", nil, nil, nil)
   976  	c.Assert(err, jc.ErrorIsNil)
   977  	_, err = s.Model.AddAction(unit, operationID, "snapshot", nil, nil, nil)
   978  	c.Assert(err, jc.ErrorIsNil)
   979  
   980  	// make sure unit still has actions
   981  	actions, err := unit.PendingActions()
   982  	c.Assert(err, jc.ErrorIsNil)
   983  	c.Assert(len(actions), gc.Equals, 2)
   984  
   985  	// destroy unit and run cleanups
   986  	err = dummy.Destroy()
   987  	c.Assert(err, jc.ErrorIsNil)
   988  	s.assertCleanupRuns(c)
   989  
   990  	// make sure unit still has actions, after first cleanup pass
   991  	actions, err = unit.PendingActions()
   992  	c.Assert(err, jc.ErrorIsNil)
   993  	c.Assert(len(actions), gc.Equals, 2)
   994  
   995  	// second cleanup pass
   996  	s.assertCleanupRuns(c)
   997  
   998  	// make sure unit has no actions, after second cleanup pass
   999  	actions, err = unit.PendingActions()
  1000  	c.Assert(err, jc.ErrorIsNil)
  1001  	c.Assert(len(actions), gc.Equals, 0)
  1002  
  1003  	// Application has been cleaned up, but now we cleanup the charm
  1004  	c.Assert(dummy.Refresh(), jc.Satisfies, errors.IsNotFound)
  1005  	s.assertCleanupRuns(c)
  1006  
  1007  	// check no cleanups
  1008  	s.assertDoesNotNeedCleanup(c)
  1009  }
  1010  
  1011  func (s *CleanupSuite) TestCleanupWithCompletedActions(c *gc.C) {
  1012  	for _, status := range []state.ActionStatus{
  1013  		state.ActionCompleted,
  1014  		state.ActionCancelled,
  1015  		state.ActionAborted,
  1016  		state.ActionFailed,
  1017  	} {
  1018  		// Create a application with a unit.
  1019  		dummy := s.AddTestingApplication(c, "dummy", s.AddTestingCharm(c, "dummy"))
  1020  		unit, err := dummy.AddUnit(state.AddUnitParams{})
  1021  		c.Assert(err, jc.ErrorIsNil)
  1022  		s.assertDoesNotNeedCleanup(c)
  1023  
  1024  		// Add a completed action to the unit.
  1025  		operationID, err := s.Model.EnqueueOperation("a test", 1)
  1026  		c.Assert(err, jc.ErrorIsNil)
  1027  		action, err := s.Model.AddAction(unit, operationID, "snapshot", nil, nil, nil)
  1028  		c.Assert(err, jc.ErrorIsNil)
  1029  		action, err = action.Finish(state.ActionResults{
  1030  			Status:  status,
  1031  			Message: "done",
  1032  		})
  1033  		c.Assert(err, jc.ErrorIsNil)
  1034  		c.Assert(action.Status(), gc.Equals, status)
  1035  
  1036  		// Destroy application and run cleanups.
  1037  		err = dummy.Destroy()
  1038  		c.Assert(err, jc.ErrorIsNil)
  1039  		// First cleanup marks all units of the application as dying.
  1040  		// Second cleanup clear pending actions.
  1041  		s.assertCleanupCount(c, 3)
  1042  	}
  1043  }
  1044  
  1045  func (s *CleanupSuite) TestCleanupStorageAttachments(c *gc.C) {
  1046  	s.assertDoesNotNeedCleanup(c)
  1047  
  1048  	ch := s.AddTestingCharm(c, "storage-block")
  1049  	storage := map[string]state.StorageConstraints{
  1050  		"data": makeStorageCons("loop", 1024, 1),
  1051  	}
  1052  	application := s.AddTestingApplicationWithStorage(c, "storage-block", ch, storage)
  1053  	u, err := application.AddUnit(state.AddUnitParams{})
  1054  	c.Assert(err, jc.ErrorIsNil)
  1055  
  1056  	// check no cleanups
  1057  	s.assertDoesNotNeedCleanup(c)
  1058  
  1059  	// this tag matches the storage instance created for the unit above.
  1060  	storageTag := names.NewStorageTag("data/0")
  1061  
  1062  	sa, err := s.storageBackend.StorageAttachment(storageTag, u.UnitTag())
  1063  	c.Assert(err, jc.ErrorIsNil)
  1064  	c.Assert(sa.Life(), gc.Equals, state.Alive)
  1065  
  1066  	// destroy unit and run cleanups; the storage should be detached
  1067  	err = u.Destroy()
  1068  	c.Assert(err, jc.ErrorIsNil)
  1069  	s.assertCleanupRuns(c)
  1070  
  1071  	// After running the cleanup, the attachment should be removed
  1072  	// (short-circuited, because volume was never attached).
  1073  	_, err = s.storageBackend.StorageAttachment(storageTag, u.UnitTag())
  1074  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1075  
  1076  	// check no cleanups
  1077  	s.assertDoesNotNeedCleanup(c)
  1078  }
  1079  
  1080  func (s *CleanupSuite) TestCleanupStorageInstances(c *gc.C) {
  1081  	ch := s.AddTestingCharm(c, "storage-block")
  1082  	storage := map[string]state.StorageConstraints{
  1083  		"allecto": makeStorageCons("modelscoped-block", 1024, 1),
  1084  	}
  1085  	application := s.AddTestingApplicationWithStorage(c, "storage-block", ch, storage)
  1086  	u, err := application.AddUnit(state.AddUnitParams{})
  1087  	c.Assert(err, jc.ErrorIsNil)
  1088  
  1089  	// check no cleanups
  1090  	s.assertDoesNotNeedCleanup(c)
  1091  
  1092  	// this tag matches the storage instance created for the unit above.
  1093  	storageTag := names.NewStorageTag("allecto/0")
  1094  
  1095  	si, err := s.storageBackend.StorageInstance(storageTag)
  1096  	c.Assert(err, jc.ErrorIsNil)
  1097  	c.Assert(si.Life(), gc.Equals, state.Alive)
  1098  
  1099  	// destroy storage instance and run cleanups
  1100  	err = s.storageBackend.DestroyStorageInstance(storageTag, true, false, dontWait)
  1101  	c.Assert(err, jc.ErrorIsNil)
  1102  	si, err = s.storageBackend.StorageInstance(storageTag)
  1103  	c.Assert(err, jc.ErrorIsNil)
  1104  	c.Assert(si.Life(), gc.Equals, state.Dying)
  1105  	sa, err := s.storageBackend.StorageAttachment(storageTag, u.UnitTag())
  1106  	c.Assert(err, jc.ErrorIsNil)
  1107  	c.Assert(sa.Life(), gc.Equals, state.Alive)
  1108  	s.assertCleanupRuns(c)
  1109  
  1110  	// After running the cleanup, the attachment should be removed
  1111  	// (short-circuited, because volume was never attached).
  1112  	_, err = s.storageBackend.StorageAttachment(storageTag, u.UnitTag())
  1113  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1114  
  1115  	// check no cleanups
  1116  	s.assertDoesNotNeedCleanup(c)
  1117  }
  1118  
  1119  func (s *CleanupSuite) TestCleanupMachineStorage(c *gc.C) {
  1120  	ch := s.AddTestingCharm(c, "storage-block")
  1121  	storage := map[string]state.StorageConstraints{
  1122  		"data": makeStorageCons("modelscoped", 1024, 1),
  1123  	}
  1124  	application := s.AddTestingApplicationWithStorage(c, "storage-block", ch, storage)
  1125  	unit, err := application.AddUnit(state.AddUnitParams{})
  1126  	c.Assert(err, jc.ErrorIsNil)
  1127  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
  1128  	c.Assert(err, jc.ErrorIsNil)
  1129  	machineId, err := unit.AssignedMachineId()
  1130  	c.Assert(err, jc.ErrorIsNil)
  1131  	machine, err := s.State.Machine(machineId)
  1132  	c.Assert(err, jc.ErrorIsNil)
  1133  
  1134  	// Destroy the application, so we can destroy the machine.
  1135  	err = unit.Destroy()
  1136  	c.Assert(err, jc.ErrorIsNil)
  1137  	s.assertCleanupRuns(c)
  1138  	err = application.Destroy()
  1139  	c.Assert(err, jc.ErrorIsNil)
  1140  	s.assertCleanupRuns(c)
  1141  
  1142  	// check no cleanups
  1143  	s.assertDoesNotNeedCleanup(c)
  1144  
  1145  	// destroy machine and run cleanups; the volume attachment
  1146  	// should be marked dying.
  1147  	err = machine.Destroy()
  1148  	c.Assert(err, jc.ErrorIsNil)
  1149  	s.assertCleanupRuns(c)
  1150  
  1151  	vas, err := s.storageBackend.MachineVolumeAttachments(machine.MachineTag())
  1152  	c.Assert(err, jc.ErrorIsNil)
  1153  	c.Assert(vas, gc.HasLen, 1)
  1154  	c.Assert(vas[0].Life(), gc.Equals, state.Dying)
  1155  
  1156  	// check no cleanups
  1157  	s.assertDoesNotNeedCleanup(c)
  1158  }
  1159  
  1160  func (s *CleanupSuite) TestCleanupCAASApplicationWithStorage(c *gc.C) {
  1161  	s.assertCleanupCAASEntityWithStorage(c, func(st *state.State, app *state.Application) error {
  1162  		op := app.DestroyOperation()
  1163  		op.DestroyStorage = true
  1164  		return st.ApplyOperation(op)
  1165  	})
  1166  }
  1167  
  1168  func (s *CleanupSuite) TestCleanupCAASUnitWithStorage(c *gc.C) {
  1169  	s.assertCleanupCAASEntityWithStorage(c, func(st *state.State, app *state.Application) error {
  1170  		units, err := app.AllUnits()
  1171  		if err != nil {
  1172  			return err
  1173  		}
  1174  		op := units[0].DestroyOperation()
  1175  		op.DestroyStorage = true
  1176  		return st.ApplyOperation(op)
  1177  	})
  1178  }
  1179  
  1180  func (s *CleanupSuite) assertCleanupCAASEntityWithStorage(c *gc.C, deleteOp func(*state.State, *state.Application) error) {
  1181  	s.PatchValue(&k8sprovider.NewK8sClients, k8stesting.NoopFakeK8sClients)
  1182  	st := s.Factory.MakeCAASModel(c, nil)
  1183  	defer st.Close()
  1184  
  1185  	assertCleanups := func(n int) {
  1186  		for i := 0; i < 4; i++ {
  1187  			err := st.Cleanup()
  1188  			c.Assert(err, jc.ErrorIsNil)
  1189  		}
  1190  		state.AssertNoCleanups(c, st)
  1191  	}
  1192  
  1193  	sb, err := state.NewStorageBackend(st)
  1194  	c.Assert(err, jc.ErrorIsNil)
  1195  	model, err := st.Model()
  1196  	c.Assert(err, jc.ErrorIsNil)
  1197  	broker, err := stateenvirons.GetNewCAASBrokerFunc(caas.New)(model)
  1198  	c.Assert(err, jc.ErrorIsNil)
  1199  	registry := stateenvirons.NewStorageProviderRegistry(broker)
  1200  	s.policy = testing.MockPolicy{
  1201  		GetStorageProviderRegistry: func() (corestorage.ProviderRegistry, error) {
  1202  			return registry, nil
  1203  		},
  1204  	}
  1205  
  1206  	ch := state.AddTestingCharmForSeries(c, st, "kubernetes", "storage-filesystem")
  1207  	storCons := map[string]state.StorageConstraints{
  1208  		"data": makeStorageCons("", 1024, 1),
  1209  	}
  1210  	application := state.AddTestingApplicationWithStorage(c, st, "storage-filesystem", ch, storCons)
  1211  	unit, err := application.AddUnit(state.AddUnitParams{})
  1212  	c.Assert(err, jc.ErrorIsNil)
  1213  	c.Assert(application.Refresh(), jc.ErrorIsNil)
  1214  
  1215  	fs, err := sb.AllFilesystems()
  1216  	c.Assert(err, jc.ErrorIsNil)
  1217  	c.Assert(fs, gc.HasLen, 1)
  1218  	fas, err := sb.UnitFilesystemAttachments(unit.UnitTag())
  1219  	c.Assert(err, jc.ErrorIsNil)
  1220  	c.Assert(fas, gc.HasLen, 1)
  1221  
  1222  	vols, err := sb.AllVolumes()
  1223  	c.Assert(err, jc.ErrorIsNil)
  1224  	c.Assert(vols, gc.HasLen, 1)
  1225  
  1226  	c.Log("provision storage")
  1227  	err = sb.SetVolumeInfo(vols[0].VolumeTag(), state.VolumeInfo{
  1228  		Size:     1024,
  1229  		VolumeId: "cloud-vol-id-1234",
  1230  	})
  1231  	c.Assert(err, jc.ErrorIsNil)
  1232  	err = sb.SetVolumeAttachmentInfo(unit.UnitTag(), vols[0].VolumeTag(), state.VolumeAttachmentInfo{})
  1233  	c.Assert(err, jc.ErrorIsNil)
  1234  	err = sb.SetFilesystemInfo(fs[0].FilesystemTag(), state.FilesystemInfo{
  1235  		Size:         1024,
  1236  		Pool:         "",
  1237  		FilesystemId: "cloud-fs-id-123",
  1238  	})
  1239  	c.Assert(err, jc.ErrorIsNil)
  1240  	err = sb.SetFilesystemAttachmentInfo(unit.UnitTag(), fs[0].FilesystemTag(), state.FilesystemAttachmentInfo{
  1241  		MountPoint: "/abc",
  1242  	})
  1243  	c.Assert(err, jc.ErrorIsNil)
  1244  
  1245  	c.Log("delete op")
  1246  	err = deleteOp(st, application)
  1247  	c.Assert(err, jc.ErrorIsNil)
  1248  	c.Assert(application.Refresh(), jc.ErrorIsNil)
  1249  
  1250  	c.Log("destroy app")
  1251  	err = application.Destroy()
  1252  	c.Assert(err, jc.ErrorIsNil)
  1253  
  1254  	assertCleanups(4)
  1255  	c.Assert(application.Life(), gc.Equals, state.Dying)
  1256  	c.Assert(unit.Refresh(), jc.ErrorIsNil)
  1257  	c.Assert(unit.Life(), gc.Equals, state.Dying)
  1258  	fas, err = sb.UnitFilesystemAttachments(unit.UnitTag())
  1259  	c.Assert(err, jc.ErrorIsNil)
  1260  	c.Assert(fas, gc.HasLen, 1)
  1261  	fs, err = sb.AllFilesystems()
  1262  	c.Assert(err, jc.ErrorIsNil)
  1263  	c.Assert(fs, gc.HasLen, 1)
  1264  	sas, err := sb.AllStorageInstances()
  1265  	c.Assert(err, jc.ErrorIsNil)
  1266  	c.Assert(sas, gc.HasLen, 1)
  1267  
  1268  	c.Log("unit dying")
  1269  	// RemoveStorageAttachment is called after storage-detaching hook.
  1270  	err = sb.RemoveStorageAttachment(names.NewStorageTag("data/0"), unit.UnitTag(), false)
  1271  	c.Assert(err, jc.ErrorIsNil)
  1272  	assertCleanups(1)
  1273  
  1274  	err = unit.EnsureDead()
  1275  	c.Assert(err, jc.ErrorIsNil)
  1276  	err = unit.Remove()
  1277  	c.Assert(err, jc.ErrorIsNil)
  1278  	sas, err = sb.AllStorageInstances()
  1279  	c.Assert(err, jc.ErrorIsNil)
  1280  	c.Assert(sas, gc.HasLen, 0)
  1281  
  1282  	assertCleanups(1)
  1283  	fs, err = sb.AllFilesystems()
  1284  	c.Assert(err, jc.ErrorIsNil)
  1285  	c.Assert(fs, gc.HasLen, 0)
  1286  
  1287  	// A storage provisioner would call this.
  1288  	err = sb.RemoveVolumeAttachment(unit.UnitTag(), vols[0].VolumeTag(), false)
  1289  	c.Assert(err, jc.ErrorIsNil)
  1290  	err = sb.RemoveVolume(vols[0].VolumeTag())
  1291  	c.Assert(err, jc.ErrorIsNil)
  1292  
  1293  	assertCleanups(1)
  1294  	vols, err = sb.AllVolumes()
  1295  	c.Assert(err, jc.ErrorIsNil)
  1296  	c.Assert(vols, gc.HasLen, 0)
  1297  }
  1298  
  1299  func (s *CleanupSuite) TestCleanupVolumeAttachments(c *gc.C) {
  1300  	_, err := s.State.AddOneMachine(state.MachineTemplate{
  1301  		Base: state.UbuntuBase("12.10"),
  1302  		Jobs: []state.MachineJob{state.JobHostUnits},
  1303  		Volumes: []state.HostVolumeParams{{
  1304  			Volume: state.VolumeParams{Pool: "loop", Size: 1024},
  1305  		}},
  1306  	})
  1307  	c.Assert(err, jc.ErrorIsNil)
  1308  	s.assertDoesNotNeedCleanup(c)
  1309  
  1310  	err = s.storageBackend.DestroyVolume(names.NewVolumeTag("0/0"), false)
  1311  	c.Assert(err, jc.ErrorIsNil)
  1312  	s.assertCleanupRuns(c)
  1313  
  1314  	attachment, err := s.storageBackend.VolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0"))
  1315  	c.Assert(err, jc.ErrorIsNil)
  1316  	c.Assert(attachment.Life(), gc.Equals, state.Dying)
  1317  }
  1318  
  1319  func (s *CleanupSuite) TestCleanupFilesystemAttachments(c *gc.C) {
  1320  	_, err := s.State.AddOneMachine(state.MachineTemplate{
  1321  		Base: state.UbuntuBase("12.10"),
  1322  		Jobs: []state.MachineJob{state.JobHostUnits},
  1323  		Filesystems: []state.HostFilesystemParams{{
  1324  			Filesystem: state.FilesystemParams{Pool: "rootfs", Size: 1024},
  1325  		}},
  1326  	})
  1327  	c.Assert(err, jc.ErrorIsNil)
  1328  	s.assertDoesNotNeedCleanup(c)
  1329  
  1330  	err = s.storageBackend.DestroyFilesystem(names.NewFilesystemTag("0/0"), false)
  1331  	c.Assert(err, jc.ErrorIsNil)
  1332  	s.assertCleanupRuns(c)
  1333  
  1334  	attachment, err := s.storageBackend.FilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("0/0"))
  1335  	c.Assert(err, jc.ErrorIsNil)
  1336  	c.Assert(attachment.Life(), gc.Equals, state.Dying)
  1337  }
  1338  
  1339  func (s *CleanupSuite) TestCleanupResourceBlob(c *gc.C) {
  1340  	app := s.AddTestingApplication(c, "wp", s.AddTestingCharm(c, "wordpress"))
  1341  	data := "ancient-debris"
  1342  	res := resourcetesting.NewResource(c, nil, "mug", "wp", data).Resource
  1343  	resources := s.State.Resources()
  1344  	_, err := resources.SetResource("wp", res.Username, res.Resource, bytes.NewBufferString(data), state.IncrementCharmModifiedVersion)
  1345  	c.Assert(err, jc.ErrorIsNil)
  1346  
  1347  	err = app.Destroy()
  1348  	c.Assert(err, jc.ErrorIsNil)
  1349  
  1350  	path := "application-wp/resources/mug"
  1351  	stateStorage := storage.NewStorage(s.State.ModelUUID(), s.State.MongoSession())
  1352  	closer, _, err := stateStorage.Get(path)
  1353  	c.Assert(err, jc.ErrorIsNil)
  1354  	err = closer.Close()
  1355  	c.Assert(err, jc.ErrorIsNil)
  1356  
  1357  	s.assertCleanupRuns(c)
  1358  
  1359  	_, _, err = stateStorage.Get(path)
  1360  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1361  }
  1362  
  1363  func (s *CleanupSuite) TestCleanupResourceBlobHandlesMissing(c *gc.C) {
  1364  	app := s.AddTestingApplication(c, "wp", s.AddTestingCharm(c, "wordpress"))
  1365  	data := "ancient-debris"
  1366  	res := resourcetesting.NewResource(c, nil, "mug", "wp", data).Resource
  1367  	resources := s.State.Resources()
  1368  	_, err := resources.SetResource("wp", res.Username, res.Resource, bytes.NewBufferString(data), state.IncrementCharmModifiedVersion)
  1369  	c.Assert(err, jc.ErrorIsNil)
  1370  
  1371  	err = app.Destroy()
  1372  	c.Assert(err, jc.ErrorIsNil)
  1373  
  1374  	path := "application-wp/resources/mug"
  1375  	stateStorage := storage.NewStorage(s.State.ModelUUID(), s.State.MongoSession())
  1376  	err = stateStorage.Remove(path)
  1377  	c.Assert(err, jc.ErrorIsNil)
  1378  
  1379  	s.assertCleanupRuns(c)
  1380  	// Make sure the cleanup completed successfully.
  1381  	s.assertDoesNotNeedCleanup(c)
  1382  }
  1383  
  1384  func (s *CleanupSuite) TestNothingToCleanup(c *gc.C) {
  1385  	s.assertDoesNotNeedCleanup(c)
  1386  	s.assertCleanupRuns(c)
  1387  	s.assertDoesNotNeedCleanup(c)
  1388  }
  1389  
  1390  func (s *CleanupSuite) TestCleanupIDSanity(c *gc.C) {
  1391  	// Cleanup IDs shouldn't be ObjectIdHex("blah")
  1392  	app := s.AddTestingApplication(c, "wp", s.AddTestingCharm(c, "wordpress"))
  1393  	err := app.Destroy()
  1394  	c.Assert(err, jc.ErrorIsNil)
  1395  
  1396  	coll := s.Session.DB("juju").C("cleanups")
  1397  	var ids []struct {
  1398  		ID string `bson:"_id"`
  1399  	}
  1400  	err = coll.Find(nil).All(&ids)
  1401  	c.Assert(err, jc.ErrorIsNil)
  1402  	for _, item := range ids {
  1403  		c.Assert(item.ID, gc.Not(gc.Matches), `.*ObjectIdHex\(.*`)
  1404  	}
  1405  	s.assertCleanupRuns(c)
  1406  }
  1407  
  1408  func (s *CleanupSuite) TestDyingUnitWithForceSchedulesForceFallback(c *gc.C) {
  1409  	ch := s.AddTestingCharm(c, "mysql")
  1410  	application := s.AddTestingApplication(c, "mysql", ch)
  1411  	unit, err := application.AddUnit(state.AddUnitParams{})
  1412  	c.Assert(err, jc.ErrorIsNil)
  1413  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
  1414  	c.Assert(err, jc.ErrorIsNil)
  1415  
  1416  	err = unit.SetAgentStatus(status.StatusInfo{
  1417  		Status: status.Idle,
  1418  	})
  1419  	c.Assert(err, jc.ErrorIsNil)
  1420  
  1421  	opErrs, err := unit.DestroyWithForce(true, time.Minute)
  1422  	c.Assert(err, jc.ErrorIsNil)
  1423  	c.Assert(opErrs, gc.IsNil)
  1424  
  1425  	// The unit should be dying, and there's a deferred cleanup to
  1426  	// endeaden it.
  1427  	assertLifeIs(c, unit, state.Dying)
  1428  
  1429  	s.assertNeedsCleanup(c)
  1430  	// dyingUnit
  1431  	s.assertCleanupRuns(c)
  1432  
  1433  	s.Clock.Advance(time.Minute)
  1434  	// forceDestroyedUnit
  1435  	s.assertCleanupRuns(c)
  1436  
  1437  	assertLifeIs(c, unit, state.Dead)
  1438  
  1439  	s.Clock.Advance(time.Minute)
  1440  	// forceRemoveUnit
  1441  	s.assertCleanupRuns(c)
  1442  
  1443  	assertUnitRemoved(c, unit)
  1444  	// After this there are two cleanups remaining: removedUnit, forceRemoveMachine
  1445  	//(but the last is delayed a minute).
  1446  	s.assertCleanupCountDirty(c, 2)
  1447  
  1448  	s.Clock.Advance(time.Minute)
  1449  	s.assertCleanupCount(c, 1)
  1450  }
  1451  
  1452  func (s *CleanupSuite) TestForceDestroyUnitDestroysSubordinates(c *gc.C) {
  1453  	prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeContainer)
  1454  	prr.allEnterScope(c)
  1455  	for _, principal := range []*state.Unit{prr.pu0, prr.pu1} {
  1456  		err := s.State.AssignUnit(principal, state.AssignCleanEmpty)
  1457  		c.Assert(err, jc.ErrorIsNil)
  1458  	}
  1459  	for _, unit := range []*state.Unit{prr.pu0, prr.pu1, prr.ru0, prr.ru1} {
  1460  		err := unit.SetAgentStatus(status.StatusInfo{
  1461  			Status: status.Idle,
  1462  		})
  1463  		c.Assert(err, jc.ErrorIsNil)
  1464  	}
  1465  
  1466  	unit := prr.pu0
  1467  	subordinate := prr.ru0
  1468  
  1469  	opErrs, err := unit.DestroyWithForce(true, time.Duration(0))
  1470  	c.Assert(err, jc.ErrorIsNil)
  1471  	c.Assert(opErrs, gc.IsNil)
  1472  
  1473  	assertLifeIs(c, unit, state.Dying)
  1474  	assertLifeIs(c, subordinate, state.Alive)
  1475  
  1476  	s.assertNeedsCleanup(c)
  1477  	// dyingUnit(mysql/0)
  1478  	s.assertNextCleanup(c, "dyingUnit(mysql/0)")
  1479  
  1480  	// forceDestroyUnit(mysql/0) triggers destruction of the subordinate that
  1481  	// needs to run - it fails because the subordinates haven't yet
  1482  	// been removed. It will be pending until near the end of this test when it
  1483  	// finally succeeds.
  1484  	s.assertNextCleanup(c, "forceDestroyUnit(mysql/0)")
  1485  
  1486  	assertLifeIs(c, subordinate, state.Dying)
  1487  	assertLifeIs(c, unit, state.Dying)
  1488  
  1489  	// dyingUnit(logging/0) runs and schedules the force cleanup.
  1490  	s.assertNextCleanup(c, "dyingUnit(logging/0)")
  1491  	// forceDestroyUnit(logging/0) sets it to dead.
  1492  	s.assertNextCleanup(c, "forceDestroyUnit(logging/0)")
  1493  
  1494  	assertLifeIs(c, subordinate, state.Dead)
  1495  	assertLifeIs(c, unit, state.Dying)
  1496  
  1497  	// forceRemoveUnit(logging/0) runs
  1498  	s.assertNextCleanup(c, "forceRemoveUnit(logging/0)")
  1499  	assertUnitRemoved(c, subordinate)
  1500  
  1501  	// Now forceDestroyUnit(mysql/0) can run successfully and make the unit dead
  1502  	s.assertNextCleanup(c, "forceRemoveUnit(mysql/0)")
  1503  	assertLifeIs(c, unit, state.Dead)
  1504  
  1505  	// forceRemoveUnit
  1506  	s.assertNextCleanup(c, "forceRemoveUnit")
  1507  
  1508  	assertUnitRemoved(c, unit)
  1509  	// After this there are two cleanups remaining: removedUnit, forceRemoveMachine.
  1510  	s.assertCleanupCount(c, 2)
  1511  }
  1512  
  1513  func (s *CleanupSuite) TestForceDestroyUnitLeavesRelations(c *gc.C) {
  1514  	prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)
  1515  	prr.allEnterScope(c)
  1516  	for _, unit := range []*state.Unit{prr.pu0, prr.pu1, prr.ru0, prr.ru1} {
  1517  		err := s.State.AssignUnit(unit, state.AssignCleanEmpty)
  1518  		c.Assert(err, jc.ErrorIsNil)
  1519  		err = unit.SetAgentStatus(status.StatusInfo{
  1520  			Status: status.Idle,
  1521  		})
  1522  		c.Assert(err, jc.ErrorIsNil)
  1523  	}
  1524  
  1525  	unit := prr.pu0
  1526  	opErrs, err := unit.DestroyWithForce(true, dontWait)
  1527  	c.Assert(err, jc.ErrorIsNil)
  1528  	c.Assert(opErrs, gc.IsNil)
  1529  
  1530  	assertLifeIs(c, unit, state.Dying)
  1531  	assertUnitInScope(c, unit, prr.rel, true)
  1532  
  1533  	// dyingUnit schedules forceDestroyedUnit
  1534  	s.assertCleanupRuns(c)
  1535  	// ...which leaves the scope for its relations.
  1536  	s.assertCleanupRuns(c)
  1537  
  1538  	assertLifeIs(c, unit, state.Dead)
  1539  	assertUnitInScope(c, unit, prr.rel, false)
  1540  
  1541  	// forceRemoveUnit
  1542  	s.assertCleanupRuns(c)
  1543  
  1544  	assertUnitRemoved(c, unit)
  1545  	// After this there are two cleanups remaining: removedUnit, forceRemoveMachine.
  1546  	s.assertCleanupCount(c, 2)
  1547  }
  1548  
  1549  func (s *CleanupSuite) TestForceDestroyUnitRemovesStorageAttachments(c *gc.C) {
  1550  	s.assertDoesNotNeedCleanup(c)
  1551  
  1552  	ch := s.AddTestingCharm(c, "storage-block")
  1553  	storage := map[string]state.StorageConstraints{
  1554  		"data": makeStorageCons("loop", 1024, 1),
  1555  	}
  1556  	application := s.AddTestingApplicationWithStorage(c, "storage-block", ch, storage)
  1557  	u, err := application.AddUnit(state.AddUnitParams{})
  1558  	c.Assert(err, jc.ErrorIsNil)
  1559  
  1560  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
  1561  	c.Assert(err, jc.ErrorIsNil)
  1562  	err = u.AssignToMachine(machine)
  1563  	c.Assert(err, jc.ErrorIsNil)
  1564  	err = u.SetAgentStatus(status.StatusInfo{
  1565  		Status: status.Idle,
  1566  	})
  1567  	c.Assert(err, jc.ErrorIsNil)
  1568  
  1569  	// this tag matches the storage instance created for the unit above.
  1570  	storageTag := names.NewStorageTag("data/0")
  1571  
  1572  	sa, err := s.storageBackend.StorageAttachment(storageTag, u.UnitTag())
  1573  	c.Assert(err, jc.ErrorIsNil)
  1574  	c.Assert(sa.Life(), gc.Equals, state.Alive)
  1575  
  1576  	// Ensure there's a volume on the machine hosting the unit so the
  1577  	// attachment removal can't be short-circuited.
  1578  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
  1579  	c.Assert(err, jc.ErrorIsNil)
  1580  	volume, err := s.storageBackend.StorageInstanceVolume(storageTag)
  1581  	c.Assert(err, jc.ErrorIsNil)
  1582  	err = s.storageBackend.SetVolumeInfo(
  1583  		volume.VolumeTag(), state.VolumeInfo{VolumeId: "vol-123"})
  1584  	c.Assert(err, jc.ErrorIsNil)
  1585  	err = s.storageBackend.SetVolumeAttachmentInfo(
  1586  		machine.MachineTag(),
  1587  		volume.VolumeTag(),
  1588  		state.VolumeAttachmentInfo{DeviceName: "sdc"},
  1589  	)
  1590  	c.Assert(err, jc.ErrorIsNil)
  1591  
  1592  	// destroy unit and run cleanups
  1593  	opErrs, err := u.DestroyWithForce(true, dontWait)
  1594  	c.Assert(opErrs, gc.IsNil)
  1595  	c.Assert(err, jc.ErrorIsNil)
  1596  	s.assertCleanupRuns(c)
  1597  
  1598  	// After running the cleanup, the attachment should still be
  1599  	// around because volume was attached.
  1600  	_, err = s.storageBackend.StorageAttachment(storageTag, u.UnitTag())
  1601  	c.Assert(err, jc.ErrorIsNil)
  1602  
  1603  	// So now run the forceDestroyedUnit cleanup...
  1604  	s.assertCleanupRuns(c)
  1605  
  1606  	// ...and the storage instance should be gone.
  1607  	// After running the cleanup, the attachment should still be
  1608  	// around because volume was attached.
  1609  	_, err = s.storageBackend.StorageAttachment(storageTag, u.UnitTag())
  1610  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1611  
  1612  	// forceRemoveUnit
  1613  	s.assertCleanupRuns(c)
  1614  
  1615  	assertUnitRemoved(c, u)
  1616  	// After this there are two cleanups remaining: removedUnit, forceRemoveMachine.
  1617  	s.assertCleanupCount(c, 2)
  1618  }
  1619  
  1620  func (s *CleanupSuite) TestForceDestroyApplicationRemovesUnitsThatAreAlreadyDying(c *gc.C) {
  1621  	// If you remove an application when it has a unit in error, and
  1622  	// then you try to force-remove it, it should get cleaned up
  1623  	// correctly.
  1624  	mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
  1625  	unit, err := mysql.AddUnit(state.AddUnitParams{})
  1626  	c.Assert(err, jc.ErrorIsNil)
  1627  	preventUnitDestroyRemove(c, unit)
  1628  	s.assertDoesNotNeedCleanup(c)
  1629  
  1630  	err = mysql.Destroy()
  1631  	c.Assert(err, jc.ErrorIsNil)
  1632  	err = mysql.Refresh()
  1633  	c.Assert(err, jc.ErrorIsNil)
  1634  
  1635  	// The application is dying and there's a cleanup to make the unit
  1636  	// dying but it hasn't run yet.
  1637  	s.assertNeedsCleanup(c)
  1638  	assertLife(c, mysql, state.Dying)
  1639  	assertLife(c, unit, state.Alive)
  1640  
  1641  	// cleanupUnitsForDyingApplication
  1642  	s.assertCleanupRuns(c)
  1643  	assertLife(c, unit, state.Dying)
  1644  	// dyingUnit
  1645  	s.assertCleanupRuns(c)
  1646  	assertLife(c, unit, state.Dying)
  1647  
  1648  	// Simulate the unit being in error by never coming back and
  1649  	// reporting the unit dead. The user eventually gets tired of
  1650  	// waiting and force-removes the application.
  1651  	op := mysql.DestroyOperation()
  1652  	op.Force = true
  1653  	err = s.State.ApplyOperation(op)
  1654  	c.Assert(err, jc.ErrorIsNil)
  1655  	c.Assert(op.Errors, gc.HasLen, 0)
  1656  
  1657  	// cleanupUnitsForDyingApplication
  1658  	s.assertNeedsCleanup(c)
  1659  	s.assertCleanupRuns(c)
  1660  	assertLifeIs(c, unit, state.Dying)
  1661  
  1662  	// Even though the unit was already dying, because we're forcing
  1663  	// we rerun the destroy operation so that fallback-scheduling can
  1664  	// happen.
  1665  
  1666  	// dyingUnit
  1667  	s.assertNeedsCleanup(c)
  1668  	s.assertCleanupRuns(c)
  1669  	assertLifeIs(c, unit, state.Dying)
  1670  
  1671  	// forceDestroyedUnit
  1672  	s.assertNeedsCleanup(c)
  1673  	s.assertCleanupRuns(c)
  1674  	assertLifeIs(c, unit, state.Dead)
  1675  
  1676  	// forceRemoveUnit
  1677  	s.assertNeedsCleanup(c)
  1678  	s.assertCleanupRuns(c)
  1679  	assertUnitRemoved(c, unit)
  1680  
  1681  	// application
  1682  	s.assertNeedsCleanup(c)
  1683  	s.assertCleanupRuns(c)
  1684  	assertRemoved(c, mysql)
  1685  }
  1686  
  1687  func (s *CleanupSuite) TestForceDestroyRelationIncorrectUnitCount(c *gc.C) {
  1688  	prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)
  1689  	prr.allEnterScope(c)
  1690  
  1691  	rel := prr.rel
  1692  	state.RemoveUnitRelations(c, rel)
  1693  	err := rel.Refresh()
  1694  	c.Assert(err, jc.ErrorIsNil)
  1695  	c.Assert(rel.UnitCount(), gc.Not(gc.Equals), 0)
  1696  
  1697  	opErrs, err := rel.DestroyWithForce(true, dontWait)
  1698  	c.Assert(err, jc.ErrorIsNil)
  1699  	c.Assert(opErrs, gc.IsNil)
  1700  
  1701  	// dyingRelation schedules cleanupForceDestroyedRelation
  1702  	s.assertCleanupRuns(c)
  1703  	err = rel.Refresh()
  1704  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1705  
  1706  	s.assertCleanupCount(c, 0)
  1707  }
  1708  
  1709  func (s *CleanupSuite) assertCleanupRuns(c *gc.C) {
  1710  	err := s.State.Cleanup()
  1711  	c.Assert(err, jc.ErrorIsNil)
  1712  }
  1713  
  1714  func (s *CleanupSuite) assertNeedsCleanup(c *gc.C) {
  1715  	actual, err := s.State.NeedsCleanup()
  1716  	c.Assert(err, jc.ErrorIsNil)
  1717  	c.Assert(actual, jc.IsTrue, gc.Commentf("NeedsCleanup returned false, expected true"))
  1718  }
  1719  
  1720  func (s *CleanupSuite) assertDoesNotNeedCleanup(c *gc.C) {
  1721  	state.AssertNoCleanups(c, s.State)
  1722  }
  1723  
  1724  // assertCleanupCount is useful because certain cleanups cause other cleanups
  1725  // to be queued; it makes more sense to just run cleanup again than to unpick
  1726  // object destruction so that we run the cleanups inline while running cleanups.
  1727  func (s *CleanupSuite) assertCleanupCount(c *gc.C, count int) {
  1728  	for i := 0; i < count; i++ {
  1729  		c.Logf("checking cleanups %d", i)
  1730  		s.assertNeedsCleanup(c)
  1731  		s.assertCleanupRuns(c)
  1732  	}
  1733  	s.assertDoesNotNeedCleanup(c)
  1734  }
  1735  
  1736  // assertCleanupCountDirty is the same as assertCleanupCount, but it
  1737  // checks that there are still cleanups to run.
  1738  func (s *CleanupSuite) assertCleanupCountDirty(c *gc.C, count int) {
  1739  	for i := 0; i < count; i++ {
  1740  		c.Logf("checking cleanups %d", i)
  1741  		s.assertNeedsCleanup(c)
  1742  		s.assertCleanupRuns(c)
  1743  	}
  1744  	s.assertNeedsCleanup(c)
  1745  }
  1746  
  1747  // assertNextCleanup tracks that the next cleanup runs, and logs what cleanup we are expecting.
  1748  func (s *CleanupSuite) assertNextCleanup(c *gc.C, message string) {
  1749  	c.Logf("expect cleanup: %s", message)
  1750  	s.assertNeedsCleanup(c)
  1751  	s.assertCleanupRuns(c)
  1752  }
  1753  
  1754  type lifeChecker interface {
  1755  	Refresh() error
  1756  	Life() state.Life
  1757  }
  1758  
  1759  func assertLifeIs(c *gc.C, thing lifeChecker, expected state.Life) {
  1760  	err := thing.Refresh()
  1761  	c.Assert(err, jc.ErrorIsNil)
  1762  	c.Assert(thing.Life(), gc.Equals, expected)
  1763  }
  1764  
  1765  func assertUnitRemoved(c *gc.C, thing lifeChecker) {
  1766  	err := thing.Refresh()
  1767  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1768  }
  1769  
  1770  func assertUnitInScope(c *gc.C, unit *state.Unit, rel *state.Relation, expected bool) {
  1771  	ru, err := rel.Unit(unit)
  1772  	c.Assert(err, jc.ErrorIsNil)
  1773  	inscope, err := ru.InScope()
  1774  	c.Assert(err, jc.ErrorIsNil)
  1775  	c.Assert(inscope, gc.Equals, expected)
  1776  }