github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/storage/add_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package storage_test
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/cmd/cmdtesting"
    12  	"github.com/juju/errors"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/apiserver/common"
    17  	"github.com/juju/juju/apiserver/params"
    18  	"github.com/juju/juju/cmd/juju/storage"
    19  	_ "github.com/juju/juju/provider/dummy"
    20  )
    21  
    22  type addSuite struct {
    23  	SubStorageSuite
    24  	mockAPI *mockAddAPI
    25  	args    []string
    26  }
    27  
    28  var _ = gc.Suite(&addSuite{})
    29  
    30  func (s *addSuite) SetUpTest(c *gc.C) {
    31  	s.SubStorageSuite.SetUpTest(c)
    32  
    33  	s.mockAPI = &mockAddAPI{
    34  		addToUnitFunc: func(storages []params.StorageAddParams) ([]params.AddStorageResult, error) {
    35  			result := make([]params.AddStorageResult, len(storages))
    36  			for i, one := range storages {
    37  				if strings.HasPrefix(one.StorageName, "err") {
    38  					result[i].Error = common.ServerError(errors.Errorf("test failure"))
    39  					continue
    40  				}
    41  				result[i].Result = &params.AddStorageDetails{
    42  					StorageTags: []string{
    43  						"storage-foo-0",
    44  						"storage-foo-1",
    45  					},
    46  				}
    47  			}
    48  			return result, nil
    49  		},
    50  	}
    51  	s.args = nil
    52  }
    53  
    54  type tstData struct {
    55  	args        []string
    56  	expectedErr string
    57  	visibleErr  string
    58  }
    59  
    60  var errorTsts = []tstData{
    61  	{
    62  		args:        nil,
    63  		expectedErr: "add-storage requires a unit and a storage directive",
    64  		visibleErr:  "add-storage requires a unit and a storage directive",
    65  	},
    66  	{
    67  		args:        []string{"tst/123"},
    68  		expectedErr: "add-storage requires a unit and a storage directive",
    69  		visibleErr:  "add-storage requires a unit and a storage directive",
    70  	},
    71  	{
    72  		args:        []string{"tst/123", "data="},
    73  		expectedErr: `storage constraints require at least one field to be specified`,
    74  		visibleErr:  `cannot parse constraints for storage "data": storage constraints require at least one field to be specified`,
    75  	},
    76  	{
    77  		args:        []string{"tst/123", "data=-676"},
    78  		expectedErr: `count must be greater than zero, got "-676"`,
    79  		visibleErr:  `cannot parse constraints for storage "data": cannot parse count: count must be greater than zero, got "-676"`,
    80  	},
    81  	{
    82  		args:        []string{"tst/123", "data=676", "data=676"},
    83  		expectedErr: `storage "data" specified more than once`,
    84  		visibleErr:  `storage "data" specified more than once`,
    85  	},
    86  }
    87  
    88  func (s *addSuite) TestAddArgs(c *gc.C) {
    89  	for i, t := range errorTsts {
    90  		c.Logf("test %d for %q", i, t.args)
    91  		s.args = t.args
    92  		s.assertAddErrorOutput(c, t.expectedErr, visibleErrorMessage(t.visibleErr))
    93  	}
    94  }
    95  
    96  func (s *addSuite) TestAddInvalidUnit(c *gc.C) {
    97  	s.args = []string{"tst-123", "data=676"}
    98  
    99  	expectedErr := `unit name "tst-123" not valid`
   100  	s.assertAddErrorOutput(c, expectedErr, visibleErrorMessage(expectedErr))
   101  }
   102  
   103  func (s *addSuite) TestAddSuccess(c *gc.C) {
   104  	validArgs := [][]string{
   105  		{"tst/123", "data=676"},
   106  		{"tst/123", "data"},
   107  	}
   108  	expectedStderr := `
   109  added storage foo/0 to tst/123
   110  added storage foo/1 to tst/123
   111  `[1:]
   112  
   113  	for i, args := range validArgs {
   114  		c.Logf("test %d for %q", i, args)
   115  		context, err := s.runAdd(c, args...)
   116  		c.Assert(err, jc.ErrorIsNil)
   117  		s.assertExpectedOutput(c, context, expectedStderr)
   118  	}
   119  }
   120  
   121  func (s *addSuite) TestAddOperationAborted(c *gc.C) {
   122  	s.args = []string{"tst/123", "data=676"}
   123  	s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.AddStorageResult, error) {
   124  		return nil, errors.New("aborted")
   125  	}
   126  	s.assertAddErrorOutput(c, ".*aborted.*", "")
   127  }
   128  
   129  func (s *addSuite) TestAddFailure(c *gc.C) {
   130  	s.args = []string{"tst/123", "err=676"}
   131  	s.assertAddErrorOutput(c, "cmd: error out silently", "failed to add storage \"err\" to tst/123: test failure\n")
   132  }
   133  
   134  func (s *addSuite) TestAddMixOrderPreserved(c *gc.C) {
   135  	expectedErr := `
   136  added storage foo/0 to tst/123
   137  added storage foo/1 to tst/123
   138  failed to add storage "err" to tst/123: test failure
   139  `[1:]
   140  
   141  	s.args = []string{"tst/123", "a=676", "err=676"}
   142  	s.assertAddErrorOutput(c, "cmd: error out silently", expectedErr)
   143  
   144  	s.args = []string{"tst/123", "err=676", "a=676"}
   145  	s.assertAddErrorOutput(c, "cmd: error out silently", expectedErr)
   146  }
   147  
   148  func (s *addSuite) TestAddAllDistinctErrors(c *gc.C) {
   149  	expectedErr := `
   150  added storage "storage0" to tst/123
   151  added storage "storage1" to tst/123
   152  failed to add storage "storage2" to tst/123: storage pool "barf" not found
   153  failed to add storage "storage42" to tst/123: storage "storage42" not found
   154  `[1:]
   155  
   156  	s.args = []string{"tst/123", "storage0=ebs", "storage2=barf", "storage1=123", "storage42=loop"}
   157  	s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.AddStorageResult, error) {
   158  		result := make([]params.AddStorageResult, len(storages))
   159  		for i, one := range storages {
   160  			if one.StorageName == "storage2" {
   161  				result[i].Error = common.ServerError(errors.Errorf(`storage pool "barf" not found`))
   162  			}
   163  			if one.StorageName == "storage42" {
   164  				result[i].Error = common.ServerError(errors.Errorf(`storage "storage42" not found`))
   165  			}
   166  		}
   167  		return result, nil
   168  	}
   169  
   170  	s.assertAddErrorOutput(c, "cmd: error out silently", expectedErr)
   171  }
   172  
   173  func (s *addSuite) TestAddStorageOnlyDistinctErrors(c *gc.C) {
   174  	expectedErr := `
   175  added storage "storage0" to tst/123
   176  failed to add storage "storage2" to tst/123: storage "storage2" not found
   177  failed to add storage "storage42" to tst/123: storage "storage42" not found
   178  `[1:]
   179  
   180  	s.args = []string{"tst/123", "storage0=ebs", "storage2=barf", "storage42=loop"}
   181  	s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.AddStorageResult, error) {
   182  		result := make([]params.AddStorageResult, len(storages))
   183  		for i, one := range storages {
   184  			if one.StorageName == "storage42" || one.StorageName == "storage2" {
   185  				result[i].Error = common.ServerError(errors.Errorf(`storage "%v" not found`, one.StorageName))
   186  			}
   187  		}
   188  		return result, nil
   189  	}
   190  
   191  	s.assertAddErrorOutput(c, "cmd: error out silently", expectedErr)
   192  }
   193  
   194  func (s *addSuite) TestAddStorageMixDistinctAndNonDistinctErrors(c *gc.C) {
   195  	expectedErr := `
   196  some unit error
   197  storage "storage0" not found
   198  `[1:]
   199  
   200  	unitErr := `some unit error`
   201  	s.args = []string{"tst/123", "storage0=ebs", "storage2=barf", "storage42=loop"}
   202  	s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.AddStorageResult, error) {
   203  		result := make([]params.AddStorageResult, len(storages))
   204  		for i, one := range storages {
   205  			if one.StorageName == "storage42" || one.StorageName == "storage2" {
   206  				result[i].Error = common.ServerError(errors.New(unitErr))
   207  			} else {
   208  				result[i].Error = common.ServerError(errors.Errorf(`storage "%v" not found`, one.StorageName))
   209  			}
   210  		}
   211  		return result, nil
   212  	}
   213  
   214  	s.assertAddErrorOutput(c, "cmd: error out silently", expectedErr)
   215  }
   216  
   217  func (s *addSuite) TestCollapseUnitErrors(c *gc.C) {
   218  	expectedErr := `some unit error`
   219  
   220  	s.args = []string{"tst/123", "storage0=ebs", "storage2=barf", "storage1=123", "storage42=loop"}
   221  	s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.AddStorageResult, error) {
   222  		result := make([]params.AddStorageResult, len(storages))
   223  		for i := range storages {
   224  			result[i].Error = common.ServerError(errors.New(expectedErr))
   225  		}
   226  		return result, nil
   227  	}
   228  
   229  	s.assertAddErrorOutput(c, "cmd: error out silently", expectedErr+"\n")
   230  }
   231  
   232  func (s *addSuite) TestUnauthorizedMentionsJujuGrant(c *gc.C) {
   233  	s.args = []string{"tst/123", "data"}
   234  	s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.AddStorageResult, error) {
   235  		return nil, &params.Error{
   236  			Message: "permission denied",
   237  			Code:    params.CodeUnauthorized,
   238  		}
   239  	}
   240  
   241  	ctx, _ := s.runAdd(c, s.args...)
   242  	errString := strings.Replace(cmdtesting.Stderr(ctx), "\n", " ", -1)
   243  	c.Assert(errString, gc.Matches, `.*juju grant.*`)
   244  }
   245  
   246  func (s *addSuite) assertAddErrorOutput(c *gc.C, expected string, expectedErr string) {
   247  	context, err := s.runAdd(c, s.args...)
   248  	c.Assert(errors.Cause(err), gc.ErrorMatches, expected)
   249  	s.assertExpectedOutput(c, context, expectedErr)
   250  }
   251  
   252  func (s *addSuite) assertExpectedOutput(c *gc.C, context *cmd.Context, expectedErr string) {
   253  	c.Assert(cmdtesting.Stdout(context), gc.Equals, "")
   254  	c.Assert(cmdtesting.Stderr(context), gc.Equals, expectedErr)
   255  }
   256  
   257  func (s *addSuite) runAdd(c *gc.C, args ...string) (*cmd.Context, error) {
   258  	return cmdtesting.RunCommand(c, storage.NewAddCommandForTest(s.mockAPI, s.store), args...)
   259  }
   260  
   261  func visibleErrorMessage(errMsg string) string {
   262  	return fmt.Sprintf("ERROR %v\n", errMsg)
   263  }
   264  
   265  type mockAddAPI struct {
   266  	addToUnitFunc func(storages []params.StorageAddParams) ([]params.AddStorageResult, error)
   267  }
   268  
   269  func (s mockAddAPI) Close() error {
   270  	return nil
   271  }
   272  
   273  func (s mockAddAPI) AddToUnit(storages []params.StorageAddParams) ([]params.AddStorageResult, error) {
   274  	return s.addToUnitFunc(storages)
   275  }