github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/volume_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/mgo/v3/bson" 9 "github.com/juju/names/v5" 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/core/constraints" 14 "github.com/juju/juju/core/instance" 15 "github.com/juju/juju/state" 16 stateerrors "github.com/juju/juju/state/errors" 17 "github.com/juju/juju/state/testing" 18 "github.com/juju/juju/storage" 19 "github.com/juju/juju/storage/poolmanager" 20 "github.com/juju/juju/storage/provider" 21 dummystorage "github.com/juju/juju/storage/provider/dummy" 22 ) 23 24 type VolumeStateSuite struct { 25 StorageStateSuiteBase 26 } 27 28 var _ = gc.Suite(&VolumeStateSuite{}) 29 30 func (s *VolumeStateSuite) TestAddMachine(c *gc.C) { 31 _, unit, _ := s.setupSingleStorage(c, "block", "loop-pool") 32 err := s.State.AssignUnit(unit, state.AssignCleanEmpty) 33 c.Assert(err, jc.ErrorIsNil) 34 s.assertMachineVolume(c, unit) 35 } 36 37 func (s *VolumeStateSuite) TestAssignToMachine(c *gc.C) { 38 _, unit, _ := s.setupSingleStorage(c, "block", "loop-pool") 39 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 40 c.Assert(err, jc.ErrorIsNil) 41 err = unit.AssignToMachine(machine) 42 c.Assert(err, jc.ErrorIsNil) 43 s.assertMachineVolume(c, unit) 44 } 45 46 func (s *VolumeStateSuite) assertMachineVolume(c *gc.C, unit *state.Unit) { 47 assignedMachineId, err := unit.AssignedMachineId() 48 c.Assert(err, jc.ErrorIsNil) 49 50 storageAttachments, err := s.storageBackend.UnitStorageAttachments(unit.UnitTag()) 51 c.Assert(err, jc.ErrorIsNil) 52 c.Assert(storageAttachments, gc.HasLen, 1) 53 storageInstance, err := s.storageBackend.StorageInstance(storageAttachments[0].StorageInstance()) 54 c.Assert(err, jc.ErrorIsNil) 55 56 volume := s.storageInstanceVolume(c, storageInstance.StorageTag()) 57 c.Assert(volume.VolumeTag(), gc.Equals, names.NewVolumeTag("0/0")) 58 volumeStorageTag, err := volume.StorageInstance() 59 c.Assert(err, jc.ErrorIsNil) 60 c.Assert(volumeStorageTag, gc.Equals, storageInstance.StorageTag()) 61 _, err = volume.Info() 62 c.Assert(err, jc.Satisfies, errors.IsNotProvisioned) 63 _, ok := volume.Params() 64 c.Assert(ok, jc.IsTrue) 65 66 machine, err := s.State.Machine(assignedMachineId) 67 c.Assert(err, jc.ErrorIsNil) 68 69 volumeAttachments, err := s.storageBackend.MachineVolumeAttachments(machine.MachineTag()) 70 c.Assert(err, jc.ErrorIsNil) 71 c.Assert(volumeAttachments, gc.HasLen, 1) 72 c.Assert(volumeAttachments[0].Volume(), gc.Equals, volume.VolumeTag()) 73 c.Assert(volumeAttachments[0].Host(), gc.Equals, machine.MachineTag()) 74 _, err = volumeAttachments[0].Info() 75 c.Assert(err, jc.Satisfies, errors.IsNotProvisioned) 76 _, ok = volumeAttachments[0].Params() 77 c.Assert(ok, jc.IsTrue) 78 79 _, err = s.storageBackend.VolumeAttachment(machine.MachineTag(), volume.VolumeTag()) 80 c.Assert(err, jc.ErrorIsNil) 81 82 assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag()) 83 } 84 85 func (s *VolumeStateSuite) TestAddApplicationInvalidPool(c *gc.C) { 86 ch := s.AddTestingCharm(c, "storage-block") 87 testStorage := map[string]state.StorageConstraints{ 88 "data": makeStorageCons("invalid-pool", 1024, 1), 89 } 90 _, err := s.State.AddApplication(state.AddApplicationArgs{ 91 Name: "storage-block", Charm: ch, 92 CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{ 93 OS: "ubuntu", 94 Channel: "20.04/stable", 95 }}, 96 Storage: testStorage, 97 }) 98 c.Assert(err, gc.ErrorMatches, `.* pool "invalid-pool" not found`) 99 } 100 101 func (s *VolumeStateSuite) TestAddApplicationNoUserDefaultPool(c *gc.C) { 102 ch := s.AddTestingCharm(c, "storage-block") 103 testStorage := map[string]state.StorageConstraints{ 104 "data": makeStorageCons("", 1024, 1), 105 } 106 app, err := s.State.AddApplication(state.AddApplicationArgs{ 107 Name: "storage-block", Charm: ch, 108 CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{ 109 OS: "ubuntu", 110 Channel: "20.04/stable", 111 }}, 112 Storage: testStorage, 113 }) 114 c.Assert(err, jc.ErrorIsNil) 115 cons, err := app.StorageConstraints() 116 c.Assert(err, jc.ErrorIsNil) 117 c.Assert(cons, jc.DeepEquals, map[string]state.StorageConstraints{ 118 "data": { 119 Pool: "loop", 120 Size: 1024, 121 Count: 1, 122 }, 123 "allecto": { 124 Pool: "loop", 125 Size: 1024, 126 Count: 0, 127 }, 128 }) 129 } 130 131 func (s *VolumeStateSuite) TestAddApplicationDefaultPool(c *gc.C) { 132 // Register a default pool. 133 pm := poolmanager.New(state.NewStateSettings(s.State), storage.ChainedProviderRegistry{ 134 dummystorage.StorageProviders(), 135 provider.CommonStorageProviders(), 136 }) 137 _, err := pm.Create("default-block", provider.LoopProviderType, map[string]interface{}{}) 138 c.Assert(err, jc.ErrorIsNil) 139 err = s.Model.UpdateModelConfig(map[string]interface{}{ 140 "storage-default-block-source": "default-block", 141 }, nil) 142 c.Assert(err, jc.ErrorIsNil) 143 144 ch := s.AddTestingCharm(c, "storage-block") 145 testStorage := map[string]state.StorageConstraints{ 146 "data": makeStorageCons("", 1024, 1), 147 } 148 app := s.AddTestingApplicationWithStorage(c, "storage-block", ch, testStorage) 149 cons, err := app.StorageConstraints() 150 c.Assert(err, jc.ErrorIsNil) 151 c.Assert(cons, jc.DeepEquals, map[string]state.StorageConstraints{ 152 "data": { 153 Pool: "default-block", 154 Size: 1024, 155 Count: 1, 156 }, 157 "allecto": { 158 Pool: "loop", 159 Size: 1024, 160 Count: 0, 161 }, 162 }) 163 } 164 165 func (s *VolumeStateSuite) TestSetVolumeInfo(c *gc.C) { 166 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 167 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 168 c.Assert(err, jc.ErrorIsNil) 169 170 volume := s.storageInstanceVolume(c, storageTag) 171 volumeTag := volume.VolumeTag() 172 s.assertVolumeUnprovisioned(c, volumeTag) 173 174 volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-ume"} 175 err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet) 176 c.Assert(err, jc.ErrorIsNil) 177 volumeInfoSet.Pool = "loop-pool" // taken from params 178 s.assertVolumeInfo(c, volumeTag, volumeInfoSet) 179 } 180 181 func (s *VolumeStateSuite) TestSetVolumeInfoNoVolumeId(c *gc.C) { 182 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 183 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 184 c.Assert(err, jc.ErrorIsNil) 185 186 volume := s.storageInstanceVolume(c, storageTag) 187 volumeTag := volume.VolumeTag() 188 s.assertVolumeUnprovisioned(c, volumeTag) 189 190 volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true} 191 err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet) 192 c.Assert(err, gc.ErrorMatches, `cannot set info for volume "0/0": volume ID not set`) 193 } 194 195 func (s *VolumeStateSuite) TestSetVolumeInfoNoStorageAssigned(c *gc.C) { 196 oneJob := []state.MachineJob{state.JobHostUnits} 197 cons := constraints.MustParse("mem=4G") 198 hc := instance.MustParseHardware("mem=2G") 199 200 volumeParams := state.VolumeParams{ 201 Pool: "loop-pool", 202 Size: 123, 203 } 204 machineTemplate := state.MachineTemplate{ 205 Base: state.UbuntuBase("12.10"), 206 Constraints: cons, 207 HardwareCharacteristics: hc, 208 InstanceId: "inst-id", 209 Nonce: "nonce", 210 Jobs: oneJob, 211 Volumes: []state.HostVolumeParams{{ 212 Volume: volumeParams, 213 }}, 214 } 215 machines, err := s.State.AddMachines(machineTemplate) 216 c.Assert(err, jc.ErrorIsNil) 217 c.Assert(machines, gc.HasLen, 1) 218 m, err := s.State.Machine(machines[0].Id()) 219 c.Assert(err, jc.ErrorIsNil) 220 221 volumeAttachments, err := s.storageBackend.MachineVolumeAttachments(m.MachineTag()) 222 c.Assert(err, jc.ErrorIsNil) 223 c.Assert(volumeAttachments, gc.HasLen, 1) 224 volumeTag := volumeAttachments[0].Volume() 225 226 volume := s.volume(c, volumeTag) 227 _, err = volume.StorageInstance() 228 c.Assert(err, jc.Satisfies, errors.IsNotAssigned) 229 230 s.assertVolumeUnprovisioned(c, volumeTag) 231 volumeInfoSet := state.VolumeInfo{Size: 123, VolumeId: "vol-ume"} 232 err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet) 233 c.Assert(err, jc.ErrorIsNil) 234 volumeInfoSet.Pool = "loop-pool" // taken from params 235 s.assertVolumeInfo(c, volumeTag, volumeInfoSet) 236 } 237 238 func (s *VolumeStateSuite) TestSetVolumeInfoImmutable(c *gc.C) { 239 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 240 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 241 c.Assert(err, jc.ErrorIsNil) 242 volume := s.storageInstanceVolume(c, storageTag) 243 volumeTag := volume.VolumeTag() 244 245 volumeInfoSet := state.VolumeInfo{Size: 123, VolumeId: "vol-ume"} 246 err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet) 247 c.Assert(err, jc.ErrorIsNil) 248 249 // The first call to SetVolumeInfo takes the pool name from 250 // the params; the second does not, but it must not change 251 // either. Callers are expected to get the existing info and 252 // update it, leaving immutable values intact. 253 err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet) 254 c.Assert(err, gc.ErrorMatches, `cannot set info for volume "0/0": cannot change pool from "loop-pool" to ""`) 255 256 volumeInfoSet.Pool = "loop-pool" 257 s.assertVolumeInfo(c, volumeTag, volumeInfoSet) 258 } 259 260 func (s *VolumeStateSuite) TestWatchVolumeAttachment(c *gc.C) { 261 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 262 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 263 c.Assert(err, jc.ErrorIsNil) 264 assignedMachineId, err := u.AssignedMachineId() 265 c.Assert(err, jc.ErrorIsNil) 266 machineTag := names.NewMachineTag(assignedMachineId) 267 268 volume := s.storageInstanceVolume(c, storageTag) 269 volumeTag := volume.VolumeTag() 270 // Ensure that all the creation events have flowed through the system. 271 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 272 273 w := s.storageBackend.WatchVolumeAttachment(machineTag, volumeTag) 274 defer testing.AssertStop(c, w) 275 wc := testing.NewNotifyWatcherC(c, w) 276 wc.AssertOneChange() 277 278 machine, err := s.State.Machine(assignedMachineId) 279 c.Assert(err, jc.ErrorIsNil) 280 err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil) 281 c.Assert(err, jc.ErrorIsNil) 282 283 // volume attachment will NOT react to volume changes 284 err = s.storageBackend.SetVolumeInfo(volumeTag, state.VolumeInfo{VolumeId: "vol-123"}) 285 c.Assert(err, jc.ErrorIsNil) 286 wc.AssertNoChange() 287 288 err = s.storageBackend.SetVolumeAttachmentInfo( 289 machineTag, volumeTag, state.VolumeAttachmentInfo{ 290 DeviceName: "xvdf1", 291 }, 292 ) 293 c.Assert(err, jc.ErrorIsNil) 294 wc.AssertOneChange() 295 } 296 297 func (s *VolumeStateSuite) TestWatchModelVolumes(c *gc.C) { 298 app := s.setupMixedScopeStorageApplication(c, "block") 299 addUnit := func() { 300 u, err := app.AddUnit(state.AddUnitParams{}) 301 c.Assert(err, jc.ErrorIsNil) 302 err = s.State.AssignUnit(u, state.AssignCleanEmpty) 303 c.Assert(err, jc.ErrorIsNil) 304 } 305 addUnit() 306 307 w := s.storageBackend.WatchModelVolumes() 308 defer testing.AssertStop(c, w) 309 wc := testing.NewStringsWatcherC(c, w) 310 wc.AssertChange("0", "1") // initial 311 wc.AssertNoChange() 312 313 addUnit() 314 wc.AssertChange("4", "5") 315 wc.AssertNoChange() 316 317 volume, err := s.storageBackend.Volume(names.NewVolumeTag("0")) 318 c.Assert(err, jc.ErrorIsNil) 319 storageTag, err := volume.StorageInstance() 320 c.Assert(err, jc.ErrorIsNil) 321 removeStorageInstance(c, s.storageBackend, storageTag) 322 err = s.storageBackend.DestroyVolume(names.NewVolumeTag("0"), false) 323 c.Assert(err, jc.ErrorIsNil) 324 wc.AssertChange("0") // dying 325 wc.AssertNoChange() 326 327 err = s.storageBackend.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0"), false) 328 c.Assert(err, jc.ErrorIsNil) 329 err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0"), false) 330 c.Assert(err, jc.ErrorIsNil) 331 332 err = s.storageBackend.RemoveVolume(names.NewVolumeTag("0")) 333 c.Assert(err, jc.ErrorIsNil) 334 wc.AssertChange("0") // removed 335 wc.AssertNoChange() 336 } 337 338 func (s *VolumeStateSuite) TestWatchModelVolumeAttachments(c *gc.C) { 339 app := s.setupMixedScopeStorageApplication(c, "block") 340 addUnit := func() { 341 u, err := app.AddUnit(state.AddUnitParams{}) 342 c.Assert(err, jc.ErrorIsNil) 343 err = s.State.AssignUnit(u, state.AssignCleanEmpty) 344 c.Assert(err, jc.ErrorIsNil) 345 } 346 addUnit() 347 348 w := s.storageBackend.WatchModelVolumeAttachments() 349 defer testing.AssertStop(c, w) 350 wc := testing.NewStringsWatcherC(c, w) 351 wc.AssertChange("0:0", "0:1") // initial 352 wc.AssertNoChange() 353 354 addUnit() 355 wc.AssertChange("1:4", "1:5") 356 wc.AssertNoChange() 357 358 err := s.storageBackend.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0"), false) 359 c.Assert(err, jc.ErrorIsNil) 360 wc.AssertChange("0:0") // dying 361 wc.AssertNoChange() 362 363 err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0"), false) 364 c.Assert(err, jc.ErrorIsNil) 365 wc.AssertChange("0:0") // removed 366 wc.AssertNoChange() 367 } 368 369 func (s *VolumeStateSuite) TestWatchMachineVolumes(c *gc.C) { 370 app := s.setupMixedScopeStorageApplication(c, "block", "machinescoped", "modelscoped") 371 addUnit := func() { 372 u, err := app.AddUnit(state.AddUnitParams{}) 373 c.Assert(err, jc.ErrorIsNil) 374 err = s.State.AssignUnit(u, state.AssignCleanEmpty) 375 c.Assert(err, jc.ErrorIsNil) 376 } 377 addUnit() 378 379 w := s.storageBackend.WatchMachineVolumes(names.NewMachineTag("0")) 380 defer testing.AssertStop(c, w) 381 wc := testing.NewStringsWatcherC(c, w) 382 wc.AssertChange("0/0", "0/1") // initial 383 wc.AssertNoChange() 384 385 addUnit() 386 // no change, since we're only interested in the one machine. 387 wc.AssertNoChange() 388 389 volume, err := s.storageBackend.Volume(names.NewVolumeTag("0/0")) 390 c.Assert(err, jc.ErrorIsNil) 391 storageTag, err := volume.StorageInstance() 392 c.Assert(err, jc.ErrorIsNil) 393 removeStorageInstance(c, s.storageBackend, storageTag) 394 err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 395 c.Assert(err, jc.ErrorIsNil) 396 wc.AssertChange("0/0") // dying 397 wc.AssertNoChange() 398 399 err = s.storageBackend.DestroyVolume(names.NewVolumeTag("0/0"), false) 400 c.Assert(err, jc.ErrorIsNil) 401 err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0"), false) 402 c.Assert(err, jc.ErrorIsNil) 403 err = s.storageBackend.RemoveVolume(names.NewVolumeTag("0/0")) 404 c.Assert(err, jc.ErrorIsNil) 405 wc.AssertChange("0/0") // removed 406 wc.AssertNoChange() 407 } 408 409 func (s *VolumeStateSuite) TestWatchMachineVolumeAttachments(c *gc.C) { 410 app := s.setupMixedScopeStorageApplication(c, "block", "machinescoped", "modelscoped") 411 addUnit := func(to *state.Machine) (u *state.Unit, m *state.Machine) { 412 var err error 413 u, err = app.AddUnit(state.AddUnitParams{}) 414 c.Assert(err, jc.ErrorIsNil) 415 if to != nil { 416 err = u.AssignToMachine(to) 417 c.Assert(err, jc.ErrorIsNil) 418 return u, to 419 } 420 err = s.State.AssignUnit(u, state.AssignCleanEmpty) 421 c.Assert(err, jc.ErrorIsNil) 422 m = unitMachine(c, s.State, u) 423 return u, m 424 } 425 _, m0 := addUnit(nil) 426 427 w := s.storageBackend.WatchMachineVolumeAttachments(names.NewMachineTag("0")) 428 defer testing.AssertStop(c, w) 429 wc := testing.NewStringsWatcherC(c, w) 430 wc.AssertChange("0:0/0", "0:0/1") // initial 431 wc.AssertNoChange() 432 433 addUnit(nil) 434 // no change, since we're only interested in the one machine. 435 wc.AssertNoChange() 436 437 err := s.storageBackend.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("2"), false) 438 c.Assert(err, jc.ErrorIsNil) 439 // no change, since we're only interested in attachments of 440 // machine-scoped volumes. 441 wc.AssertNoChange() 442 443 removeVolumeStorageInstance(c, s.storageBackend, names.NewVolumeTag("0/0")) 444 err = s.storageBackend.DestroyVolume(names.NewVolumeTag("0/0"), false) 445 c.Assert(err, jc.ErrorIsNil) 446 wc.AssertChange("0:0/0") // dying 447 wc.AssertNoChange() 448 449 err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0"), false) 450 c.Assert(err, jc.ErrorIsNil) 451 wc.AssertChange("0:0/0") // removed 452 wc.AssertNoChange() 453 454 addUnit(m0) 455 wc.AssertChange("0:0/8", "0:0/9") // added 456 } 457 458 func (s *VolumeStateSuite) TestParseVolumeAttachmentId(c *gc.C) { 459 assertValid := func(id string, m names.Tag, v names.VolumeTag) { 460 hostTag, volumeTag, err := state.ParseVolumeAttachmentId(id) 461 c.Assert(err, jc.ErrorIsNil) 462 c.Assert(hostTag, gc.Equals, m) 463 c.Assert(volumeTag, gc.Equals, v) 464 } 465 assertValid("0:0", names.NewMachineTag("0"), names.NewVolumeTag("0")) 466 assertValid("0:0/1", names.NewMachineTag("0"), names.NewVolumeTag("0/1")) 467 assertValid("0/lxd/0:1", names.NewMachineTag("0/lxd/0"), names.NewVolumeTag("1")) 468 assertValid("some-unit/0:1", names.NewUnitTag("some-unit/0"), names.NewVolumeTag("1")) 469 } 470 471 func (s *VolumeStateSuite) TestParseVolumeAttachmentIdError(c *gc.C) { 472 assertError := func(id, expect string) { 473 _, _, err := state.ParseVolumeAttachmentId(id) 474 c.Assert(err, gc.ErrorMatches, expect) 475 } 476 assertError("", `invalid volume attachment ID ""`) 477 assertError("0", `invalid volume attachment ID "0"`) 478 assertError("0:foo", `invalid volume attachment ID "0:foo"`) 479 assertError("bar:0", `invalid volume attachment ID "bar:0"`) 480 } 481 482 func (s *VolumeStateSuite) TestAllVolumes(c *gc.C) { 483 _, expected, _ := s.assertCreateVolumes(c) 484 485 volumes, err := s.storageBackend.AllVolumes() 486 c.Assert(err, jc.ErrorIsNil) 487 tags := make([]names.VolumeTag, len(volumes)) 488 for i, v := range volumes { 489 tags[i] = v.VolumeTag() 490 } 491 c.Assert(tags, jc.SameContents, expected) 492 } 493 494 func (s *VolumeStateSuite) assertCreateVolumes(c *gc.C) (_ *state.Machine, all, persistent []names.VolumeTag) { 495 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 496 Base: state.UbuntuBase("12.10"), 497 Jobs: []state.MachineJob{state.JobHostUnits}, 498 Volumes: []state.HostVolumeParams{{ 499 Volume: state.VolumeParams{Pool: "persistent-block", Size: 1024}, 500 }, { 501 Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, 502 }, { 503 Volume: state.VolumeParams{Pool: "static", Size: 2048}, 504 }}, 505 }) 506 c.Assert(err, jc.ErrorIsNil) 507 assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag()) 508 509 volume1 := s.volume(c, names.NewVolumeTag("0")) 510 volume2 := s.volume(c, names.NewVolumeTag("0/1")) 511 volume3 := s.volume(c, names.NewVolumeTag("2")) 512 513 volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-1"} 514 err = s.storageBackend.SetVolumeInfo(volume1.VolumeTag(), volumeInfoSet) 515 c.Assert(err, jc.ErrorIsNil) 516 517 volumeInfoSet = state.VolumeInfo{Size: 456, Persistent: false, VolumeId: "vol-2"} 518 err = s.storageBackend.SetVolumeInfo(volume2.VolumeTag(), volumeInfoSet) 519 c.Assert(err, jc.ErrorIsNil) 520 521 all = []names.VolumeTag{ 522 volume1.VolumeTag(), 523 volume2.VolumeTag(), 524 volume3.VolumeTag(), 525 } 526 persistent = []names.VolumeTag{ 527 volume1.VolumeTag(), 528 } 529 return machine, all, persistent 530 } 531 532 func (s *VolumeStateSuite) TestRemoveStorageInstanceDestroysAndUnassignsVolume(c *gc.C) { 533 _, u, storageTag := s.setupSingleStorage(c, "block", "modelscoped") 534 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 535 c.Assert(err, jc.ErrorIsNil) 536 volume := s.storageInstanceVolume(c, storageTag) 537 c.Assert(err, jc.ErrorIsNil) 538 539 err = u.Destroy() 540 c.Assert(err, jc.ErrorIsNil) 541 542 // Provision volume attachment so that detaching the storage 543 // attachment does not short-circuit. 544 defer state.SetBeforeHooks(c, s.State, func() { 545 machine := unitMachine(c, s.State, u) 546 err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil) 547 c.Assert(err, jc.ErrorIsNil) 548 err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), state.VolumeInfo{VolumeId: "vol-123"}) 549 c.Assert(err, jc.ErrorIsNil) 550 err = s.storageBackend.SetVolumeAttachmentInfo( 551 machine.MachineTag(), volume.VolumeTag(), 552 state.VolumeAttachmentInfo{DeviceName: "xvdf1"}, 553 ) 554 c.Assert(err, jc.ErrorIsNil) 555 }).Check() 556 557 err = s.storageBackend.DestroyStorageInstance(storageTag, true, false, dontWait) 558 c.Assert(err, jc.ErrorIsNil) 559 err = s.storageBackend.DetachStorage(storageTag, u.UnitTag(), false, dontWait) 560 c.Assert(err, jc.ErrorIsNil) 561 562 // The storage instance and attachment are dying, but not yet 563 // removed from state. The volume should still be assigned. 564 s.storageInstanceVolume(c, storageTag) 565 566 err = s.storageBackend.RemoveStorageAttachment(storageTag, u.UnitTag(), false) 567 c.Assert(err, jc.ErrorIsNil) 568 569 // The storage instance is now gone; the volume should no longer 570 // be assigned to the storage. 571 _, err = s.storageBackend.StorageInstanceVolume(storageTag) 572 c.Assert(err, gc.ErrorMatches, `volume for storage instance "data/0" not found`) 573 574 // The volume should still exist, but it should be dying. 575 v := s.volume(c, volume.VolumeTag()) 576 c.Assert(v.Life(), gc.Equals, state.Dying) 577 } 578 579 func (s *VolumeStateSuite) TestReleaseStorageInstanceVolumeReleasing(c *gc.C) { 580 _, u, storageTag := s.setupSingleStorage(c, "block", "modelscoped") 581 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 582 c.Assert(err, jc.ErrorIsNil) 583 volume := s.storageInstanceVolume(c, storageTag) 584 c.Assert(err, jc.ErrorIsNil) 585 c.Assert(volume.Releasing(), jc.IsFalse) 586 err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), state.VolumeInfo{VolumeId: "vol-123"}) 587 c.Assert(err, jc.ErrorIsNil) 588 589 err = u.Destroy() 590 c.Assert(err, jc.ErrorIsNil) 591 err = s.storageBackend.ReleaseStorageInstance(storageTag, true, false, dontWait) 592 c.Assert(err, jc.ErrorIsNil) 593 err = s.storageBackend.DetachStorage(storageTag, u.UnitTag(), false, dontWait) 594 c.Assert(err, jc.ErrorIsNil) 595 596 // The volume should should be dying, and releasing. 597 volume = s.volume(c, volume.VolumeTag()) 598 c.Assert(volume.Life(), gc.Equals, state.Dying) 599 c.Assert(volume.Releasing(), jc.IsTrue) 600 } 601 602 func (s *VolumeStateSuite) TestReleaseStorageInstanceVolumeUnreleasable(c *gc.C) { 603 _, u, storageTag := s.setupSingleStorage(c, "block", "modelscoped-unreleasable") 604 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 605 c.Assert(err, jc.ErrorIsNil) 606 volume := s.storageInstanceVolume(c, storageTag) 607 c.Assert(err, jc.ErrorIsNil) 608 c.Assert(volume.Releasing(), jc.IsFalse) 609 err = s.storageBackend.SetVolumeInfo(volume.VolumeTag(), state.VolumeInfo{VolumeId: "vol-123"}) 610 c.Assert(err, jc.ErrorIsNil) 611 612 err = u.Destroy() 613 c.Assert(err, jc.ErrorIsNil) 614 err = s.storageBackend.ReleaseStorageInstance(storageTag, true, false, dontWait) 615 c.Assert(err, gc.ErrorMatches, 616 `cannot release storage "data/0": storage provider "modelscoped-unreleasable" does not support releasing storage`, 617 ) 618 err = s.storageBackend.DetachStorage(storageTag, u.UnitTag(), false, dontWait) 619 c.Assert(err, jc.ErrorIsNil) 620 621 // The volume should should still be alive. 622 volume = s.volume(c, volume.VolumeTag()) 623 c.Assert(volume.Life(), gc.Equals, state.Alive) 624 c.Assert(volume.Releasing(), jc.IsFalse) 625 } 626 627 func (s *VolumeStateSuite) TestSetVolumeAttachmentInfoVolumeNotProvisioned(c *gc.C) { 628 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 629 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 630 c.Assert(err, jc.ErrorIsNil) 631 assignedMachineId, err := u.AssignedMachineId() 632 c.Assert(err, jc.ErrorIsNil) 633 machineTag := names.NewMachineTag(assignedMachineId) 634 635 volume := s.storageInstanceVolume(c, storageTag) 636 volumeTag := volume.VolumeTag() 637 638 err = s.storageBackend.SetVolumeAttachmentInfo( 639 machineTag, volumeTag, state.VolumeAttachmentInfo{ 640 DeviceName: "xvdf1", 641 }, 642 ) 643 c.Assert(err, gc.ErrorMatches, `cannot set info for volume attachment 0/0:0: volume "0/0" not provisioned`) 644 } 645 646 func (s *VolumeStateSuite) TestDestroyVolume(c *gc.C) { 647 volume, _ := s.setupMachineScopedVolumeAttachment(c) 648 assertDestroy := func() { 649 err := s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 650 c.Assert(err, jc.ErrorIsNil) 651 volume = s.volume(c, volume.VolumeTag()) 652 c.Assert(volume.Life(), gc.Equals, state.Dying) 653 } 654 defer state.SetBeforeHooks(c, s.State, assertDestroy).Check() 655 assertDestroy() 656 } 657 658 func (s *VolumeStateSuite) TestDestroyVolumeNotFound(c *gc.C) { 659 err := s.storageBackend.DestroyVolume(names.NewVolumeTag("0"), false) 660 c.Assert(err, gc.ErrorMatches, `destroying volume 0: volume "0" not found`) 661 c.Assert(err, jc.Satisfies, errors.IsNotFound) 662 } 663 664 func (s *VolumeStateSuite) TestDestroyVolumeStorageAssigned(c *gc.C) { 665 volume, _, u := s.setupStorageVolumeAttachment(c) 666 storageTag, err := volume.StorageInstance() 667 c.Assert(err, jc.ErrorIsNil) 668 669 err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 670 c.Assert(err, gc.ErrorMatches, "destroying volume 0: volume is assigned to storage data/0") 671 672 err = u.Destroy() 673 c.Assert(err, jc.ErrorIsNil) 674 removeStorageInstance(c, s.storageBackend, storageTag) 675 err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 676 c.Assert(err, jc.ErrorIsNil) 677 } 678 679 func (s *VolumeStateSuite) TestDestroyVolumeNoAttachments(c *gc.C) { 680 volume, machine := s.setupModelScopedVolumeAttachment(c) 681 err := s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), false) 682 c.Assert(err, jc.ErrorIsNil) 683 684 defer state.SetBeforeHooks(c, s.State, func() { 685 err := s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false) 686 c.Assert(err, jc.ErrorIsNil) 687 }).Check() 688 689 err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 690 c.Assert(err, jc.ErrorIsNil) 691 volume = s.volume(c, volume.VolumeTag()) 692 693 // There are no more attachments, so the volume should 694 // have been progressed directly to Dead. 695 c.Assert(volume.Life(), gc.Equals, state.Dead) 696 } 697 698 func (s *VolumeStateSuite) TestDetachVolumeDyingAttachment(c *gc.C) { 699 volume, machine := s.setupModelScopedVolumeAttachment(c) 700 volumeTag := volume.VolumeTag() 701 machineTag := machine.MachineTag() 702 // Make sure the state is already dying by the time we call DetachVolume 703 defer state.SetBeforeHooks(c, s.State, func() { 704 err := s.storageBackend.DetachVolume(machineTag, volumeTag, false) 705 c.Assert(err, jc.ErrorIsNil) 706 }).Check() 707 708 err := s.storageBackend.DetachVolume(machineTag, volumeTag, false) 709 c.Assert(err, jc.ErrorIsNil) 710 711 volumeAttachment := s.volumeAttachment(c, machineTag, volumeTag) 712 c.Assert(volumeAttachment.Life(), gc.Equals, state.Dying) 713 volume = s.volume(c, volumeTag) 714 c.Assert(volume.Life(), gc.Equals, state.Alive) 715 err = s.storageBackend.DestroyVolume(volumeTag, false) 716 c.Assert(err, jc.ErrorIsNil) 717 } 718 719 func (s *VolumeStateSuite) TestDetachVolumeDyingAttachmentPlan(c *gc.C) { 720 volume, machine := s.setupModelScopedVolumeAttachment(c) 721 machineTag := machine.MachineTag() 722 volumeTag := volume.VolumeTag() 723 err := machine.SetProvisioned("inst-id", "", "fake_nonce", nil) 724 c.Assert(err, jc.ErrorIsNil) 725 err = s.storageBackend.SetVolumeInfo(volumeTag, 726 state.VolumeInfo{ 727 Size: 1024, 728 VolumeId: "vol-ume", 729 }) 730 c.Assert(err, jc.ErrorIsNil) 731 err = s.storageBackend.SetVolumeAttachmentInfo(machineTag, volumeTag, 732 state.VolumeAttachmentInfo{ 733 DeviceName: "bogus", 734 }) 735 c.Assert(err, jc.ErrorIsNil) 736 // Simulate a machine agent recording its intent to attach the volume 737 err = s.storageBackend.CreateVolumeAttachmentPlan(machineTag, volumeTag, 738 state.VolumeAttachmentPlanInfo{DeviceType: storage.DeviceTypeLocal}) 739 c.Assert(err, jc.ErrorIsNil) 740 741 defer state.SetBeforeHooks(c, s.State, func() { 742 err := s.storageBackend.DetachVolume(machineTag, volumeTag, false) 743 c.Assert(err, jc.ErrorIsNil) 744 }).Check() 745 746 err = s.storageBackend.DetachVolume(machineTag, volumeTag, false) 747 c.Assert(err, jc.ErrorIsNil) 748 // The volume attachment shouldn't progress to dying, but its volume attachment plan should have 749 volumeAttachment := s.volumeAttachment(c, machineTag, volumeTag) 750 c.Assert(volumeAttachment.Life(), gc.Equals, state.Alive) 751 volumeAttachmentPlan := s.volumeAttachmentPlan(c, machineTag, volumeTag) 752 c.Assert(volumeAttachmentPlan.Life(), gc.Equals, state.Dying) 753 volume = s.volume(c, volumeTag) 754 // now calling RemoveAttachmentPlan removes the plan and moves the VolumeAttachment to dying 755 err = s.storageBackend.RemoveVolumeAttachmentPlan(machineTag, volumeTag, false) 756 c.Assert(err, jc.ErrorIsNil) 757 _, err = s.storageBackend.VolumeAttachmentPlan(machineTag, volumeTag) 758 c.Assert(err, jc.Satisfies, errors.IsNotFound) 759 volumeAttachment = s.volumeAttachment(c, machineTag, volumeTag) 760 c.Assert(volumeAttachment.Life(), gc.Equals, state.Dying) 761 } 762 763 func (s *VolumeStateSuite) TestRemoveVolume(c *gc.C) { 764 volume, machine := s.setupMachineScopedVolumeAttachment(c) 765 766 err := s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 767 c.Assert(err, jc.ErrorIsNil) 768 err = s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false) 769 c.Assert(err, jc.ErrorIsNil) 770 assertRemove := func() { 771 err = s.storageBackend.RemoveVolume(volume.VolumeTag()) 772 c.Assert(err, jc.ErrorIsNil) 773 _, err = s.storageBackend.Volume(volume.VolumeTag()) 774 c.Assert(err, jc.Satisfies, errors.IsNotFound) 775 } 776 defer state.SetBeforeHooks(c, s.State, assertRemove).Check() 777 assertRemove() 778 } 779 780 func (s *VolumeStateSuite) TestRemoveVolumeNotFound(c *gc.C) { 781 err := s.storageBackend.RemoveVolume(names.NewVolumeTag("42")) 782 c.Assert(err, jc.ErrorIsNil) 783 } 784 785 func (s *VolumeStateSuite) TestRemoveVolumeNotDead(c *gc.C) { 786 volume, _ := s.setupMachineScopedVolumeAttachment(c) 787 err := s.storageBackend.RemoveVolume(volume.VolumeTag()) 788 c.Assert(err, gc.ErrorMatches, "removing volume 0/0: volume is not dead") 789 err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 790 c.Assert(err, jc.ErrorIsNil) 791 err = s.storageBackend.RemoveVolume(volume.VolumeTag()) 792 c.Assert(err, gc.ErrorMatches, "removing volume 0/0: volume is not dead") 793 } 794 795 func (s *VolumeStateSuite) TestDetachVolume(c *gc.C) { 796 volume, machine := s.setupModelScopedVolumeAttachment(c) 797 assertDetach := func() { 798 err := s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), false) 799 c.Assert(err, jc.ErrorIsNil) 800 attachment := s.volumeAttachment(c, machine.MachineTag(), volume.VolumeTag()) 801 c.Assert(attachment.Life(), gc.Equals, state.Dying) 802 } 803 defer state.SetBeforeHooks(c, s.State, assertDetach).Check() 804 assertDetach() 805 } 806 807 func (s *VolumeStateSuite) TestDetachVolumeForce(c *gc.C) { 808 volume, machine := s.setupModelScopedVolumeAttachment(c) 809 coll, closer := state.GetCollection(s.st, "volumes") 810 defer closer() 811 812 // Set the volume to Dying even though the attachment is still alive. 813 err := coll.Writeable().UpdateId( 814 state.DocID(s.st, volume.VolumeTag().Id()), 815 bson.D{{"$set", bson.D{{"life", state.Dying}}}}) 816 c.Assert(err, jc.ErrorIsNil) 817 volume, err = s.storageBackend.Volume(volume.VolumeTag()) 818 c.Assert(err, jc.ErrorIsNil) 819 c.Assert(volume.Life(), gc.Equals, state.Dying) 820 821 assertDetach := func() { 822 err := s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), true) 823 c.Assert(err, jc.ErrorIsNil) 824 attachment := s.volumeAttachment(c, machine.MachineTag(), volume.VolumeTag()) 825 c.Assert(attachment.Life(), gc.Equals, state.Dying) 826 } 827 defer state.SetBeforeHooks(c, s.State, assertDetach).Check() 828 assertDetach() 829 } 830 831 func (s *VolumeStateSuite) TestRemoveLastVolumeAttachment(c *gc.C) { 832 volume, machine := s.setupModelScopedVolumeAttachment(c) 833 834 err := s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), false) 835 c.Assert(err, jc.ErrorIsNil) 836 837 err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 838 c.Assert(err, jc.ErrorIsNil) 839 volume = s.volume(c, volume.VolumeTag()) 840 c.Assert(volume.Life(), gc.Equals, state.Dying) 841 842 err = s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false) 843 c.Assert(err, jc.ErrorIsNil) 844 845 // The volume was Dying when the last attachment was 846 // removed, so the volume should now be Dead. 847 volume = s.volume(c, volume.VolumeTag()) 848 c.Assert(volume.Life(), gc.Equals, state.Dead) 849 } 850 851 func (s *VolumeStateSuite) TestRemoveLastVolumeAttachmentConcurrently(c *gc.C) { 852 volume, machine := s.setupModelScopedVolumeAttachment(c) 853 854 err := s.storageBackend.DetachVolume(machine.MachineTag(), volume.VolumeTag(), false) 855 c.Assert(err, jc.ErrorIsNil) 856 857 defer state.SetBeforeHooks(c, s.State, func() { 858 err := s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 859 c.Assert(err, jc.ErrorIsNil) 860 volume := s.volume(c, volume.VolumeTag()) 861 c.Assert(volume.Life(), gc.Equals, state.Dying) 862 }).Check() 863 864 err = s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false) 865 c.Assert(err, jc.ErrorIsNil) 866 867 // Last attachment was removed, and the volume was (concurrently) 868 // destroyed, so the volume should be Dead. 869 volume = s.volume(c, volume.VolumeTag()) 870 c.Assert(volume.Life(), gc.Equals, state.Dead) 871 } 872 873 func (s *VolumeStateSuite) TestRemoveVolumeAttachmentNotFound(c *gc.C) { 874 err := s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("42"), names.NewVolumeTag("42"), false) 875 c.Assert(err, jc.Satisfies, errors.IsNotFound) 876 c.Assert(err, gc.ErrorMatches, `removing attachment of volume 42 from machine 42: volume "42" on "machine 42" not found`) 877 } 878 879 func (s *VolumeStateSuite) TestRemoveVolumeAttachmentConcurrently(c *gc.C) { 880 volume, machine := s.setupMachineScopedVolumeAttachment(c) 881 882 err := s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 883 c.Assert(err, jc.ErrorIsNil) 884 remove := func() { 885 err := s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false) 886 c.Assert(err, jc.ErrorIsNil) 887 assertMachineStorageRefs(c, s.storageBackend, machine.MachineTag()) 888 } 889 defer state.SetBeforeHooks(c, s.State, remove).Check() 890 remove() 891 } 892 893 func (s *VolumeStateSuite) TestRemoveVolumeAttachmentAlive(c *gc.C) { 894 volume, machine := s.setupMachineScopedVolumeAttachment(c) 895 896 err := s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false) 897 c.Assert(err, gc.ErrorMatches, "removing attachment of volume 0/0 from machine 0: volume attachment is not dying") 898 } 899 900 func (s *VolumeStateSuite) TestRemoveMachineRemovesVolumes(c *gc.C) { 901 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 902 Base: state.UbuntuBase("12.10"), 903 Jobs: []state.MachineJob{state.JobHostUnits}, 904 Volumes: []state.HostVolumeParams{{ 905 Volume: state.VolumeParams{Pool: "persistent-block", Size: 1024}, // unprovisioned 906 }, { 907 Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // provisioned 908 }, { 909 Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // unprovisioned 910 }, { 911 Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // provisioned, non-persistent 912 }, { 913 Volume: state.VolumeParams{Pool: "static", Size: 2048}, // provisioned 914 }, { 915 Volume: state.VolumeParams{Pool: "static", Size: 2048}, // unprovisioned 916 }}, 917 }) 918 c.Assert(err, jc.ErrorIsNil) 919 920 volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-1"} 921 err = s.storageBackend.SetVolumeInfo(names.NewVolumeTag("0/1"), volumeInfoSet) 922 c.Assert(err, jc.ErrorIsNil) 923 volumeInfoSet = state.VolumeInfo{Size: 456, Persistent: false, VolumeId: "vol-2"} 924 err = s.storageBackend.SetVolumeInfo(names.NewVolumeTag("0/3"), volumeInfoSet) 925 c.Assert(err, jc.ErrorIsNil) 926 volumeInfoSet = state.VolumeInfo{Size: 789, Persistent: false, VolumeId: "vol-3"} 927 err = s.storageBackend.SetVolumeInfo(names.NewVolumeTag("4"), volumeInfoSet) 928 c.Assert(err, jc.ErrorIsNil) 929 930 allVolumes, err := s.storageBackend.AllVolumes() 931 c.Assert(err, jc.ErrorIsNil) 932 933 persistentVolumes := make([]state.Volume, 0, len(allVolumes)) 934 for _, v := range allVolumes { 935 info, err := v.Info() 936 if err == nil && info.Persistent { 937 persistentVolumes = append(persistentVolumes, v) 938 } 939 } 940 c.Assert(len(allVolumes), jc.GreaterThan, len(persistentVolumes)) 941 942 c.Assert(machine.Destroy(), jc.ErrorIsNil) 943 944 // Cannot advance to Dead while there are detachable dynamic volumes. 945 err = machine.EnsureDead() 946 c.Assert(errors.Is(err, stateerrors.HasAttachmentsError), jc.IsTrue) 947 c.Assert(err, gc.ErrorMatches, "machine 0 has attachments \\[volume-0\\]") 948 s.obliterateVolumeAttachment(c, machine.MachineTag(), names.NewVolumeTag("0")) 949 c.Assert(machine.EnsureDead(), jc.ErrorIsNil) 950 c.Assert(machine.Remove(), jc.ErrorIsNil) 951 952 // Machine is gone: non-detachable storage should be done too. 953 allVolumes, err = s.storageBackend.AllVolumes() 954 c.Assert(err, jc.ErrorIsNil) 955 // We should only have the persistent volume remaining. 956 c.Assert(allVolumes, gc.HasLen, 1) 957 c.Assert(allVolumes[0].Tag().String(), gc.Equals, "volume-0") 958 959 attachments, err := s.storageBackend.MachineVolumeAttachments(machine.MachineTag()) 960 c.Assert(err, jc.ErrorIsNil) 961 c.Assert(attachments, gc.HasLen, 0) 962 } 963 964 func (s *VolumeStateSuite) TestEnsureMachineDeadAddVolumeConcurrently(c *gc.C) { 965 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 966 Base: state.UbuntuBase("12.10"), 967 Jobs: []state.MachineJob{state.JobHostUnits}, 968 Volumes: []state.HostVolumeParams{{ 969 Volume: state.VolumeParams{Pool: "static", Size: 1024}, 970 }}, 971 }) 972 c.Assert(err, jc.ErrorIsNil) 973 974 addVolume := func() { 975 _, u, _ := s.setupSingleStorage(c, "block", "modelscoped") 976 err := u.AssignToMachine(machine) 977 c.Assert(err, jc.ErrorIsNil) 978 s.obliterateUnit(c, u.UnitTag()) 979 } 980 defer state.SetBeforeHooks(c, s.State, addVolume).Check() 981 982 // The static volume the machine was provisioned with does not matter, 983 // but the volume added concurrently does. 984 err = machine.EnsureDead() 985 c.Assert(err, gc.ErrorMatches, `machine 0 has attachments \[volume-1\]`) 986 } 987 988 func (s *VolumeStateSuite) TestEnsureMachineDeadRemoveVolumeConcurrently(c *gc.C) { 989 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 990 Base: state.UbuntuBase("12.10"), 991 Jobs: []state.MachineJob{state.JobHostUnits}, 992 Volumes: []state.HostVolumeParams{{ 993 Volume: state.VolumeParams{Pool: "static", Size: 1024}, 994 }}, 995 }) 996 c.Assert(err, jc.ErrorIsNil) 997 998 removeVolume := func() { 999 s.obliterateVolume(c, names.NewVolumeTag("0")) 1000 } 1001 defer state.SetBeforeHooks(c, s.State, removeVolume).Check() 1002 1003 // Removing a volume concurrently does not cause a transaction failure. 1004 err = machine.EnsureDead() 1005 c.Assert(err, jc.ErrorIsNil) 1006 } 1007 1008 func (s *VolumeStateSuite) TestVolumeMachineScoped(c *gc.C) { 1009 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 1010 Base: state.UbuntuBase("12.10"), 1011 Jobs: []state.MachineJob{state.JobHostUnits}, 1012 Volumes: []state.HostVolumeParams{{ 1013 Volume: state.VolumeParams{Pool: "loop", Size: 1024}, 1014 }}, 1015 }) 1016 c.Assert(err, jc.ErrorIsNil) 1017 1018 volume := s.volume(c, names.NewVolumeTag("0/0")) 1019 c.Assert(volume.Life(), gc.Equals, state.Alive) 1020 1021 err = s.storageBackend.DestroyVolume(volume.VolumeTag(), false) 1022 c.Assert(err, jc.ErrorIsNil) 1023 err = s.storageBackend.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag(), false) 1024 c.Assert(err, jc.ErrorIsNil) 1025 volume = s.volume(c, volume.VolumeTag()) 1026 c.Assert(volume.Life(), gc.Equals, state.Dead) 1027 1028 // Remove the machine: this should remove the volume. 1029 err = machine.Destroy() 1030 c.Assert(err, jc.ErrorIsNil) 1031 err = machine.EnsureDead() 1032 c.Assert(err, jc.ErrorIsNil) 1033 err = machine.Remove() 1034 c.Assert(err, jc.ErrorIsNil) 1035 volume, err = s.storageBackend.Volume(volume.VolumeTag()) 1036 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1037 } 1038 1039 func (s *VolumeStateSuite) TestVolumeBindingStorage(c *gc.C) { 1040 // Volumes created assigned to a storage instance are bound 1041 // to the machine/model, and not the storage. i.e. storage 1042 // is persistent by default. 1043 volume, _, u := s.setupStorageVolumeAttachment(c) 1044 storageTag, err := volume.StorageInstance() 1045 c.Assert(err, jc.ErrorIsNil) 1046 1047 // The volume should transition to Dying when the storage is removed. 1048 // We must destroy the unit before we can remove the storage. 1049 err = u.Destroy() 1050 c.Assert(err, jc.ErrorIsNil) 1051 removeStorageInstance(c, s.storageBackend, storageTag) 1052 volume = s.volume(c, volume.VolumeTag()) 1053 c.Assert(volume.Life(), gc.Equals, state.Dying) 1054 } 1055 1056 func (s *VolumeStateSuite) setupStorageVolumeAttachment(c *gc.C) (state.Volume, *state.Machine, *state.Unit) { 1057 _, u, storageTag := s.setupSingleStorage(c, "block", "modelscoped") 1058 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 1059 c.Assert(err, jc.ErrorIsNil) 1060 machine := unitMachine(c, s.State, u) 1061 return s.storageInstanceVolume(c, storageTag), machine, u 1062 } 1063 1064 func (s *VolumeStateSuite) setupModelScopedVolumeAttachment(c *gc.C) (state.Volume, *state.Machine) { 1065 return s.setupVolumeAttachment(c, "modelscoped") 1066 } 1067 1068 func (s *VolumeStateSuite) setupMachineScopedVolumeAttachment(c *gc.C) (state.Volume, *state.Machine) { 1069 return s.setupVolumeAttachment(c, "loop") 1070 } 1071 1072 func (s *VolumeStateSuite) setupVolumeAttachment(c *gc.C, pool string) (state.Volume, *state.Machine) { 1073 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 1074 Base: state.UbuntuBase("12.10"), 1075 Jobs: []state.MachineJob{state.JobHostUnits}, 1076 Volumes: []state.HostVolumeParams{{ 1077 Volume: state.VolumeParams{Pool: pool, Size: 1024}, 1078 }}, 1079 }) 1080 c.Assert(err, jc.ErrorIsNil) 1081 volumeAttachments, err := s.storageBackend.MachineVolumeAttachments(machine.MachineTag()) 1082 c.Assert(err, jc.ErrorIsNil) 1083 c.Assert(volumeAttachments, gc.HasLen, 1) 1084 volume, err := s.storageBackend.Volume(volumeAttachments[0].Volume()) 1085 c.Assert(err, jc.ErrorIsNil) 1086 return volume, machine 1087 } 1088 1089 func removeVolumeStorageInstance(c *gc.C, sb *state.StorageBackend, volumeTag names.VolumeTag) { 1090 volume, err := sb.Volume(volumeTag) 1091 c.Assert(err, jc.ErrorIsNil) 1092 storageTag, err := volume.StorageInstance() 1093 c.Assert(err, jc.ErrorIsNil) 1094 removeStorageInstance(c, sb, storageTag) 1095 } 1096 1097 func removeStorageInstance(c *gc.C, sb *state.StorageBackend, storageTag names.StorageTag) { 1098 err := sb.DestroyStorageInstance(storageTag, true, false, dontWait) 1099 c.Assert(err, jc.ErrorIsNil) 1100 attachments, err := sb.StorageAttachments(storageTag) 1101 c.Assert(err, jc.ErrorIsNil) 1102 for _, a := range attachments { 1103 err = sb.DetachStorage(storageTag, a.Unit(), false, dontWait) 1104 c.Assert(err, jc.ErrorIsNil) 1105 } 1106 _, err = sb.StorageInstance(storageTag) 1107 c.Assert(err, jc.Satisfies, errors.IsNotFound) 1108 }