github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/names" 9 jc "github.com/juju/testing/checkers" 10 "github.com/juju/utils/set" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/constraints" 14 "github.com/juju/juju/instance" 15 "github.com/juju/juju/state" 16 "github.com/juju/juju/state/testing" 17 "github.com/juju/juju/storage/poolmanager" 18 "github.com/juju/juju/storage/provider" 19 ) 20 21 type VolumeStateSuite struct { 22 StorageStateSuiteBase 23 } 24 25 var _ = gc.Suite(&VolumeStateSuite{}) 26 27 func (s *VolumeStateSuite) TestAddMachine(c *gc.C) { 28 _, unit, _ := s.setupSingleStorage(c, "block", "loop-pool") 29 err := s.State.AssignUnit(unit, state.AssignCleanEmpty) 30 c.Assert(err, jc.ErrorIsNil) 31 s.assertMachineVolume(c, unit) 32 } 33 34 func (s *VolumeStateSuite) TestAssignToMachine(c *gc.C) { 35 _, unit, _ := s.setupSingleStorage(c, "block", "loop-pool") 36 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 37 c.Assert(err, jc.ErrorIsNil) 38 err = unit.AssignToMachine(machine) 39 c.Assert(err, jc.ErrorIsNil) 40 s.assertMachineVolume(c, unit) 41 } 42 43 func (s *VolumeStateSuite) assertMachineVolume(c *gc.C, unit *state.Unit) { 44 assignedMachineId, err := unit.AssignedMachineId() 45 c.Assert(err, jc.ErrorIsNil) 46 47 storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag()) 48 c.Assert(err, jc.ErrorIsNil) 49 c.Assert(storageAttachments, gc.HasLen, 1) 50 storageInstance, err := s.State.StorageInstance(storageAttachments[0].StorageInstance()) 51 c.Assert(err, jc.ErrorIsNil) 52 53 volume := s.storageInstanceVolume(c, storageInstance.StorageTag()) 54 c.Assert(volume.VolumeTag(), gc.Equals, names.NewVolumeTag("0/0")) 55 volumeStorageTag, err := volume.StorageInstance() 56 c.Assert(err, jc.ErrorIsNil) 57 c.Assert(volumeStorageTag, gc.Equals, storageInstance.StorageTag()) 58 _, err = volume.Info() 59 c.Assert(err, jc.Satisfies, errors.IsNotProvisioned) 60 _, ok := volume.Params() 61 c.Assert(ok, jc.IsTrue) 62 63 machine, err := s.State.Machine(assignedMachineId) 64 c.Assert(err, jc.ErrorIsNil) 65 volumeAttachments, err := s.State.MachineVolumeAttachments(machine.MachineTag()) 66 c.Assert(err, jc.ErrorIsNil) 67 c.Assert(volumeAttachments, gc.HasLen, 1) 68 c.Assert(volumeAttachments[0].Volume(), gc.Equals, volume.VolumeTag()) 69 c.Assert(volumeAttachments[0].Machine(), gc.Equals, machine.MachineTag()) 70 _, err = volumeAttachments[0].Info() 71 c.Assert(err, jc.Satisfies, errors.IsNotProvisioned) 72 _, ok = volumeAttachments[0].Params() 73 c.Assert(ok, jc.IsTrue) 74 75 _, err = s.State.VolumeAttachment(machine.MachineTag(), volume.VolumeTag()) 76 c.Assert(err, jc.ErrorIsNil) 77 78 assertMachineStorageRefs(c, s.State, machine.MachineTag()) 79 } 80 81 func (s *VolumeStateSuite) TestAddServiceInvalidPool(c *gc.C) { 82 ch := s.AddTestingCharm(c, "storage-block") 83 storage := map[string]state.StorageConstraints{ 84 "data": makeStorageCons("invalid-pool", 1024, 1), 85 } 86 _, err := s.State.AddService(state.AddServiceArgs{Name: "storage-block", Owner: s.Owner.String(), Charm: ch, Storage: storage}) 87 c.Assert(err, gc.ErrorMatches, `.* pool "invalid-pool" not found`) 88 } 89 90 func (s *VolumeStateSuite) TestAddServiceNoUserDefaultPool(c *gc.C) { 91 ch := s.AddTestingCharm(c, "storage-block") 92 storage := map[string]state.StorageConstraints{ 93 "data": makeStorageCons("", 1024, 1), 94 } 95 service, err := s.State.AddService(state.AddServiceArgs{Name: "storage-block", Owner: s.Owner.String(), Charm: ch, Storage: storage}) 96 c.Assert(err, jc.ErrorIsNil) 97 cons, err := service.StorageConstraints() 98 c.Assert(err, jc.ErrorIsNil) 99 c.Assert(cons, jc.DeepEquals, map[string]state.StorageConstraints{ 100 "data": state.StorageConstraints{ 101 Pool: "loop", 102 Size: 1024, 103 Count: 1, 104 }, 105 "allecto": state.StorageConstraints{ 106 Pool: "loop", 107 Size: 1024, 108 Count: 0, 109 }, 110 }) 111 } 112 113 func (s *VolumeStateSuite) TestAddServiceDefaultPool(c *gc.C) { 114 // Register a default pool. 115 pm := poolmanager.New(state.NewStateSettings(s.State)) 116 _, err := pm.Create("default-block", provider.LoopProviderType, map[string]interface{}{}) 117 c.Assert(err, jc.ErrorIsNil) 118 err = s.State.UpdateModelConfig(map[string]interface{}{ 119 "storage-default-block-source": "default-block", 120 }, nil, nil) 121 c.Assert(err, jc.ErrorIsNil) 122 123 ch := s.AddTestingCharm(c, "storage-block") 124 storage := map[string]state.StorageConstraints{ 125 "data": makeStorageCons("", 1024, 1), 126 } 127 service := s.AddTestingServiceWithStorage(c, "storage-block", ch, storage) 128 cons, err := service.StorageConstraints() 129 c.Assert(err, jc.ErrorIsNil) 130 c.Assert(cons, jc.DeepEquals, map[string]state.StorageConstraints{ 131 "data": state.StorageConstraints{ 132 Pool: "default-block", 133 Size: 1024, 134 Count: 1, 135 }, 136 "allecto": state.StorageConstraints{ 137 Pool: "loop", 138 Size: 1024, 139 Count: 0, 140 }, 141 }) 142 } 143 144 func (s *VolumeStateSuite) TestSetVolumeInfo(c *gc.C) { 145 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 146 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 147 c.Assert(err, jc.ErrorIsNil) 148 149 volume := s.storageInstanceVolume(c, storageTag) 150 volumeTag := volume.VolumeTag() 151 s.assertVolumeUnprovisioned(c, volumeTag) 152 153 volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-ume"} 154 err = s.State.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet) 155 c.Assert(err, jc.ErrorIsNil) 156 volumeInfoSet.Pool = "loop-pool" // taken from params 157 s.assertVolumeInfo(c, volumeTag, volumeInfoSet) 158 } 159 160 func (s *VolumeStateSuite) TestSetVolumeInfoNoVolumeId(c *gc.C) { 161 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 162 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 163 c.Assert(err, jc.ErrorIsNil) 164 165 volume := s.storageInstanceVolume(c, storageTag) 166 volumeTag := volume.VolumeTag() 167 s.assertVolumeUnprovisioned(c, volumeTag) 168 169 volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true} 170 err = s.State.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet) 171 c.Assert(err, gc.ErrorMatches, `cannot set info for volume "0/0": volume ID not set`) 172 } 173 174 func (s *VolumeStateSuite) TestSetVolumeInfoNoStorageAssigned(c *gc.C) { 175 oneJob := []state.MachineJob{state.JobHostUnits} 176 cons := constraints.MustParse("mem=4G") 177 hc := instance.MustParseHardware("mem=2G") 178 179 volumeParams := state.VolumeParams{ 180 Pool: "loop-pool", 181 Size: 123, 182 } 183 machineTemplate := state.MachineTemplate{ 184 Series: "precise", 185 Constraints: cons, 186 HardwareCharacteristics: hc, 187 InstanceId: "inst-id", 188 Nonce: "nonce", 189 Jobs: oneJob, 190 Volumes: []state.MachineVolumeParams{{ 191 Volume: volumeParams, 192 }}, 193 } 194 machines, err := s.State.AddMachines(machineTemplate) 195 c.Assert(err, jc.ErrorIsNil) 196 c.Assert(machines, gc.HasLen, 1) 197 m, err := s.State.Machine(machines[0].Id()) 198 c.Assert(err, jc.ErrorIsNil) 199 200 volumeAttachments, err := s.State.MachineVolumeAttachments(m.MachineTag()) 201 c.Assert(err, jc.ErrorIsNil) 202 c.Assert(volumeAttachments, gc.HasLen, 1) 203 volumeTag := volumeAttachments[0].Volume() 204 205 volume := s.volume(c, volumeTag) 206 _, err = volume.StorageInstance() 207 c.Assert(err, jc.Satisfies, errors.IsNotAssigned) 208 209 s.assertVolumeUnprovisioned(c, volumeTag) 210 volumeInfoSet := state.VolumeInfo{Size: 123, VolumeId: "vol-ume"} 211 err = s.State.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet) 212 c.Assert(err, jc.ErrorIsNil) 213 volumeInfoSet.Pool = "loop-pool" // taken from params 214 s.assertVolumeInfo(c, volumeTag, volumeInfoSet) 215 } 216 217 func (s *VolumeStateSuite) TestSetVolumeInfoImmutable(c *gc.C) { 218 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 219 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 220 c.Assert(err, jc.ErrorIsNil) 221 volume := s.storageInstanceVolume(c, storageTag) 222 volumeTag := volume.VolumeTag() 223 224 volumeInfoSet := state.VolumeInfo{Size: 123, VolumeId: "vol-ume"} 225 err = s.State.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet) 226 c.Assert(err, jc.ErrorIsNil) 227 228 // The first call to SetVolumeInfo takes the pool name from 229 // the params; the second does not, but it must not change 230 // either. Callers are expected to get the existing info and 231 // update it, leaving immutable values intact. 232 err = s.State.SetVolumeInfo(volume.VolumeTag(), volumeInfoSet) 233 c.Assert(err, gc.ErrorMatches, `cannot set info for volume "0/0": cannot change pool from "loop-pool" to ""`) 234 235 volumeInfoSet.Pool = "loop-pool" 236 s.assertVolumeInfo(c, volumeTag, volumeInfoSet) 237 } 238 239 func (s *VolumeStateSuite) TestWatchVolumeAttachment(c *gc.C) { 240 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 241 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 242 c.Assert(err, jc.ErrorIsNil) 243 assignedMachineId, err := u.AssignedMachineId() 244 c.Assert(err, jc.ErrorIsNil) 245 machineTag := names.NewMachineTag(assignedMachineId) 246 247 volume := s.storageInstanceVolume(c, storageTag) 248 volumeTag := volume.VolumeTag() 249 250 w := s.State.WatchVolumeAttachment(machineTag, volumeTag) 251 defer testing.AssertStop(c, w) 252 wc := testing.NewNotifyWatcherC(c, s.State, w) 253 wc.AssertOneChange() 254 255 machine, err := s.State.Machine(assignedMachineId) 256 c.Assert(err, jc.ErrorIsNil) 257 err = machine.SetProvisioned("inst-id", "fake_nonce", nil) 258 c.Assert(err, jc.ErrorIsNil) 259 260 // volume attachment will NOT react to volume changes 261 err = s.State.SetVolumeInfo(volumeTag, state.VolumeInfo{VolumeId: "vol-123"}) 262 c.Assert(err, jc.ErrorIsNil) 263 wc.AssertNoChange() 264 265 err = s.State.SetVolumeAttachmentInfo( 266 machineTag, volumeTag, state.VolumeAttachmentInfo{ 267 DeviceName: "xvdf1", 268 }, 269 ) 270 c.Assert(err, jc.ErrorIsNil) 271 wc.AssertOneChange() 272 } 273 274 func (s *VolumeStateSuite) TestWatchModelVolumes(c *gc.C) { 275 service := s.setupMixedScopeStorageService(c, "block") 276 addUnit := func() { 277 u, err := service.AddUnit() 278 c.Assert(err, jc.ErrorIsNil) 279 err = s.State.AssignUnit(u, state.AssignCleanEmpty) 280 c.Assert(err, jc.ErrorIsNil) 281 } 282 addUnit() 283 284 w := s.State.WatchModelVolumes() 285 defer testing.AssertStop(c, w) 286 wc := testing.NewStringsWatcherC(c, s.State, w) 287 wc.AssertChangeInSingleEvent("0") // initial 288 wc.AssertNoChange() 289 290 addUnit() 291 wc.AssertChangeInSingleEvent("3") 292 wc.AssertNoChange() 293 294 err := s.State.DestroyVolume(names.NewVolumeTag("0")) 295 c.Assert(err, jc.ErrorIsNil) 296 wc.AssertChangeInSingleEvent("0") // dying 297 wc.AssertNoChange() 298 299 err = s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0")) 300 c.Assert(err, jc.ErrorIsNil) 301 err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0")) 302 c.Assert(err, jc.ErrorIsNil) 303 304 err = s.State.RemoveVolume(names.NewVolumeTag("0")) 305 c.Assert(err, jc.ErrorIsNil) 306 wc.AssertChangeInSingleEvent("0") // removed 307 wc.AssertNoChange() 308 } 309 310 func (s *VolumeStateSuite) TestWatchEnvironVolumeAttachments(c *gc.C) { 311 service := s.setupMixedScopeStorageService(c, "block") 312 addUnit := func() { 313 u, err := service.AddUnit() 314 c.Assert(err, jc.ErrorIsNil) 315 err = s.State.AssignUnit(u, state.AssignCleanEmpty) 316 c.Assert(err, jc.ErrorIsNil) 317 } 318 addUnit() 319 320 w := s.State.WatchEnvironVolumeAttachments() 321 defer testing.AssertStop(c, w) 322 wc := testing.NewStringsWatcherC(c, s.State, w) 323 wc.AssertChangeInSingleEvent("0:0") // initial 324 wc.AssertNoChange() 325 326 addUnit() 327 wc.AssertChangeInSingleEvent("1:3") 328 wc.AssertNoChange() 329 330 err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0")) 331 c.Assert(err, jc.ErrorIsNil) 332 wc.AssertChangeInSingleEvent("0:0") // dying 333 wc.AssertNoChange() 334 335 err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0")) 336 c.Assert(err, jc.ErrorIsNil) 337 wc.AssertChangeInSingleEvent("0:0") // removed 338 wc.AssertNoChange() 339 } 340 341 func (s *VolumeStateSuite) TestWatchMachineVolumes(c *gc.C) { 342 service := s.setupMixedScopeStorageService(c, "block") 343 addUnit := func() { 344 u, err := service.AddUnit() 345 c.Assert(err, jc.ErrorIsNil) 346 err = s.State.AssignUnit(u, state.AssignCleanEmpty) 347 c.Assert(err, jc.ErrorIsNil) 348 } 349 addUnit() 350 351 w := s.State.WatchMachineVolumes(names.NewMachineTag("0")) 352 defer testing.AssertStop(c, w) 353 wc := testing.NewStringsWatcherC(c, s.State, w) 354 wc.AssertChangeInSingleEvent("0/1", "0/2") // initial 355 wc.AssertNoChange() 356 357 addUnit() 358 // no change, since we're only interested in the one machine. 359 wc.AssertNoChange() 360 361 err := s.State.DestroyVolume(names.NewVolumeTag("0/1")) 362 c.Assert(err, jc.ErrorIsNil) 363 wc.AssertChangeInSingleEvent("0/1") // dying 364 wc.AssertNoChange() 365 366 err = s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0/1")) 367 c.Assert(err, jc.ErrorIsNil) 368 err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/1")) 369 c.Assert(err, jc.ErrorIsNil) 370 371 err = s.State.RemoveVolume(names.NewVolumeTag("0/1")) 372 c.Assert(err, jc.ErrorIsNil) 373 wc.AssertChangeInSingleEvent("0/1") // removed 374 wc.AssertNoChange() 375 } 376 377 func (s *VolumeStateSuite) TestWatchMachineVolumeAttachments(c *gc.C) { 378 service := s.setupMixedScopeStorageService(c, "block") 379 addUnit := func(to *state.Machine) (u *state.Unit, m *state.Machine) { 380 var err error 381 u, err = service.AddUnit() 382 c.Assert(err, jc.ErrorIsNil) 383 if to != nil { 384 err = u.AssignToMachine(to) 385 c.Assert(err, jc.ErrorIsNil) 386 return u, to 387 } 388 err = s.State.AssignUnit(u, state.AssignCleanEmpty) 389 c.Assert(err, jc.ErrorIsNil) 390 mid, err := u.AssignedMachineId() 391 c.Assert(err, jc.ErrorIsNil) 392 m, err = s.State.Machine(mid) 393 c.Assert(err, jc.ErrorIsNil) 394 return u, m 395 } 396 _, m0 := addUnit(nil) 397 398 w := s.State.WatchMachineVolumeAttachments(names.NewMachineTag("0")) 399 defer testing.AssertStop(c, w) 400 wc := testing.NewStringsWatcherC(c, s.State, w) 401 wc.AssertChangeInSingleEvent("0:0/1", "0:0/2") // initial 402 wc.AssertNoChange() 403 404 addUnit(nil) 405 // no change, since we're only interested in the one machine. 406 wc.AssertNoChange() 407 408 err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0")) 409 c.Assert(err, jc.ErrorIsNil) 410 // no change, since we're only interested in attachments of 411 // machine-scoped volumes. 412 wc.AssertNoChange() 413 414 err = s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0/1")) 415 c.Assert(err, jc.ErrorIsNil) 416 wc.AssertChangeInSingleEvent("0:0/1") // dying 417 wc.AssertNoChange() 418 419 err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/1")) 420 c.Assert(err, jc.ErrorIsNil) 421 wc.AssertChangeInSingleEvent("0:0/1") // removed 422 wc.AssertNoChange() 423 424 addUnit(m0) 425 wc.AssertChangeInSingleEvent("0:0/7", "0:0/8") // added 426 } 427 428 func (s *VolumeStateSuite) TestParseVolumeAttachmentId(c *gc.C) { 429 assertValid := func(id string, m names.MachineTag, v names.VolumeTag) { 430 machineTag, volumeTag, err := state.ParseVolumeAttachmentId(id) 431 c.Assert(err, jc.ErrorIsNil) 432 c.Assert(machineTag, gc.Equals, m) 433 c.Assert(volumeTag, gc.Equals, v) 434 } 435 assertValid("0:0", names.NewMachineTag("0"), names.NewVolumeTag("0")) 436 assertValid("0:0/1", names.NewMachineTag("0"), names.NewVolumeTag("0/1")) 437 assertValid("0/lxc/0:1", names.NewMachineTag("0/lxc/0"), names.NewVolumeTag("1")) 438 } 439 440 func (s *VolumeStateSuite) TestParseVolumeAttachmentIdError(c *gc.C) { 441 assertError := func(id, expect string) { 442 _, _, err := state.ParseVolumeAttachmentId(id) 443 c.Assert(err, gc.ErrorMatches, expect) 444 } 445 assertError("", `invalid volume attachment ID ""`) 446 assertError("0", `invalid volume attachment ID "0"`) 447 assertError("0:foo", `invalid volume attachment ID "0:foo"`) 448 assertError("bar:0", `invalid volume attachment ID "bar:0"`) 449 } 450 451 func (s *VolumeStateSuite) TestAllVolumes(c *gc.C) { 452 _, expected, _ := s.assertCreateVolumes(c) 453 454 volumes, err := s.State.AllVolumes() 455 c.Assert(err, jc.ErrorIsNil) 456 tags := make([]names.VolumeTag, len(volumes)) 457 for i, v := range volumes { 458 tags[i] = v.VolumeTag() 459 } 460 c.Assert(tags, jc.SameContents, expected) 461 } 462 463 func (s *VolumeStateSuite) assertCreateVolumes(c *gc.C) (_ *state.Machine, all, persistent []names.VolumeTag) { 464 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 465 Series: "quantal", 466 Jobs: []state.MachineJob{state.JobHostUnits}, 467 Volumes: []state.MachineVolumeParams{{ 468 Volume: state.VolumeParams{Pool: "persistent-block", Size: 1024}, 469 }, { 470 Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, 471 }, { 472 Volume: state.VolumeParams{Pool: "static", Size: 2048}, 473 }}, 474 }) 475 c.Assert(err, jc.ErrorIsNil) 476 assertMachineStorageRefs(c, s.State, machine.MachineTag()) 477 478 volume1 := s.volume(c, names.NewVolumeTag("0")) 479 volume2 := s.volume(c, names.NewVolumeTag("0/1")) 480 volume3 := s.volume(c, names.NewVolumeTag("2")) 481 482 c.Assert(volume1.LifeBinding(), gc.Equals, machine.MachineTag()) 483 c.Assert(volume2.LifeBinding(), gc.Equals, machine.MachineTag()) 484 c.Assert(volume3.LifeBinding(), gc.Equals, machine.MachineTag()) 485 486 volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-1"} 487 err = s.State.SetVolumeInfo(volume1.VolumeTag(), volumeInfoSet) 488 c.Assert(err, jc.ErrorIsNil) 489 490 volumeInfoSet = state.VolumeInfo{Size: 456, Persistent: false, VolumeId: "vol-2"} 491 err = s.State.SetVolumeInfo(volume2.VolumeTag(), volumeInfoSet) 492 c.Assert(err, jc.ErrorIsNil) 493 494 all = []names.VolumeTag{ 495 volume1.VolumeTag(), 496 volume2.VolumeTag(), 497 volume3.VolumeTag(), 498 } 499 persistent = []names.VolumeTag{ 500 volume1.VolumeTag(), 501 } 502 return machine, all, persistent 503 } 504 505 func (s *VolumeStateSuite) TestRemoveStorageInstanceUnassignsVolume(c *gc.C) { 506 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 507 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 508 c.Assert(err, jc.ErrorIsNil) 509 volume := s.storageInstanceVolume(c, storageTag) 510 c.Assert(err, jc.ErrorIsNil) 511 volumeTag := volume.VolumeTag() 512 513 err = s.State.DestroyStorageInstance(storageTag) 514 c.Assert(err, jc.ErrorIsNil) 515 err = s.State.DestroyStorageAttachment(storageTag, u.UnitTag()) 516 c.Assert(err, jc.ErrorIsNil) 517 518 // The storage instance and attachment are dying, but not yet 519 // removed from state. The volume should still be assigned. 520 s.storageInstanceVolume(c, storageTag) 521 522 err = s.State.RemoveStorageAttachment(storageTag, u.UnitTag()) 523 c.Assert(err, jc.ErrorIsNil) 524 525 // The storage instance is now gone; the volume should no longer 526 // be assigned to the storage. 527 _, err = s.State.StorageInstanceVolume(storageTag) 528 c.Assert(err, gc.ErrorMatches, `volume for storage instance "data/0" not found`) 529 530 // The volume should not have been removed, though. 531 s.volume(c, volumeTag) 532 } 533 534 func (s *VolumeStateSuite) TestSetVolumeAttachmentInfoVolumeNotProvisioned(c *gc.C) { 535 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 536 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 537 c.Assert(err, jc.ErrorIsNil) 538 assignedMachineId, err := u.AssignedMachineId() 539 c.Assert(err, jc.ErrorIsNil) 540 machineTag := names.NewMachineTag(assignedMachineId) 541 542 volume := s.storageInstanceVolume(c, storageTag) 543 volumeTag := volume.VolumeTag() 544 545 err = s.State.SetVolumeAttachmentInfo( 546 machineTag, volumeTag, state.VolumeAttachmentInfo{ 547 DeviceName: "xvdf1", 548 }, 549 ) 550 c.Assert(err, gc.ErrorMatches, `cannot set info for volume attachment 0/0:0: volume "0/0" not provisioned`) 551 } 552 553 func (s *VolumeStateSuite) TestDestroyVolume(c *gc.C) { 554 volume, _ := s.setupVolumeAttachment(c) 555 assertDestroy := func() { 556 err := s.State.DestroyVolume(volume.VolumeTag()) 557 c.Assert(err, jc.ErrorIsNil) 558 volume = s.volume(c, volume.VolumeTag()) 559 c.Assert(volume.Life(), gc.Equals, state.Dying) 560 } 561 defer state.SetBeforeHooks(c, s.State, assertDestroy).Check() 562 assertDestroy() 563 } 564 565 func (s *VolumeStateSuite) TestDestroyVolumeNoAttachments(c *gc.C) { 566 volume, machine := s.setupVolumeAttachment(c) 567 568 err := s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag()) 569 c.Assert(err, jc.ErrorIsNil) 570 571 defer state.SetBeforeHooks(c, s.State, func() { 572 err := s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag()) 573 c.Assert(err, jc.ErrorIsNil) 574 }).Check() 575 576 err = s.State.DestroyVolume(volume.VolumeTag()) 577 c.Assert(err, jc.ErrorIsNil) 578 volume = s.volume(c, volume.VolumeTag()) 579 580 // There are no more attachments, so the volume should 581 // have been progressed directly to Dead. 582 c.Assert(volume.Life(), gc.Equals, state.Dead) 583 } 584 585 func (s *VolumeStateSuite) TestRemoveVolume(c *gc.C) { 586 volume, machine := s.setupVolumeAttachment(c) 587 err := s.State.DestroyVolume(volume.VolumeTag()) 588 c.Assert(err, jc.ErrorIsNil) 589 err = s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag()) 590 c.Assert(err, jc.ErrorIsNil) 591 err = s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag()) 592 c.Assert(err, jc.ErrorIsNil) 593 assertRemove := func() { 594 err = s.State.RemoveVolume(volume.VolumeTag()) 595 c.Assert(err, jc.ErrorIsNil) 596 _, err = s.State.Volume(volume.VolumeTag()) 597 c.Assert(err, jc.Satisfies, errors.IsNotFound) 598 } 599 defer state.SetBeforeHooks(c, s.State, assertRemove).Check() 600 assertRemove() 601 } 602 603 func (s *VolumeStateSuite) TestRemoveVolumeNotFound(c *gc.C) { 604 err := s.State.RemoveVolume(names.NewVolumeTag("42")) 605 c.Assert(err, jc.ErrorIsNil) 606 } 607 608 func (s *VolumeStateSuite) TestRemoveVolumeNotDead(c *gc.C) { 609 volume, _ := s.setupVolumeAttachment(c) 610 err := s.State.RemoveVolume(volume.VolumeTag()) 611 c.Assert(err, gc.ErrorMatches, "removing volume 0/0: volume is not dead") 612 err = s.State.DestroyVolume(volume.VolumeTag()) 613 c.Assert(err, jc.ErrorIsNil) 614 err = s.State.RemoveVolume(volume.VolumeTag()) 615 c.Assert(err, gc.ErrorMatches, "removing volume 0/0: volume is not dead") 616 } 617 618 func (s *VolumeStateSuite) TestDetachVolume(c *gc.C) { 619 volume, machine := s.setupVolumeAttachment(c) 620 assertDetach := func() { 621 err := s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag()) 622 c.Assert(err, jc.ErrorIsNil) 623 attachment := s.volumeAttachment(c, machine.MachineTag(), volume.VolumeTag()) 624 c.Assert(attachment.Life(), gc.Equals, state.Dying) 625 } 626 defer state.SetBeforeHooks(c, s.State, assertDetach).Check() 627 assertDetach() 628 } 629 630 func (s *VolumeStateSuite) TestRemoveLastVolumeAttachment(c *gc.C) { 631 volume, machine := s.setupVolumeAttachment(c) 632 633 err := s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag()) 634 c.Assert(err, jc.ErrorIsNil) 635 636 err = s.State.DestroyVolume(volume.VolumeTag()) 637 c.Assert(err, jc.ErrorIsNil) 638 volume = s.volume(c, volume.VolumeTag()) 639 c.Assert(volume.Life(), gc.Equals, state.Dying) 640 641 err = s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag()) 642 c.Assert(err, jc.ErrorIsNil) 643 644 // The volume was Dying when the last attachment was 645 // removed, so the volume should now be Dead. 646 volume = s.volume(c, volume.VolumeTag()) 647 c.Assert(volume.Life(), gc.Equals, state.Dead) 648 } 649 650 func (s *VolumeStateSuite) TestRemoveLastVolumeAttachmentConcurrently(c *gc.C) { 651 volume, machine := s.setupVolumeAttachment(c) 652 653 err := s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag()) 654 c.Assert(err, jc.ErrorIsNil) 655 656 defer state.SetBeforeHooks(c, s.State, func() { 657 err := s.State.DestroyVolume(volume.VolumeTag()) 658 c.Assert(err, jc.ErrorIsNil) 659 volume := s.volume(c, volume.VolumeTag()) 660 c.Assert(volume.Life(), gc.Equals, state.Dying) 661 }).Check() 662 663 err = s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag()) 664 c.Assert(err, jc.ErrorIsNil) 665 666 // Last attachment was removed, and the volume was (concurrently) 667 // destroyed, so the volume should be Dead. 668 volume = s.volume(c, volume.VolumeTag()) 669 c.Assert(volume.Life(), gc.Equals, state.Dead) 670 } 671 672 func (s *VolumeStateSuite) TestRemoveVolumeAttachmentNotFound(c *gc.C) { 673 err := s.State.RemoveVolumeAttachment(names.NewMachineTag("42"), names.NewVolumeTag("42")) 674 c.Assert(err, jc.Satisfies, errors.IsNotFound) 675 c.Assert(err, gc.ErrorMatches, `removing attachment of volume 42 from machine 42: volume "42" on machine "42" not found`) 676 } 677 678 func (s *VolumeStateSuite) TestRemoveVolumeAttachmentConcurrently(c *gc.C) { 679 volume, machine := s.setupVolumeAttachment(c) 680 err := s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag()) 681 c.Assert(err, jc.ErrorIsNil) 682 remove := func() { 683 err := s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag()) 684 c.Assert(err, jc.ErrorIsNil) 685 assertMachineStorageRefs(c, s.State, machine.MachineTag()) 686 } 687 defer state.SetBeforeHooks(c, s.State, remove).Check() 688 remove() 689 } 690 691 func (s *VolumeStateSuite) TestRemoveVolumeAttachmentAlive(c *gc.C) { 692 volume, machine := s.setupVolumeAttachment(c) 693 err := s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag()) 694 c.Assert(err, gc.ErrorMatches, "removing attachment of volume 0/0 from machine 0: volume attachment is not dying") 695 } 696 697 func (s *VolumeStateSuite) TestRemoveMachineRemovesVolumes(c *gc.C) { 698 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 699 Series: "quantal", 700 Jobs: []state.MachineJob{state.JobHostUnits}, 701 Volumes: []state.MachineVolumeParams{{ 702 Volume: state.VolumeParams{Pool: "persistent-block", Size: 1024}, // unprovisioned 703 }, { 704 Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // provisioned 705 }, { 706 Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // unprovisioned 707 }, { 708 Volume: state.VolumeParams{Pool: "loop-pool", Size: 2048}, // provisioned, non-persistent 709 }, { 710 Volume: state.VolumeParams{Pool: "static", Size: 2048}, // provisioned 711 }, { 712 Volume: state.VolumeParams{Pool: "static", Size: 2048}, // unprovisioned 713 }}, 714 }) 715 c.Assert(err, jc.ErrorIsNil) 716 717 volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-1"} 718 err = s.State.SetVolumeInfo(names.NewVolumeTag("0/1"), volumeInfoSet) 719 c.Assert(err, jc.ErrorIsNil) 720 volumeInfoSet = state.VolumeInfo{Size: 456, Persistent: false, VolumeId: "vol-2"} 721 err = s.State.SetVolumeInfo(names.NewVolumeTag("0/3"), volumeInfoSet) 722 c.Assert(err, jc.ErrorIsNil) 723 volumeInfoSet = state.VolumeInfo{Size: 789, Persistent: false, VolumeId: "vol-3"} 724 err = s.State.SetVolumeInfo(names.NewVolumeTag("4"), volumeInfoSet) 725 c.Assert(err, jc.ErrorIsNil) 726 727 allVolumes, err := s.State.AllVolumes() 728 c.Assert(err, jc.ErrorIsNil) 729 730 persistentVolumes := make([]state.Volume, 0, len(allVolumes)) 731 for _, v := range allVolumes { 732 info, err := v.Info() 733 if err == nil && info.Persistent { 734 persistentVolumes = append(persistentVolumes, v) 735 } 736 } 737 c.Assert(len(allVolumes), jc.GreaterThan, len(persistentVolumes)) 738 739 c.Assert(machine.Destroy(), jc.ErrorIsNil) 740 741 // Cannot advance to Dead while there are persistent, or 742 // unprovisioned dynamic volumes (regardless of scope). 743 err = machine.EnsureDead() 744 c.Assert(err, jc.Satisfies, state.IsHasAttachmentsError) 745 c.Assert(err, gc.ErrorMatches, "machine 0 has attachments \\[volume-0 volume-0-1 volume-0-2\\]") 746 s.obliterateVolumeAttachment(c, machine.MachineTag(), names.NewVolumeTag("0")) 747 s.obliterateVolumeAttachment(c, machine.MachineTag(), names.NewVolumeTag("0/1")) 748 s.obliterateVolumeAttachment(c, machine.MachineTag(), names.NewVolumeTag("0/2")) 749 c.Assert(machine.EnsureDead(), jc.ErrorIsNil) 750 c.Assert(machine.Remove(), jc.ErrorIsNil) 751 752 // Machine is gone: non-persistent and unprovisioned static or machine- 753 // scoped storage should be gone too. 754 allVolumes, err = s.State.AllVolumes() 755 c.Assert(err, jc.ErrorIsNil) 756 // We should only have the persistent volume and the loop devices remaining. 757 remaining := make(set.Strings) 758 for _, v := range allVolumes { 759 remaining.Add(v.Tag().String()) 760 } 761 c.Assert(remaining.SortedValues(), jc.DeepEquals, []string{ 762 "volume-0", "volume-0-1", "volume-0-2", 763 }) 764 765 attachments, err := s.State.MachineVolumeAttachments(machine.MachineTag()) 766 c.Assert(err, jc.ErrorIsNil) 767 c.Assert(attachments, gc.HasLen, 0) 768 } 769 770 func (s *VolumeStateSuite) TestEnsureMachineDeadAddVolumeConcurrently(c *gc.C) { 771 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 772 Series: "quantal", 773 Jobs: []state.MachineJob{state.JobHostUnits}, 774 Volumes: []state.MachineVolumeParams{{ 775 Volume: state.VolumeParams{Pool: "static", Size: 1024}, 776 }}, 777 }) 778 c.Assert(err, jc.ErrorIsNil) 779 780 addVolume := func() { 781 _, u, _ := s.setupSingleStorage(c, "block", "environscoped") 782 err := u.AssignToMachine(machine) 783 c.Assert(err, jc.ErrorIsNil) 784 s.obliterateUnit(c, u.UnitTag()) 785 } 786 defer state.SetBeforeHooks(c, s.State, addVolume).Check() 787 788 // The static volume the machine was provisioned with does not matter, 789 // but the volume added concurrently does. 790 err = machine.EnsureDead() 791 c.Assert(err, gc.ErrorMatches, `machine 0 has attachments \[volume-1\]`) 792 } 793 794 func (s *VolumeStateSuite) TestEnsureMachineDeadRemoveVolumeConcurrently(c *gc.C) { 795 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 796 Series: "quantal", 797 Jobs: []state.MachineJob{state.JobHostUnits}, 798 Volumes: []state.MachineVolumeParams{{ 799 Volume: state.VolumeParams{Pool: "static", Size: 1024}, 800 }}, 801 }) 802 c.Assert(err, jc.ErrorIsNil) 803 804 removeVolume := func() { 805 s.obliterateVolume(c, names.NewVolumeTag("0")) 806 } 807 defer state.SetBeforeHooks(c, s.State, removeVolume).Check() 808 809 // Removing a volume concurrently does not cause a transaction failure. 810 err = machine.EnsureDead() 811 c.Assert(err, jc.ErrorIsNil) 812 } 813 814 func (s *VolumeStateSuite) TestVolumeBindingMachine(c *gc.C) { 815 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 816 Series: "quantal", 817 Jobs: []state.MachineJob{state.JobHostUnits}, 818 Volumes: []state.MachineVolumeParams{{ 819 Volume: state.VolumeParams{Pool: "environscoped", Size: 1024}, 820 }}, 821 }) 822 c.Assert(err, jc.ErrorIsNil) 823 824 // Volumes created unassigned to a storage instance are 825 // bound to the initially attached machine. 826 volume := s.volume(c, names.NewVolumeTag("0")) 827 c.Assert(volume.LifeBinding(), gc.Equals, machine.Tag()) 828 c.Assert(volume.Life(), gc.Equals, state.Alive) 829 830 err = s.State.DetachVolume(machine.MachineTag(), volume.VolumeTag()) 831 c.Assert(err, jc.ErrorIsNil) 832 err = s.State.RemoveVolumeAttachment(machine.MachineTag(), volume.VolumeTag()) 833 c.Assert(err, jc.ErrorIsNil) 834 volume = s.volume(c, volume.VolumeTag()) 835 c.Assert(volume.Life(), gc.Equals, state.Dead) 836 837 // TODO(axw) when we can assign storage to an existing volume, we 838 // should test that a machine-bound volume is not destroyed when 839 // its assigned storage instance is removed. 840 } 841 842 func (s *VolumeStateSuite) TestVolumeBindingStorage(c *gc.C) { 843 // Volumes created assigned to a storage instance are bound 844 // to the storage instance. 845 volume, _ := s.setupVolumeAttachment(c) 846 storageTag, err := volume.StorageInstance() 847 c.Assert(err, jc.ErrorIsNil) 848 c.Assert(volume.LifeBinding(), gc.Equals, storageTag) 849 850 err = s.State.DestroyStorageInstance(storageTag) 851 c.Assert(err, jc.ErrorIsNil) 852 attachments, err := s.State.StorageAttachments(storageTag) 853 c.Assert(err, jc.ErrorIsNil) 854 for _, a := range attachments { 855 err = s.State.DestroyStorageAttachment(storageTag, a.Unit()) 856 c.Assert(err, jc.ErrorIsNil) 857 err = s.State.RemoveStorageAttachment(storageTag, a.Unit()) 858 c.Assert(err, jc.ErrorIsNil) 859 } 860 861 // The storage instance should be removed, 862 // and the volume should be Dying. 863 _, err = s.State.StorageInstance(storageTag) 864 c.Assert(err, jc.Satisfies, errors.IsNotFound) 865 volume = s.volume(c, volume.VolumeTag()) 866 c.Assert(volume.Life(), gc.Equals, state.Dying) 867 } 868 869 func (s *VolumeStateSuite) setupVolumeAttachment(c *gc.C) (state.Volume, *state.Machine) { 870 _, u, storageTag := s.setupSingleStorage(c, "block", "loop-pool") 871 err := s.State.AssignUnit(u, state.AssignCleanEmpty) 872 c.Assert(err, jc.ErrorIsNil) 873 assignedMachineId, err := u.AssignedMachineId() 874 c.Assert(err, jc.ErrorIsNil) 875 return s.storageInstanceVolume(c, storageTag), s.machine(c, assignedMachineId) 876 }