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 }