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