github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/featuretests/storage_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package featuretests 5 6 import ( 7 "strings" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/cmd/envcmd" 16 cmdstorage "github.com/juju/juju/cmd/juju/storage" 17 jujutesting "github.com/juju/juju/juju/testing" 18 "github.com/juju/juju/provider/ec2" 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/storage/poolmanager" 21 "github.com/juju/juju/storage/provider" 22 "github.com/juju/juju/storage/provider/registry" 23 "github.com/juju/juju/testing" 24 ) 25 26 const ( 27 testPool = "block" 28 testPersistentPool = "block-persistent" 29 ) 30 31 func setupTestStorageSupport(c *gc.C, s *state.State) { 32 stsetts := state.NewStateSettings(s) 33 poolManager := poolmanager.New(stsetts) 34 _, err := poolManager.Create(testPool, provider.LoopProviderType, map[string]interface{}{"it": "works"}) 35 c.Assert(err, jc.ErrorIsNil) 36 _, err = poolManager.Create(testPersistentPool, ec2.EBS_ProviderType, map[string]interface{}{"persistent": true}) 37 c.Assert(err, jc.ErrorIsNil) 38 39 registry.RegisterEnvironStorageProviders("dummy", ec2.EBS_ProviderType) 40 registry.RegisterEnvironStorageProviders("dummyenv", ec2.EBS_ProviderType) 41 } 42 43 func makeStorageCons(pool string, size, count uint64) state.StorageConstraints { 44 return state.StorageConstraints{Pool: pool, Size: size, Count: count} 45 } 46 47 func createUnitWithStorage(c *gc.C, s *jujutesting.JujuConnSuite, poolName string) string { 48 ch := s.AddTestingCharm(c, "storage-block") 49 storage := map[string]state.StorageConstraints{ 50 "data": makeStorageCons(poolName, 1024, 1), 51 } 52 service := s.AddTestingServiceWithStorage(c, "storage-block", ch, storage) 53 unit, err := service.AddUnit() 54 c.Assert(err, jc.ErrorIsNil) 55 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 56 c.Assert(err, jc.ErrorIsNil) 57 58 return unit.Tag().Id() 59 } 60 61 type cmdStorageSuite struct { 62 jujutesting.RepoSuite 63 } 64 65 func (s *cmdStorageSuite) SetUpTest(c *gc.C) { 66 s.RepoSuite.SetUpTest(c) 67 setupTestStorageSupport(c, s.State) 68 } 69 70 func runShow(c *gc.C, args ...string) *cmd.Context { 71 context, err := testing.RunCommand(c, envcmd.Wrap(&cmdstorage.ShowCommand{}), args...) 72 c.Assert(err, jc.ErrorIsNil) 73 return context 74 } 75 76 func (s *cmdStorageSuite) TestStorageShowEmpty(c *gc.C) { 77 _, err := testing.RunCommand(c, envcmd.Wrap(&cmdstorage.ShowCommand{})) 78 c.Assert(errors.Cause(err), gc.ErrorMatches, ".*must specify storage id.*") 79 } 80 81 func (s *cmdStorageSuite) TestStorageShowInvalidId(c *gc.C) { 82 _, err := testing.RunCommand(c, envcmd.Wrap(&cmdstorage.ShowCommand{}), "fluff") 83 c.Assert(errors.Cause(err), gc.ErrorMatches, ".*invalid storage id.*") 84 } 85 86 func (s *cmdStorageSuite) TestStorageShow(c *gc.C) { 87 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 88 89 context := runShow(c, "data/0") 90 expected := ` 91 storage-block/0: 92 data/0: 93 storage: data 94 kind: block 95 status: pending 96 persistent: false 97 `[1:] 98 c.Assert(testing.Stdout(context), gc.Equals, expected) 99 } 100 101 func (s *cmdStorageSuite) TestStorageShowOneMatchingFilter(c *gc.C) { 102 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 103 104 context := runShow(c, "data/0", "fluff/0") 105 expected := ` 106 storage-block/0: 107 data/0: 108 storage: data 109 kind: block 110 status: pending 111 persistent: false 112 `[1:] 113 c.Assert(testing.Stdout(context), gc.Equals, expected) 114 } 115 116 func (s *cmdStorageSuite) TestStorageShowNoMatch(c *gc.C) { 117 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 118 context := runShow(c, "fluff/0") 119 c.Assert(testing.Stdout(context), gc.Equals, "{}\n") 120 } 121 122 func runList(c *gc.C) *cmd.Context { 123 context, err := testing.RunCommand(c, envcmd.Wrap(&cmdstorage.ListCommand{})) 124 c.Assert(err, jc.ErrorIsNil) 125 return context 126 } 127 128 func (s *cmdStorageSuite) TestStorageListEmpty(c *gc.C) { 129 context := runList(c) 130 c.Assert(testing.Stdout(context), gc.Equals, "") 131 } 132 133 func (s *cmdStorageSuite) TestStorageList(c *gc.C) { 134 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 135 136 context := runList(c) 137 expected := ` 138 [Storage] 139 UNIT ID LOCATION STATUS PERSISTENT 140 storage-block/0 data/0 pending false 141 142 `[1:] 143 c.Assert(testing.Stdout(context), gc.Equals, expected) 144 } 145 146 func (s *cmdStorageSuite) TestStorageListPersistent(c *gc.C) { 147 createUnitWithStorage(c, &s.JujuConnSuite, testPersistentPool) 148 149 context := runList(c) 150 expected := ` 151 [Storage] 152 UNIT ID LOCATION STATUS PERSISTENT 153 storage-block/0 data/0 pending true 154 155 `[1:] 156 c.Assert(testing.Stdout(context), gc.Equals, expected) 157 } 158 159 func (s *cmdStorageSuite) TestStoragePersistentProvisioned(c *gc.C) { 160 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 161 vol, err := s.State.StorageInstanceVolume(names.NewStorageTag("data/0")) 162 c.Assert(err, jc.ErrorIsNil) 163 err = s.State.SetVolumeInfo(vol.VolumeTag(), state.VolumeInfo{ 164 Size: 1024, 165 Persistent: true, 166 VolumeId: "vol-ume", 167 }) 168 c.Assert(err, jc.ErrorIsNil) 169 170 context := runShow(c, "data/0") 171 expected := ` 172 storage-block/0: 173 data/0: 174 storage: data 175 kind: block 176 status: pending 177 persistent: true 178 `[1:] 179 c.Assert(testing.Stdout(context), gc.Equals, expected) 180 } 181 182 func (s *cmdStorageSuite) TestStoragePersistentUnprovisioned(c *gc.C) { 183 createUnitWithStorage(c, &s.JujuConnSuite, testPersistentPool) 184 185 context := runShow(c, "data/0") 186 expected := ` 187 storage-block/0: 188 data/0: 189 storage: data 190 kind: block 191 status: pending 192 persistent: true 193 `[1:] 194 c.Assert(testing.Stdout(context), gc.Equals, expected) 195 } 196 197 func runPoolList(c *gc.C, args ...string) *cmd.Context { 198 context, err := testing.RunCommand(c, envcmd.Wrap(&cmdstorage.PoolListCommand{}), args...) 199 c.Assert(err, jc.ErrorIsNil) 200 return context 201 } 202 203 func (s *cmdStorageSuite) TestListPools(c *gc.C) { 204 context := runPoolList(c) 205 expected := ` 206 block: 207 provider: loop 208 attrs: 209 it: works 210 block-persistent: 211 provider: ebs 212 attrs: 213 persistent: true 214 ebs: 215 provider: ebs 216 loop: 217 provider: loop 218 rootfs: 219 provider: rootfs 220 tmpfs: 221 provider: tmpfs 222 `[1:] 223 c.Assert(testing.Stdout(context), gc.Equals, expected) 224 } 225 226 func (s *cmdStorageSuite) TestListPoolsTabular(c *gc.C) { 227 context := runPoolList(c, "--format", "tabular") 228 expected := ` 229 NAME PROVIDER ATTRS 230 block loop it=works 231 block-persistent ebs persistent=true 232 ebs ebs 233 loop loop 234 rootfs rootfs 235 tmpfs tmpfs 236 237 `[1:] 238 c.Assert(testing.Stdout(context), gc.Equals, expected) 239 } 240 241 func (s *cmdStorageSuite) TestListPoolsName(c *gc.C) { 242 context := runPoolList(c, "--name", "block") 243 expected := ` 244 block: 245 provider: loop 246 attrs: 247 it: works 248 `[1:] 249 c.Assert(testing.Stdout(context), gc.Equals, expected) 250 } 251 252 func (s *cmdStorageSuite) TestListPoolsNameNoMatch(c *gc.C) { 253 context := runPoolList(c, "--name", "cranky") 254 c.Assert(testing.Stdout(context), gc.Equals, "") 255 } 256 257 func (s *cmdStorageSuite) TestListPoolsNameInvalid(c *gc.C) { 258 _, err := testing.RunCommand(c, envcmd.Wrap(&cmdstorage.PoolListCommand{}), "--name", "9oops") 259 c.Assert(errors.Cause(err), gc.ErrorMatches, ".*not valid.*") 260 } 261 262 func (s *cmdStorageSuite) TestListPoolsProvider(c *gc.C) { 263 context := runPoolList(c, "--provider", "ebs") 264 expected := ` 265 block-persistent: 266 provider: ebs 267 attrs: 268 persistent: true 269 ebs: 270 provider: ebs 271 `[1:] 272 c.Assert(testing.Stdout(context), gc.Equals, expected) 273 } 274 275 func (s *cmdStorageSuite) registerTmpProviderType(c *gc.C) { 276 cfg, err := s.State.EnvironConfig() 277 c.Assert(err, jc.ErrorIsNil) 278 registry.RegisterEnvironStorageProviders(cfg.Name(), provider.TmpfsProviderType) 279 } 280 281 func (s *cmdStorageSuite) TestListPoolsProviderNoMatch(c *gc.C) { 282 s.registerTmpProviderType(c) 283 context := runPoolList(c, "--provider", string(provider.TmpfsProviderType)) 284 expected := ` 285 tmpfs: 286 provider: tmpfs 287 `[1:] 288 c.Assert(testing.Stdout(context), gc.Equals, expected) 289 } 290 291 func (s *cmdStorageSuite) TestListPoolsProviderUnregistered(c *gc.C) { 292 _, err := testing.RunCommand(c, envcmd.Wrap(&cmdstorage.PoolListCommand{}), "--provider", "oops") 293 c.Assert(errors.Cause(err), gc.ErrorMatches, ".*not supported.*") 294 } 295 296 func (s *cmdStorageSuite) TestListPoolsNameAndProvider(c *gc.C) { 297 context := runPoolList(c, "--name", "block", "--provider", "loop") 298 expected := ` 299 block: 300 provider: loop 301 attrs: 302 it: works 303 `[1:] 304 c.Assert(testing.Stdout(context), gc.Equals, expected) 305 } 306 307 func (s *cmdStorageSuite) TestListPoolsProviderAndNotName(c *gc.C) { 308 context := runPoolList(c, "--name", "fluff", "--provider", "ebs") 309 // there is no pool that matches this name AND type 310 c.Assert(testing.Stdout(context), gc.Equals, "") 311 } 312 313 func (s *cmdStorageSuite) TestListPoolsNameAndNotProvider(c *gc.C) { 314 s.registerTmpProviderType(c) 315 context := runPoolList(c, "--name", "block", "--provider", string(provider.TmpfsProviderType)) 316 // no pool matches this name and this provider 317 c.Assert(testing.Stdout(context), gc.Equals, "") 318 } 319 320 func (s *cmdStorageSuite) TestListPoolsNotNameAndNotProvider(c *gc.C) { 321 s.registerTmpProviderType(c) 322 context := runPoolList(c, "--name", "fluff", "--provider", string(provider.TmpfsProviderType)) 323 c.Assert(testing.Stdout(context), gc.Equals, "") 324 } 325 326 func runPoolCreate(c *gc.C, args ...string) *cmd.Context { 327 context, err := testing.RunCommand(c, envcmd.Wrap(&cmdstorage.PoolCreateCommand{}), args...) 328 c.Assert(err, jc.ErrorIsNil) 329 return context 330 } 331 332 func (s *cmdStorageSuite) TestCreatePool(c *gc.C) { 333 pname := "ftPool" 334 context := runPoolCreate(c, pname, "loop", "smth=one") 335 c.Assert(testing.Stdout(context), gc.Equals, "") 336 assertPoolExists(c, s.State, pname, "loop", "smth=one") 337 } 338 339 func (s *cmdStorageSuite) assertCreatePoolError(c *gc.C, expected string, args ...string) { 340 _, err := testing.RunCommand(c, envcmd.Wrap(&cmdstorage.PoolCreateCommand{}), args...) 341 c.Assert(errors.Cause(err), gc.ErrorMatches, expected) 342 } 343 344 func (s *cmdStorageSuite) TestCreatePoolErrorNoAttrs(c *gc.C) { 345 s.assertCreatePoolError(c, ".*pool creation requires names, provider type and attrs for configuration.*", "loop", "ftPool") 346 } 347 348 func (s *cmdStorageSuite) TestCreatePoolErrorNoProvider(c *gc.C) { 349 s.assertCreatePoolError(c, ".*pool creation requires names, provider type and attrs for configuration.*", "oops provider", "smth=one") 350 } 351 352 func (s *cmdStorageSuite) TestCreatePoolErrorProviderType(c *gc.C) { 353 s.assertCreatePoolError(c, ".*not found.*", "loop", "ftPool", "smth=one") 354 } 355 356 func (s *cmdStorageSuite) TestCreatePoolDuplicateName(c *gc.C) { 357 pname := "ftPool" 358 context := runPoolCreate(c, pname, "loop", "smth=one") 359 c.Assert(testing.Stdout(context), gc.Equals, "") 360 assertPoolExists(c, s.State, pname, "loop", "smth=one") 361 s.assertCreatePoolError(c, ".*cannot overwrite existing settings*", pname, "loop", "smth=one") 362 } 363 364 func assertPoolExists(c *gc.C, st *state.State, pname, provider, attr string) { 365 stsetts := state.NewStateSettings(st) 366 poolManager := poolmanager.New(stsetts) 367 368 found, err := poolManager.List() 369 c.Assert(err, jc.ErrorIsNil) 370 c.Assert(len(found) > 0, jc.IsTrue) 371 372 exists := false 373 for _, one := range found { 374 if one.Name() == pname { 375 exists = true 376 c.Assert(string(one.Provider()), gc.Equals, provider) 377 // At this stage, only 1 attr is expected and checked 378 expectedAttrs := strings.Split(attr, "=") 379 value, ok := one.Attrs()[expectedAttrs[0]] 380 c.Assert(ok, jc.IsTrue) 381 c.Assert(value, gc.Equals, expectedAttrs[1]) 382 } 383 } 384 c.Assert(exists, jc.IsTrue) 385 } 386 387 func runVolumeList(c *gc.C, args ...string) *cmd.Context { 388 context, err := testing.RunCommand(c, 389 envcmd.Wrap(&cmdstorage.VolumeListCommand{}), args...) 390 c.Assert(err, jc.ErrorIsNil) 391 return context 392 } 393 394 func (s *cmdStorageSuite) TestListVolumeInvalidMachine(c *gc.C) { 395 context := runVolumeList(c, "abc", "--format", "yaml") 396 c.Assert(testing.Stdout(context), gc.Equals, "") 397 c.Assert(testing.Stderr(context), 398 gc.Matches, 399 `parsing machine tag machine-abc: "machine-abc" is not a valid machine tag 400 `) 401 } 402 403 func (s *cmdStorageSuite) TestListVolumeTabularFilterMatch(c *gc.C) { 404 createUnitWithStorage(c, &s.JujuConnSuite, testPersistentPool) 405 context := runVolumeList(c, "0") 406 expected := ` 407 MACHINE UNIT STORAGE DEVICE VOLUME ID SIZE STATE MESSAGE 408 0 storage-block/0 data/0 0 pending 409 410 `[1:] 411 c.Assert(testing.Stdout(context), gc.Equals, expected) 412 c.Assert(testing.Stderr(context), gc.Equals, "") 413 } 414 415 func runAddToUnit(c *gc.C, args ...string) *cmd.Context { 416 context, err := testing.RunCommand(c, envcmd.Wrap(&cmdstorage.AddCommand{}), args...) 417 c.Assert(err, jc.ErrorIsNil) 418 return context 419 } 420 421 func (s *cmdStorageSuite) TestStorageAddToUnitSuccess(c *gc.C) { 422 u := createUnitWithStorage(c, &s.JujuConnSuite, testPool) 423 instancesBefore, err := s.State.AllStorageInstances() 424 c.Assert(err, jc.ErrorIsNil) 425 volumesBefore, err := s.State.AllVolumes() 426 c.Assert(err, jc.ErrorIsNil) 427 s.assertStorageExist(c, instancesBefore, "data") 428 429 context := runAddToUnit(c, u, "allecto=1") 430 c.Assert(testing.Stdout(context), gc.Equals, "") 431 c.Assert(testing.Stderr(context), gc.Equals, "") 432 433 instancesAfter, err := s.State.AllStorageInstances() 434 c.Assert(err, jc.ErrorIsNil) 435 c.Assert(len(instancesAfter)-len(instancesBefore), gc.Equals, 1) 436 volumesAfter, err := s.State.AllVolumes() 437 c.Assert(err, jc.ErrorIsNil) 438 c.Assert(len(volumesAfter)-len(volumesBefore), gc.Equals, 1) 439 s.assertStorageExist(c, instancesAfter, "data", "allecto") 440 } 441 442 func (s *cmdStorageSuite) assertStorageExist(c *gc.C, 443 all []state.StorageInstance, 444 expected ...string) { 445 446 names := make([]string, len(all)) 447 for i, one := range all { 448 names[i] = one.StorageName() 449 } 450 c.Assert(names, jc.SameContents, expected) 451 } 452 453 func (s *cmdStorageSuite) TestStorageAddToUnitFailure(c *gc.C) { 454 context := runAddToUnit(c, "fluffyunit/0", "allecto=1") 455 c.Assert(testing.Stdout(context), gc.Equals, "") 456 c.Assert(testing.Stderr(context), gc.Equals, "fail: storage \"allecto\": permission denied\n") 457 } 458 459 func (s *cmdStorageSuite) TestStorageAddToUnitHasVolumes(c *gc.C) { 460 // Reproducing Bug1462146 461 u := createUnitWithFileSystemStorage(c, &s.JujuConnSuite, "ebs") 462 instancesBefore, err := s.State.AllStorageInstances() 463 c.Assert(err, jc.ErrorIsNil) 464 s.assertStorageExist(c, instancesBefore, "data") 465 volumesBefore, err := s.State.AllVolumes() 466 c.Assert(err, jc.ErrorIsNil) 467 c.Assert(volumesBefore, gc.HasLen, 1) 468 469 context := runList(c) 470 c.Assert(testing.Stdout(context), gc.Equals, ` 471 [Storage] 472 UNIT ID LOCATION STATUS PERSISTENT 473 storage-filesystem/0 data/0 pending false 474 475 `[1:]) 476 c.Assert(testing.Stderr(context), gc.Equals, "") 477 478 context = runAddToUnit(c, u, "data=ebs,1G") 479 c.Assert(testing.Stdout(context), gc.Equals, "") 480 c.Assert(testing.Stderr(context), gc.Equals, "") 481 482 instancesAfter, err := s.State.AllStorageInstances() 483 c.Assert(err, jc.ErrorIsNil) 484 c.Assert(len(instancesAfter)-len(instancesBefore), gc.Equals, 1) 485 s.assertStorageExist(c, instancesAfter, "data", "data") 486 volumesAfter, err := s.State.AllVolumes() 487 c.Assert(err, jc.ErrorIsNil) 488 c.Assert(volumesAfter, gc.HasLen, 2) 489 490 context = runList(c) 491 c.Assert(testing.Stdout(context), gc.Equals, ` 492 [Storage] 493 UNIT ID LOCATION STATUS PERSISTENT 494 storage-filesystem/0 data/0 pending false 495 storage-filesystem/0 data/1 pending false 496 497 `[1:]) 498 c.Assert(testing.Stderr(context), gc.Equals, "") 499 } 500 501 func createUnitWithFileSystemStorage(c *gc.C, s *jujutesting.JujuConnSuite, poolName string) string { 502 ch := s.AddTestingCharm(c, "storage-filesystem") 503 storage := map[string]state.StorageConstraints{ 504 "data": makeStorageCons(poolName, 1024, 1), 505 } 506 service := s.AddTestingServiceWithStorage(c, "storage-filesystem", ch, storage) 507 unit, err := service.AddUnit() 508 c.Assert(err, jc.ErrorIsNil) 509 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 510 c.Assert(err, jc.ErrorIsNil) 511 512 return unit.Tag().Id() 513 }