github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	"gopkg.in/juju/charm.v4"
    13  
    14  	"github.com/juju/juju/instance"
    15  	"github.com/juju/juju/state"
    16  )
    17  
    18  type CleanupSuite struct {
    19  	ConnSuite
    20  }
    21  
    22  var _ = gc.Suite(&CleanupSuite{})
    23  
    24  func (s *CleanupSuite) TestCleanupDyingServiceUnits(c *gc.C) {
    25  	s.assertDoesNotNeedCleanup(c)
    26  
    27  	// Create a service with some units.
    28  	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
    29  	units := make([]*state.Unit, 3)
    30  	for i := range units {
    31  		unit, err := mysql.AddUnit()
    32  		c.Assert(err, jc.ErrorIsNil)
    33  		units[i] = unit
    34  	}
    35  	preventUnitDestroyRemove(c, units[0])
    36  	s.assertDoesNotNeedCleanup(c)
    37  
    38  	// Destroy the service and check the units are unaffected, but a cleanup
    39  	// has been scheduled.
    40  	err := mysql.Destroy()
    41  	c.Assert(err, jc.ErrorIsNil)
    42  	for _, unit := range units {
    43  		err := unit.Refresh()
    44  		c.Assert(err, jc.ErrorIsNil)
    45  	}
    46  	s.assertNeedsCleanup(c)
    47  
    48  	// Run the cleanup, and check that units are all destroyed as appropriate.
    49  	s.assertCleanupRuns(c)
    50  	err = units[0].Refresh()
    51  	c.Assert(err, jc.ErrorIsNil)
    52  	c.Assert(units[0].Life(), gc.Equals, state.Dying)
    53  	err = units[1].Refresh()
    54  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    55  	err = units[2].Refresh()
    56  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    57  
    58  	// Run a final cleanup to clear the cleanup scheduled for the unit that
    59  	// became dying.
    60  	s.assertCleanupCount(c, 1)
    61  }
    62  
    63  func (s *CleanupSuite) TestCleanupEnvironmentServices(c *gc.C) {
    64  	s.assertDoesNotNeedCleanup(c)
    65  
    66  	// Create a service with some units.
    67  	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
    68  	units := make([]*state.Unit, 3)
    69  	for i := range units {
    70  		unit, err := mysql.AddUnit()
    71  		c.Assert(err, jc.ErrorIsNil)
    72  		units[i] = unit
    73  	}
    74  	s.assertDoesNotNeedCleanup(c)
    75  
    76  	// Destroy the environment and check the service and units are
    77  	// unaffected, but a cleanup for the service has been scheduled.
    78  	env, err := s.State.Environment()
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	err = env.Destroy()
    81  	c.Assert(err, jc.ErrorIsNil)
    82  	s.assertNeedsCleanup(c)
    83  	s.assertCleanupRuns(c)
    84  	err = mysql.Refresh()
    85  	c.Assert(err, jc.ErrorIsNil)
    86  	c.Assert(mysql.Life(), gc.Equals, state.Dying)
    87  	for _, unit := range units {
    88  		err = unit.Refresh()
    89  		c.Assert(err, jc.ErrorIsNil)
    90  		c.Assert(unit.Life(), gc.Equals, state.Alive)
    91  	}
    92  
    93  	// The first cleanup Destroys the service, which
    94  	// schedules another cleanup to destroy the units,
    95  	// then we need another pass for the actions cleanup
    96  	// which is queued on the next pass
    97  	s.assertCleanupCount(c, 2)
    98  	for _, unit := range units {
    99  		err = unit.Refresh()
   100  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
   101  	}
   102  
   103  	// Now we should have all the cleanups done
   104  	s.assertDoesNotNeedCleanup(c)
   105  }
   106  
   107  func (s *CleanupSuite) TestCleanupRelationSettings(c *gc.C) {
   108  	s.assertDoesNotNeedCleanup(c)
   109  
   110  	// Create a relation with a unit in scope.
   111  	pr := NewPeerRelation(c, s.State, s.Owner)
   112  	rel := pr.ru0.Relation()
   113  	err := pr.ru0.EnterScope(map[string]interface{}{"some": "settings"})
   114  	c.Assert(err, jc.ErrorIsNil)
   115  	s.assertDoesNotNeedCleanup(c)
   116  
   117  	// Destroy the service, check the relation's still around.
   118  	err = pr.svc.Destroy()
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	s.assertCleanupCount(c, 2)
   121  	err = rel.Refresh()
   122  	c.Assert(err, jc.ErrorIsNil)
   123  	c.Assert(rel.Life(), gc.Equals, state.Dying)
   124  
   125  	// The unit leaves scope, triggering relation removal.
   126  	err = pr.ru0.LeaveScope()
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	s.assertNeedsCleanup(c)
   129  
   130  	// Settings are not destroyed yet...
   131  	settings, err := pr.ru1.ReadSettings("riak/0")
   132  	c.Assert(err, jc.ErrorIsNil)
   133  	c.Assert(settings, gc.DeepEquals, map[string]interface{}{"some": "settings"})
   134  
   135  	// ...but they are on cleanup.
   136  	s.assertCleanupCount(c, 1)
   137  	_, err = pr.ru1.ReadSettings("riak/0")
   138  	c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": settings not found`)
   139  }
   140  
   141  func (s *CleanupSuite) TestForceDestroyMachineErrors(c *gc.C) {
   142  	manager, err := s.State.AddMachine("quantal", state.JobManageEnviron)
   143  	c.Assert(err, jc.ErrorIsNil)
   144  	s.assertDoesNotNeedCleanup(c)
   145  	err = manager.ForceDestroy()
   146  	expect := fmt.Sprintf("machine %s is required by the environment", manager.Id())
   147  	c.Assert(err, gc.ErrorMatches, expect)
   148  	s.assertDoesNotNeedCleanup(c)
   149  	assertLife(c, manager, state.Alive)
   150  }
   151  
   152  func (s *CleanupSuite) TestCleanupForceDestroyedMachineUnit(c *gc.C) {
   153  	s.assertDoesNotNeedCleanup(c)
   154  
   155  	// Create a machine.
   156  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   157  	c.Assert(err, jc.ErrorIsNil)
   158  
   159  	// Create a relation with a unit in scope and assigned to the machine.
   160  	pr := NewPeerRelation(c, s.State, s.Owner)
   161  	err = pr.u0.AssignToMachine(machine)
   162  	c.Assert(err, jc.ErrorIsNil)
   163  	err = pr.ru0.EnterScope(nil)
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	s.assertDoesNotNeedCleanup(c)
   166  
   167  	// Force machine destruction, check cleanup queued.
   168  	err = machine.ForceDestroy()
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	s.assertNeedsCleanup(c)
   171  
   172  	// Clean up, and check that the unit has been removed...
   173  	s.assertCleanupCount(c, 2)
   174  	assertRemoved(c, pr.u0)
   175  
   176  	// ...and the unit has departed relation scope...
   177  	assertNotJoined(c, pr.ru0)
   178  
   179  	// ...but that the machine remains, and is Dead, ready for removal by the
   180  	// provisioner.
   181  	assertLife(c, machine, state.Dead)
   182  }
   183  
   184  func (s *CleanupSuite) TestCleanupForceDestroyedMachineWithContainer(c *gc.C) {
   185  	s.assertDoesNotNeedCleanup(c)
   186  
   187  	// Create a machine with a container.
   188  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   189  	c.Assert(err, jc.ErrorIsNil)
   190  	container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{
   191  		Series: "quantal",
   192  		Jobs:   []state.MachineJob{state.JobHostUnits},
   193  	}, machine.Id(), instance.LXC)
   194  	c.Assert(err, jc.ErrorIsNil)
   195  
   196  	// Create active units (in relation scope, with subordinates).
   197  	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeContainer)
   198  	err = prr.pru0.EnterScope(nil)
   199  	c.Assert(err, jc.ErrorIsNil)
   200  	err = prr.pru1.EnterScope(nil)
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	err = prr.rru0.EnterScope(nil)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	err = prr.rru1.EnterScope(nil)
   205  	c.Assert(err, jc.ErrorIsNil)
   206  
   207  	// Assign the various units to machines.
   208  	err = prr.pu0.AssignToMachine(machine)
   209  	c.Assert(err, jc.ErrorIsNil)
   210  	err = prr.pu1.AssignToMachine(container)
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	s.assertDoesNotNeedCleanup(c)
   213  
   214  	// Force removal of the top-level machine.
   215  	err = machine.ForceDestroy()
   216  	c.Assert(err, jc.ErrorIsNil)
   217  	s.assertNeedsCleanup(c)
   218  
   219  	// And do it again, just to check that the second cleanup doc for the same
   220  	// machine doesn't cause problems down the line.
   221  	err = machine.ForceDestroy()
   222  	c.Assert(err, jc.ErrorIsNil)
   223  	s.assertNeedsCleanup(c)
   224  
   225  	// Clean up, and check that the container has been removed...
   226  	s.assertCleanupCount(c, 2)
   227  	err = container.Refresh()
   228  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   229  
   230  	// ...and so have all the units...
   231  	assertRemoved(c, prr.pu0)
   232  	assertRemoved(c, prr.pu1)
   233  	assertRemoved(c, prr.ru0)
   234  	assertRemoved(c, prr.ru1)
   235  
   236  	// ...and none of the units have left relation scopes occupied...
   237  	assertNotInScope(c, prr.pru0)
   238  	assertNotInScope(c, prr.pru1)
   239  	assertNotInScope(c, prr.rru0)
   240  	assertNotInScope(c, prr.rru1)
   241  
   242  	// ...but that the machine remains, and is Dead, ready for removal by the
   243  	// provisioner.
   244  	assertLife(c, machine, state.Dead)
   245  }
   246  
   247  func (s *CleanupSuite) TestCleanupDyingUnit(c *gc.C) {
   248  	// Create active unit, in a relation.
   249  	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)
   250  	err := prr.pru0.EnterScope(nil)
   251  	c.Assert(err, jc.ErrorIsNil)
   252  
   253  	// Destroy provider unit 0; check it's Dying, and a cleanup has been scheduled.
   254  	err = prr.pu0.Destroy()
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	err = prr.pu0.Refresh()
   257  	c.Assert(err, jc.ErrorIsNil)
   258  	assertLife(c, prr.pu0, state.Dying)
   259  	s.assertNeedsCleanup(c)
   260  
   261  	// Check it's reported in scope until cleaned up.
   262  	assertJoined(c, prr.pru0)
   263  	s.assertCleanupCount(c, 1)
   264  	assertInScope(c, prr.pru0)
   265  	assertNotJoined(c, prr.pru0)
   266  
   267  	// Destroy the relation, and check it sticks around...
   268  	err = prr.rel.Destroy()
   269  	c.Assert(err, jc.ErrorIsNil)
   270  	assertLife(c, prr.rel, state.Dying)
   271  
   272  	// ...until the unit is removed, and really leaves scope.
   273  	err = prr.pu0.EnsureDead()
   274  	c.Assert(err, jc.ErrorIsNil)
   275  	err = prr.pu0.Remove()
   276  	c.Assert(err, jc.ErrorIsNil)
   277  	assertNotInScope(c, prr.pru0)
   278  	assertRemoved(c, prr.rel)
   279  }
   280  
   281  func (s *CleanupSuite) TestCleanupDyingUnitAlreadyRemoved(c *gc.C) {
   282  	// Create active unit, in a relation.
   283  	prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal)
   284  	err := prr.pru0.EnterScope(nil)
   285  	c.Assert(err, jc.ErrorIsNil)
   286  
   287  	// Destroy provider unit 0; check it's Dying, and a cleanup has been scheduled.
   288  	err = prr.pu0.Destroy()
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	err = prr.pu0.Refresh()
   291  	c.Assert(err, jc.ErrorIsNil)
   292  	assertLife(c, prr.pu0, state.Dying)
   293  	s.assertNeedsCleanup(c)
   294  
   295  	// Remove the unit, and the relation.
   296  	err = prr.pu0.EnsureDead()
   297  	c.Assert(err, jc.ErrorIsNil)
   298  	err = prr.pu0.Remove()
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	err = prr.rel.Destroy()
   301  	c.Assert(err, jc.ErrorIsNil)
   302  	assertRemoved(c, prr.rel)
   303  
   304  	// Check the cleanup still runs happily.
   305  	s.assertCleanupCount(c, 1)
   306  }
   307  
   308  func (s *CleanupSuite) TestCleanupActions(c *gc.C) {
   309  	s.assertDoesNotNeedCleanup(c)
   310  
   311  	// Create a service with a unit.
   312  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   313  	unit, err := dummy.AddUnit()
   314  	c.Assert(err, jc.ErrorIsNil)
   315  
   316  	// check no cleanups
   317  	s.assertDoesNotNeedCleanup(c)
   318  
   319  	// Add a couple actions to the unit
   320  	_, err = unit.AddAction("snapshot", nil)
   321  	c.Assert(err, jc.ErrorIsNil)
   322  	_, err = unit.AddAction("snapshot", nil)
   323  	c.Assert(err, jc.ErrorIsNil)
   324  
   325  	// make sure unit still has actions
   326  	actions, err := unit.PendingActions()
   327  	c.Assert(err, jc.ErrorIsNil)
   328  	c.Assert(len(actions), gc.Equals, 2)
   329  
   330  	// destroy unit and run cleanups
   331  	err = dummy.Destroy()
   332  	c.Assert(err, jc.ErrorIsNil)
   333  	s.assertCleanupRuns(c)
   334  
   335  	// make sure unit still has actions, after first cleanup pass
   336  	actions, err = unit.PendingActions()
   337  	c.Assert(err, jc.ErrorIsNil)
   338  	c.Assert(len(actions), gc.Equals, 2)
   339  
   340  	// second cleanup pass
   341  	s.assertCleanupRuns(c)
   342  
   343  	// make sure unit has no actions, after second cleanup pass
   344  	actions, err = unit.PendingActions()
   345  	c.Assert(err, jc.ErrorIsNil)
   346  	c.Assert(len(actions), gc.Equals, 0)
   347  
   348  	// check no cleanups
   349  	s.assertDoesNotNeedCleanup(c)
   350  }
   351  
   352  func (s *CleanupSuite) TestNothingToCleanup(c *gc.C) {
   353  	s.assertDoesNotNeedCleanup(c)
   354  	s.assertCleanupRuns(c)
   355  	s.assertDoesNotNeedCleanup(c)
   356  }
   357  
   358  func (s *CleanupSuite) assertCleanupRuns(c *gc.C) {
   359  	err := s.State.Cleanup()
   360  	c.Assert(err, jc.ErrorIsNil)
   361  }
   362  
   363  func (s *CleanupSuite) assertNeedsCleanup(c *gc.C) {
   364  	actual, err := s.State.NeedsCleanup()
   365  	c.Assert(err, jc.ErrorIsNil)
   366  	c.Assert(actual, jc.IsTrue)
   367  }
   368  
   369  func (s *CleanupSuite) assertDoesNotNeedCleanup(c *gc.C) {
   370  	actual, err := s.State.NeedsCleanup()
   371  	c.Assert(err, jc.ErrorIsNil)
   372  	c.Assert(actual, jc.IsFalse)
   373  }
   374  
   375  // assertCleanupCount is useful because certain cleanups cause other cleanups
   376  // to be queued; it makes more sense to just run cleanup again than to unpick
   377  // object destruction so that we run the cleanups inline while running cleanups.
   378  func (s *CleanupSuite) assertCleanupCount(c *gc.C, count int) {
   379  	for i := 0; i < count; i++ {
   380  		c.Logf("checking cleanups %d", i)
   381  		s.assertNeedsCleanup(c)
   382  		s.assertCleanupRuns(c)
   383  	}
   384  	s.assertDoesNotNeedCleanup(c)
   385  }