github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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/loggo" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/errors" 16 jujucmd "github.com/juju/juju/cmd/juju/commands" 17 jujutesting "github.com/juju/juju/juju/testing" 18 "github.com/juju/juju/provider/dummy" 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/testing" 23 ) 24 25 const ( 26 testPool = "block" 27 ) 28 29 func setupTestStorageSupport(c *gc.C, s *state.State) { 30 stsetts := state.NewStateSettings(s) 31 poolManager := poolmanager.New(stsetts, dummy.StorageProviders()) 32 _, err := poolManager.Create(testPool, provider.LoopProviderType, map[string]interface{}{"it": "works"}) 33 c.Assert(err, jc.ErrorIsNil) 34 } 35 36 func makeStorageCons(pool string, size, count uint64) state.StorageConstraints { 37 return state.StorageConstraints{Pool: pool, Size: size, Count: count} 38 } 39 40 func createUnitWithStorage(c *gc.C, s *jujutesting.JujuConnSuite, poolName string) string { 41 ch := s.AddTestingCharm(c, "storage-block") 42 storage := map[string]state.StorageConstraints{ 43 "data": makeStorageCons(poolName, 1024, 1), 44 } 45 service := s.AddTestingServiceWithStorage(c, "storage-block", ch, storage) 46 unit, err := service.AddUnit() 47 c.Assert(err, jc.ErrorIsNil) 48 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 49 c.Assert(err, jc.ErrorIsNil) 50 51 return unit.Tag().Id() 52 } 53 54 type cmdStorageSuite struct { 55 jujutesting.RepoSuite 56 } 57 58 func (s *cmdStorageSuite) SetUpTest(c *gc.C) { 59 s.RepoSuite.SetUpTest(c) 60 setupTestStorageSupport(c, s.State) 61 } 62 63 func runShow(c *gc.C, expectedError string, args ...string) { 64 cmdArgs := append([]string{"show-storage"}, args...) 65 context, err := runJujuCommand(c, cmdArgs...) 66 if expectedError == "" { 67 c.Assert(err, jc.ErrorIsNil) 68 } else { 69 c.Assert(err, gc.NotNil) 70 c.Assert(testing.Stderr(context), jc.Contains, expectedError) 71 } 72 } 73 74 func (s *cmdStorageSuite) TestStorageShowEmpty(c *gc.C) { 75 runShow(c, "must specify storage id") 76 } 77 78 func (s *cmdStorageSuite) TestStorageShowInvalidId(c *gc.C) { 79 runShow(c, "invalid storage id", "fluff") 80 } 81 82 func (s *cmdStorageSuite) TestStorageShow(c *gc.C) { 83 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 84 85 expected := ` 86 data/0: 87 kind: block 88 status: 89 current: pending 90 since: .* 91 persistent: false 92 attachments: 93 units: 94 storage-block/0: 95 machine: "0" 96 `[1:] 97 context, err := runJujuCommand(c, "show-storage", "data/0") 98 c.Assert(err, jc.ErrorIsNil) 99 c.Assert(testing.Stdout(context), gc.Matches, expected) 100 } 101 102 func (s *cmdStorageSuite) TestStorageShowOneInvalid(c *gc.C) { 103 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 104 105 runShow(c, "storage instance \"fluff/0\" not found", "data/0", "fluff/0") 106 } 107 108 func (s *cmdStorageSuite) TestStorageShowNoMatch(c *gc.C) { 109 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 110 runShow(c, "storage instance \"fluff/0\" not found", "data/0", "fluff/0") 111 } 112 113 func runList(c *gc.C, expectedOutput string, args ...string) { 114 cmdArgs := append([]string{"list-storage"}, args...) 115 context, err := runJujuCommand(c, cmdArgs...) 116 c.Assert(err, jc.ErrorIsNil) 117 c.Assert(testing.Stdout(context), gc.Equals, expectedOutput) 118 } 119 120 func (s *cmdStorageSuite) TestStorageListEmpty(c *gc.C) { 121 runList(c, "") 122 } 123 124 func (s *cmdStorageSuite) TestStorageList(c *gc.C) { 125 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 126 127 expected := ` 128 [Storage] 129 Unit Id Location Status Message 130 storage-block/0 data/0 pending 131 132 `[1:] 133 runList(c, expected) 134 } 135 136 func (s *cmdStorageSuite) TestStorageListPersistent(c *gc.C) { 137 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 138 139 // There are currently no guarantees about whether storage 140 // will be persistent until it has been provisioned. 141 expected := ` 142 [Storage] 143 Unit Id Location Status Message 144 storage-block/0 data/0 pending 145 146 `[1:] 147 runList(c, expected) 148 } 149 150 func (s *cmdStorageSuite) TestStoragePersistentProvisioned(c *gc.C) { 151 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 152 vol, err := s.State.StorageInstanceVolume(names.NewStorageTag("data/0")) 153 c.Assert(err, jc.ErrorIsNil) 154 err = s.State.SetVolumeInfo(vol.VolumeTag(), state.VolumeInfo{ 155 Size: 1024, 156 Persistent: true, 157 VolumeId: "vol-ume", 158 }) 159 c.Assert(err, jc.ErrorIsNil) 160 161 expected := ` 162 data/0: 163 kind: block 164 status: 165 current: pending 166 since: .* 167 persistent: true 168 attachments: 169 units: 170 storage-block/0: 171 machine: "0" 172 `[1:] 173 context, err := runJujuCommand(c, "show-storage", "data/0") 174 c.Assert(err, jc.ErrorIsNil) 175 c.Assert(testing.Stdout(context), gc.Matches, expected) 176 } 177 178 func (s *cmdStorageSuite) TestStoragePersistentUnprovisioned(c *gc.C) { 179 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 180 181 // There are currently no guarantees about whether storage 182 // will be persistent until it has been provisioned. 183 expected := ` 184 data/0: 185 kind: block 186 status: 187 current: pending 188 since: .* 189 persistent: false 190 attachments: 191 units: 192 storage-block/0: 193 machine: "0" 194 `[1:] 195 context, err := runJujuCommand(c, "show-storage", "data/0") 196 c.Assert(err, jc.ErrorIsNil) 197 c.Assert(testing.Stdout(context), gc.Matches, expected) 198 } 199 200 func runJujuCommand(c *gc.C, args ...string) (*cmd.Context, error) { 201 // NOTE (alesstimec): Writers need to be reset, because 202 // they are set globally in the juju/cmd package and will 203 // return an error if we attempt to run two commands in the 204 // same test. 205 loggo.RemoveWriter("warning") 206 ctx, err := cmd.DefaultContext() 207 c.Assert(err, jc.ErrorIsNil) 208 command := jujucmd.NewJujuCommand(ctx) 209 return testing.RunCommand(c, command, args...) 210 } 211 212 func runPoolList(c *gc.C, args ...string) (string, string, error) { 213 cmdArgs := append([]string{"list-storage-pools"}, args...) 214 ctx, err := runJujuCommand(c, cmdArgs...) 215 stdout, stderr := "", "" 216 if ctx != nil { 217 stdout = testing.Stdout(ctx) 218 stderr = testing.Stderr(ctx) 219 } 220 return stdout, stderr, err 221 } 222 223 func (s *cmdStorageSuite) TestListPools(c *gc.C) { 224 stdout, _, err := runPoolList(c, "--format", "yaml") 225 c.Assert(err, jc.ErrorIsNil) 226 expected := ` 227 block: 228 provider: loop 229 attrs: 230 it: works 231 environscoped: 232 provider: environscoped 233 environscoped-block: 234 provider: environscoped-block 235 loop: 236 provider: loop 237 machinescoped: 238 provider: machinescoped 239 rootfs: 240 provider: rootfs 241 static: 242 provider: static 243 tmpfs: 244 provider: tmpfs 245 `[1:] 246 c.Assert(stdout, gc.Equals, expected) 247 } 248 249 func (s *cmdStorageSuite) TestListPoolsTabular(c *gc.C) { 250 stdout, _, err := runPoolList(c) 251 c.Assert(err, jc.ErrorIsNil) 252 expected := ` 253 Name Provider Attrs 254 block loop it=works 255 environscoped environscoped 256 environscoped-block environscoped-block 257 loop loop 258 machinescoped machinescoped 259 rootfs rootfs 260 static static 261 tmpfs tmpfs 262 263 `[1:] 264 c.Assert(stdout, gc.Equals, expected) 265 } 266 267 func (s *cmdStorageSuite) TestListPoolsName(c *gc.C) { 268 stdout, _, err := runPoolList(c, "--format", "yaml", "--name", "block") 269 c.Assert(err, jc.ErrorIsNil) 270 expected := ` 271 block: 272 provider: loop 273 attrs: 274 it: works 275 `[1:] 276 c.Assert(stdout, gc.Equals, expected) 277 } 278 279 func (s *cmdStorageSuite) TestListPoolsNameNoMatch(c *gc.C) { 280 stdout, stderr, err := runPoolList(c, "--name", "cranky") 281 c.Assert(err, jc.ErrorIsNil) 282 c.Assert(stderr, gc.Equals, "No storage pools to display.\n") 283 c.Assert(stdout, gc.Equals, "") 284 } 285 286 func (s *cmdStorageSuite) TestListPoolsNameInvalid(c *gc.C) { 287 _, stderr, err := runPoolList(c, "--name", "9oops") 288 c.Assert(err, gc.NotNil) 289 c.Assert(stderr, jc.Contains, `ERROR pool name "9oops" not valid`) 290 } 291 292 func (s *cmdStorageSuite) TestListPoolsProvider(c *gc.C) { 293 stdout, _, err := runPoolList(c, "--format", "yaml", "--provider", "loop") 294 c.Assert(err, jc.ErrorIsNil) 295 expected := ` 296 block: 297 provider: loop 298 attrs: 299 it: works 300 loop: 301 provider: loop 302 `[1:] 303 c.Assert(stdout, gc.Equals, expected) 304 } 305 306 func (s *cmdStorageSuite) TestListPoolsProviderNoMatch(c *gc.C) { 307 stdout, _, err := runPoolList(c, "--format", "yaml", "--provider", string(provider.TmpfsProviderType)) 308 c.Assert(err, jc.ErrorIsNil) 309 expected := ` 310 tmpfs: 311 provider: tmpfs 312 `[1:] 313 c.Assert(stdout, gc.Equals, expected) 314 } 315 316 func (s *cmdStorageSuite) TestListPoolsProviderUnregistered(c *gc.C) { 317 _, stderr, err := runPoolList(c, "--provider", "oops") 318 c.Assert(err, gc.NotNil) 319 c.Assert(stderr, jc.Contains, `storage provider "oops" not found`) 320 } 321 322 func (s *cmdStorageSuite) TestListPoolsNameAndProvider(c *gc.C) { 323 stdout, _, err := runPoolList(c, "--format", "yaml", "--name", "block", "--provider", "loop") 324 c.Assert(err, jc.ErrorIsNil) 325 expected := ` 326 block: 327 provider: loop 328 attrs: 329 it: works 330 `[1:] 331 c.Assert(stdout, gc.Equals, expected) 332 } 333 334 func (s *cmdStorageSuite) TestListPoolsProviderAndNotName(c *gc.C) { 335 stdout, _, err := runPoolList(c, "--name", "fluff", "--provider", "environscoped") 336 c.Assert(err, jc.ErrorIsNil) 337 // there is no pool that matches this name AND type 338 c.Assert(stdout, gc.Equals, "") 339 } 340 341 func (s *cmdStorageSuite) TestListPoolsNameAndNotProvider(c *gc.C) { 342 stdout, _, err := runPoolList(c, "--name", "block", "--provider", string(provider.TmpfsProviderType)) 343 c.Assert(err, jc.ErrorIsNil) 344 // no pool matches this name and this provider 345 c.Assert(stdout, gc.Equals, "") 346 } 347 348 func (s *cmdStorageSuite) TestListPoolsNotNameAndNotProvider(c *gc.C) { 349 stdout, _, err := runPoolList(c, "--name", "fluff", "--provider", string(provider.TmpfsProviderType)) 350 c.Assert(err, jc.ErrorIsNil) 351 c.Assert(stdout, gc.Equals, "") 352 } 353 354 func runPoolCreate(c *gc.C, args ...string) (string, string, error) { 355 cmdArgs := append([]string{"create-storage-pool"}, args...) 356 ctx, err := runJujuCommand(c, cmdArgs...) 357 stdout, stderr := "", "" 358 if ctx != nil { 359 stdout = testing.Stdout(ctx) 360 stderr = testing.Stderr(ctx) 361 } 362 return stdout, stderr, err 363 364 } 365 366 func (s *cmdStorageSuite) TestCreatePool(c *gc.C) { 367 pname := "ftPool" 368 stdout, _, err := runPoolCreate(c, pname, "loop", "smth=one") 369 c.Assert(err, jc.ErrorIsNil) 370 c.Assert(stdout, gc.Equals, "") 371 assertPoolExists(c, s.State, pname, "loop", "smth=one") 372 } 373 374 func (s *cmdStorageSuite) assertCreatePoolError(c *gc.C, errString, expected string, args ...string) { 375 _, stderr, err := runPoolCreate(c, args...) 376 if errString != "" { 377 c.Assert(err, gc.ErrorMatches, errString) 378 } else { 379 c.Assert(err, gc.NotNil) 380 } 381 382 c.Assert(stderr, jc.Contains, expected) 383 } 384 385 func (s *cmdStorageSuite) TestCreatePoolErrorNoAttrs(c *gc.C) { 386 s.assertCreatePoolError(c, "pool creation requires names, provider type and attrs for configuration", "", "loop", "ftPool") 387 } 388 389 func (s *cmdStorageSuite) TestCreatePoolErrorNoProvider(c *gc.C) { 390 s.assertCreatePoolError(c, "pool creation requires names, provider type and attrs for configuration", "", "oops provider", "smth=one") 391 } 392 393 func (s *cmdStorageSuite) TestCreatePoolErrorProviderType(c *gc.C) { 394 s.assertCreatePoolError(c, "", "not found", "loop", "ftPool", "smth=one") 395 } 396 397 func (s *cmdStorageSuite) TestCreatePoolDuplicateName(c *gc.C) { 398 pname := "ftPool" 399 stdout, _, err := runPoolCreate(c, pname, "loop", "smth=one") 400 c.Assert(err, jc.ErrorIsNil) 401 c.Assert(stdout, gc.Equals, "") 402 assertPoolExists(c, s.State, pname, "loop", "smth=one") 403 s.assertCreatePoolError(c, "", "cannot overwrite existing settings", pname, "loop", "smth=one") 404 } 405 406 func assertPoolExists(c *gc.C, st *state.State, pname, provider, attr string) { 407 stsetts := state.NewStateSettings(st) 408 poolManager := poolmanager.New(stsetts, dummy.StorageProviders()) 409 410 found, err := poolManager.List() 411 c.Assert(err, jc.ErrorIsNil) 412 c.Assert(len(found) > 0, jc.IsTrue) 413 414 exists := false 415 for _, one := range found { 416 if one.Name() == pname { 417 exists = true 418 c.Assert(string(one.Provider()), gc.Equals, provider) 419 // At this stage, only 1 attr is expected and checked 420 expectedAttrs := strings.Split(attr, "=") 421 value, ok := one.Attrs()[expectedAttrs[0]] 422 c.Assert(ok, jc.IsTrue) 423 c.Assert(value, gc.Equals, expectedAttrs[1]) 424 } 425 } 426 c.Assert(exists, jc.IsTrue) 427 } 428 429 func runVolumeList(c *gc.C, args ...string) (string, string, error) { 430 cmdArgs := append([]string{"list-storage", "--volume"}, args...) 431 ctx, err := runJujuCommand(c, cmdArgs...) 432 return testing.Stdout(ctx), testing.Stderr(ctx), err 433 } 434 435 func (s *cmdStorageSuite) TestListVolumeInvalidMachine(c *gc.C) { 436 _, stderr, err := runVolumeList(c, "abc") 437 c.Assert(err, jc.ErrorIsNil) 438 c.Assert(stderr, jc.Contains, `"machine-abc" is not a valid machine tag`) 439 } 440 441 func (s *cmdStorageSuite) TestListVolumeTabularFilterMatch(c *gc.C) { 442 createUnitWithStorage(c, &s.JujuConnSuite, testPool) 443 stdout, _, err := runVolumeList(c, "0") 444 c.Assert(err, jc.ErrorIsNil) 445 expected := ` 446 Machine Unit Storage Id Provider Id Device Size State Message 447 0 storage-block/0 data/0 0/0 pending 448 449 `[1:] 450 c.Assert(stdout, gc.Equals, expected) 451 } 452 453 func runAddToUnit(c *gc.C, args ...string) (*cmd.Context, error) { 454 cmdArgs := append([]string{"add-storage"}, args...) 455 return runJujuCommand(c, cmdArgs...) 456 } 457 458 func (s *cmdStorageSuite) TestStorageAddToUnitSuccess(c *gc.C) { 459 u := createUnitWithStorage(c, &s.JujuConnSuite, testPool) 460 instancesBefore, err := s.State.AllStorageInstances() 461 c.Assert(err, jc.ErrorIsNil) 462 volumesBefore, err := s.State.AllVolumes() 463 c.Assert(err, jc.ErrorIsNil) 464 s.assertStorageExist(c, instancesBefore, "data") 465 466 context, err := runAddToUnit(c, u, "allecto=1") 467 c.Assert(err, jc.ErrorIsNil) 468 c.Assert(testing.Stdout(context), gc.Equals, "added \"allecto\"\n") 469 c.Assert(testing.Stderr(context), gc.Equals, "") 470 471 instancesAfter, err := s.State.AllStorageInstances() 472 c.Assert(err, jc.ErrorIsNil) 473 c.Assert(len(instancesAfter)-len(instancesBefore), gc.Equals, 1) 474 volumesAfter, err := s.State.AllVolumes() 475 c.Assert(err, jc.ErrorIsNil) 476 c.Assert(len(volumesAfter)-len(volumesBefore), gc.Equals, 1) 477 s.assertStorageExist(c, instancesAfter, "data", "allecto") 478 } 479 480 func (s *cmdStorageSuite) assertStorageExist(c *gc.C, 481 all []state.StorageInstance, 482 expected ...string) { 483 484 names := make([]string, len(all)) 485 for i, one := range all { 486 names[i] = one.StorageName() 487 } 488 c.Assert(names, jc.SameContents, expected) 489 } 490 491 func (s *cmdStorageSuite) TestStorageAddToUnitUnitDoesntExist(c *gc.C) { 492 context, err := runAddToUnit(c, "fluffyunit/0", "allecto=1") 493 c.Assert(errors.Cause(err), gc.ErrorMatches, "cmd: error out silently") 494 c.Assert(testing.Stdout(context), gc.Equals, "") 495 c.Assert(testing.Stderr(context), gc.Equals, "failed to add \"allecto\": unit \"fluffyunit/0\" not found\n") 496 } 497 498 func (s *cmdStorageSuite) TestStorageAddToUnitCollapseUnitErrors(c *gc.C) { 499 context, err := runAddToUnit(c, "fluffyunit/0", "allecto=1", "trial=1") 500 c.Assert(errors.Cause(err), gc.ErrorMatches, "cmd: error out silently") 501 c.Assert(testing.Stdout(context), gc.Equals, "") 502 c.Assert(testing.Stderr(context), gc.Equals, "unit \"fluffyunit/0\" not found\n") 503 } 504 505 func (s *cmdStorageSuite) TestStorageAddToUnitInvalidUnitName(c *gc.C) { 506 cmdArgs := append([]string{"add-storage"}, "fluffyunit-0", "allecto=1") 507 context, err := runJujuCommand(c, cmdArgs...) 508 c.Assert(err, gc.ErrorMatches, `unit name "fluffyunit-0" not valid`) 509 c.Assert(testing.Stdout(context), gc.Equals, "") 510 c.Assert(testing.Stderr(context), gc.Equals, "error: unit name \"fluffyunit-0\" not valid\n") 511 } 512 513 func (s *cmdStorageSuite) TestStorageAddToUnitStorageDoesntExist(c *gc.C) { 514 u := createUnitWithStorage(c, &s.JujuConnSuite, testPool) 515 instancesBefore, err := s.State.AllStorageInstances() 516 c.Assert(err, jc.ErrorIsNil) 517 volumesBefore, err := s.State.AllVolumes() 518 c.Assert(err, jc.ErrorIsNil) 519 s.assertStorageExist(c, instancesBefore, "data") 520 521 context, err := runAddToUnit(c, u, "nonstorage=1") 522 c.Assert(errors.Cause(err), gc.ErrorMatches, "cmd: error out silently") 523 c.Assert(testing.Stdout(context), gc.Equals, "") 524 c.Assert(testing.Stderr(context), gc.Equals, 525 `failed to add "nonstorage": adding storage to unit storage-block/0: charm storage "nonstorage" not found`+"\n", 526 ) 527 528 instancesAfter, err := s.State.AllStorageInstances() 529 c.Assert(err, jc.ErrorIsNil) 530 c.Assert(len(instancesAfter)-len(instancesBefore), gc.Equals, 0) 531 volumesAfter, err := s.State.AllVolumes() 532 c.Assert(err, jc.ErrorIsNil) 533 c.Assert(len(volumesAfter)-len(volumesBefore), gc.Equals, 0) 534 s.assertStorageExist(c, instancesAfter, "data") 535 } 536 537 func (s *cmdStorageSuite) TestStorageAddToUnitHasVolumes(c *gc.C) { 538 // Reproducing Bug1462146 539 u := createUnitWithFileSystemStorage(c, &s.JujuConnSuite, "environscoped-block") 540 instancesBefore, err := s.State.AllStorageInstances() 541 c.Assert(err, jc.ErrorIsNil) 542 s.assertStorageExist(c, instancesBefore, "data") 543 volumesBefore, err := s.State.AllVolumes() 544 c.Assert(err, jc.ErrorIsNil) 545 c.Assert(volumesBefore, gc.HasLen, 1) 546 547 context, err := runJujuCommand(c, "storage", "list") 548 c.Assert(err, jc.ErrorIsNil) 549 c.Assert(testing.Stdout(context), gc.Equals, ` 550 [Storage] 551 Unit Id Location Status Message 552 storage-filesystem/0 data/0 pending 553 554 `[1:]) 555 c.Assert(testing.Stderr(context), gc.Equals, "") 556 557 context, err = runAddToUnit(c, u, "data=environscoped-block,1G") 558 c.Assert(err, jc.ErrorIsNil) 559 c.Assert(testing.Stdout(context), gc.Equals, "added \"data\"\n") 560 c.Assert(testing.Stderr(context), gc.Equals, "") 561 562 instancesAfter, err := s.State.AllStorageInstances() 563 c.Assert(err, jc.ErrorIsNil) 564 c.Assert(len(instancesAfter)-len(instancesBefore), gc.Equals, 1) 565 s.assertStorageExist(c, instancesAfter, "data", "data") 566 volumesAfter, err := s.State.AllVolumes() 567 c.Assert(err, jc.ErrorIsNil) 568 c.Assert(volumesAfter, gc.HasLen, 2) 569 570 context, err = runJujuCommand(c, "list-storage") 571 c.Assert(err, jc.ErrorIsNil) 572 c.Assert(testing.Stdout(context), gc.Equals, ` 573 [Storage] 574 Unit Id Location Status Message 575 storage-filesystem/0 data/0 pending 576 storage-filesystem/0 data/1 pending 577 578 `[1:]) 579 c.Assert(testing.Stderr(context), gc.Equals, "") 580 } 581 582 func createUnitWithFileSystemStorage(c *gc.C, s *jujutesting.JujuConnSuite, poolName string) string { 583 ch := s.AddTestingCharm(c, "storage-filesystem") 584 storage := map[string]state.StorageConstraints{ 585 "data": makeStorageCons(poolName, 1024, 1), 586 } 587 service := s.AddTestingServiceWithStorage(c, "storage-filesystem", ch, storage) 588 unit, err := service.AddUnit() 589 c.Assert(err, jc.ErrorIsNil) 590 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 591 c.Assert(err, jc.ErrorIsNil) 592 593 return unit.Tag().Id() 594 }