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