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  }