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