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 = ¶ms.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, ¶ms.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 }