github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/modelcmd/modelcommand_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package modelcmd_test 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "os" 10 11 "github.com/juju/cmd" 12 "github.com/juju/cmd/cmdtesting" 13 "github.com/juju/errors" 14 "github.com/juju/testing" 15 jc "github.com/juju/testing/checkers" 16 gc "gopkg.in/check.v1" 17 "gopkg.in/juju/names.v2" 18 "gopkg.in/macaroon.v2-unstable" 19 20 "github.com/juju/juju/api" 21 apitesting "github.com/juju/juju/api/testing" 22 jujucmd "github.com/juju/juju/cmd" 23 "github.com/juju/juju/cmd/modelcmd" 24 "github.com/juju/juju/core/model" 25 "github.com/juju/juju/juju/osenv" 26 "github.com/juju/juju/jujuclient" 27 "github.com/juju/juju/permission" 28 ) 29 30 type ModelCommandSuite struct { 31 testing.IsolationSuite 32 store *jujuclient.MemStore 33 } 34 35 func (s *ModelCommandSuite) SetUpTest(c *gc.C) { 36 s.IsolationSuite.SetUpTest(c) 37 s.PatchEnvironment("JUJU_CLI_VERSION", "") 38 39 s.store = jujuclient.NewMemStore() 40 } 41 42 var _ = gc.Suite(&ModelCommandSuite{}) 43 44 var modelCommandModelTests = []struct { 45 about string 46 args []string 47 modelEnvVar string 48 expectController string 49 expectModel string 50 }{{ 51 about: "explicit controller and model, long form", 52 args: []string{"--model", "bar:noncurrentbar"}, 53 expectController: "bar", 54 expectModel: "noncurrentbar", 55 }, { 56 about: "explicit controller and model, short form", 57 args: []string{"-m", "bar:noncurrentbar"}, 58 expectController: "bar", 59 expectModel: "noncurrentbar", 60 }, { 61 about: "implicit controller, explicit model, short form", 62 args: []string{"-m", "explicit"}, 63 expectController: "foo", 64 expectModel: "explicit", 65 }, { 66 about: "implicit controller, explicit model, long form", 67 args: []string{"--model", "explicit"}, 68 expectController: "foo", 69 expectModel: "explicit", 70 }, { 71 about: "explicit controller, implicit model", 72 args: []string{"--model", "bar:"}, 73 expectController: "bar", 74 expectModel: "adminbar/currentbar", 75 }, { 76 about: "controller and model in env var", 77 modelEnvVar: "bar:noncurrentbar", 78 expectController: "bar", 79 expectModel: "noncurrentbar", 80 }, { 81 about: "model only in env var", 82 modelEnvVar: "noncurrentfoo", 83 expectController: "foo", 84 expectModel: "noncurrentfoo", 85 }, { 86 about: "controller only in env var", 87 modelEnvVar: "bar:", 88 expectController: "bar", 89 expectModel: "adminbar/currentbar", 90 }, { 91 about: "explicit overrides env var", 92 modelEnvVar: "bar:noncurrentbar", 93 args: []string{"-m", "noncurrentfoo"}, 94 expectController: "foo", 95 expectModel: "noncurrentfoo", 96 }} 97 98 func (s *ModelCommandSuite) TestModelName(c *gc.C) { 99 s.store.Controllers["foo"] = jujuclient.ControllerDetails{} 100 s.store.Controllers["bar"] = jujuclient.ControllerDetails{} 101 s.store.CurrentControllerName = "foo" 102 s.store.Accounts["foo"] = jujuclient.AccountDetails{ 103 User: "bar", Password: "hunter2", 104 } 105 s.store.Accounts["bar"] = jujuclient.AccountDetails{ 106 User: "baz", Password: "hunter3", 107 } 108 err := s.store.UpdateModel("foo", "adminfoo/currentfoo", 109 jujuclient.ModelDetails{ModelUUID: "uuidfoo1", ModelType: model.IAAS}) 110 c.Assert(err, jc.ErrorIsNil) 111 err = s.store.UpdateModel("foo", "adminfoo/oncurrentfoo", 112 jujuclient.ModelDetails{ModelUUID: "uuidfoo2", ModelType: model.IAAS}) 113 c.Assert(err, jc.ErrorIsNil) 114 err = s.store.UpdateModel("foo", "bar/explicit", 115 jujuclient.ModelDetails{ModelUUID: "uuidfoo3", ModelType: model.IAAS}) 116 c.Assert(err, jc.ErrorIsNil) 117 err = s.store.UpdateModel("foo", "bar/noncurrentfoo", 118 jujuclient.ModelDetails{ModelUUID: "uuidfoo4", ModelType: model.IAAS}) 119 c.Assert(err, jc.ErrorIsNil) 120 err = s.store.UpdateModel("bar", "adminbar/currentbar", 121 jujuclient.ModelDetails{ModelUUID: "uuidbar1", ModelType: model.IAAS}) 122 c.Assert(err, jc.ErrorIsNil) 123 err = s.store.UpdateModel("bar", "adminbar/noncurrentbar", 124 jujuclient.ModelDetails{ModelUUID: "uuidbar2", ModelType: model.IAAS}) 125 c.Assert(err, jc.ErrorIsNil) 126 err = s.store.UpdateModel("bar", "baz/noncurrentbar", 127 jujuclient.ModelDetails{ModelUUID: "uuidbar3", ModelType: model.IAAS}) 128 c.Assert(err, jc.ErrorIsNil) 129 err = s.store.SetCurrentModel("foo", "adminfoo/currentfoo") 130 c.Assert(err, jc.ErrorIsNil) 131 err = s.store.SetCurrentModel("bar", "adminbar/currentbar") 132 c.Assert(err, jc.ErrorIsNil) 133 134 for i, test := range modelCommandModelTests { 135 c.Logf("test %d: %v", i, test.about) 136 os.Setenv(osenv.JujuModelEnvKey, test.modelEnvVar) 137 s.assertRunHasModel(c, test.expectController, test.expectModel, test.args...) 138 } 139 } 140 141 func (s *ModelCommandSuite) TestModelType(c *gc.C) { 142 s.store.Controllers["foo"] = jujuclient.ControllerDetails{} 143 s.store.CurrentControllerName = "foo" 144 s.store.Accounts["foo"] = jujuclient.AccountDetails{ 145 User: "bar", Password: "hunter2", 146 } 147 err := s.store.UpdateModel("foo", "adminfoo/currentfoo", 148 jujuclient.ModelDetails{ModelUUID: "uuidfoo1", ModelType: model.IAAS}) 149 c.Assert(err, jc.ErrorIsNil) 150 err = s.store.SetCurrentModel("foo", "adminfoo/currentfoo") 151 c.Assert(err, jc.ErrorIsNil) 152 153 cmd, err := runTestCommand(c, s.store) 154 c.Assert(err, jc.ErrorIsNil) 155 modelType, err := cmd.ModelType() 156 c.Assert(err, jc.ErrorIsNil) 157 c.Assert(modelType, gc.Equals, model.IAAS) 158 } 159 160 func (s *ModelCommandSuite) TestModelGeneration(c *gc.C) { 161 s.store.Controllers["foo"] = jujuclient.ControllerDetails{} 162 s.store.CurrentControllerName = "foo" 163 s.store.Accounts["foo"] = jujuclient.AccountDetails{ 164 User: "bar", Password: "hunter2", 165 } 166 err := s.store.UpdateModel("foo", "adminfoo/currentfoo", 167 jujuclient.ModelDetails{ModelUUID: "uuidfoo1", ModelType: model.IAAS, ModelGeneration: model.GenerationNext}) 168 c.Assert(err, jc.ErrorIsNil) 169 err = s.store.SetCurrentModel("foo", "adminfoo/currentfoo") 170 c.Assert(err, jc.ErrorIsNil) 171 172 cmd, err := runTestCommand(c, s.store) 173 c.Assert(err, jc.ErrorIsNil) 174 modelGeneration, err := cmd.ModelGeneration() 175 c.Assert(err, jc.ErrorIsNil) 176 c.Assert(modelGeneration, gc.Equals, model.GenerationNext) 177 178 c.Assert(cmd.SetModelGeneration(model.GenerationCurrent), jc.ErrorIsNil) 179 modelGeneration, err = cmd.ModelGeneration() 180 c.Assert(err, jc.ErrorIsNil) 181 c.Assert(modelGeneration, gc.Equals, model.GenerationCurrent) 182 } 183 184 func (s *ModelCommandSuite) TestBootstrapContext(c *gc.C) { 185 ctx := modelcmd.BootstrapContext(&cmd.Context{}) 186 c.Assert(ctx.ShouldVerifyCredentials(), jc.IsTrue) 187 } 188 189 func (s *ModelCommandSuite) TestBootstrapContextNoVerify(c *gc.C) { 190 ctx := modelcmd.BootstrapContextNoVerify(&cmd.Context{}) 191 c.Assert(ctx.ShouldVerifyCredentials(), jc.IsFalse) 192 } 193 194 func (s *ModelCommandSuite) TestWrapWithoutFlags(c *gc.C) { 195 cmd := new(testCommand) 196 wrapped := modelcmd.Wrap(cmd, modelcmd.WrapSkipModelFlags) 197 args := []string{"-m", "testmodel"} 198 err := cmdtesting.InitCommand(wrapped, args) 199 // 1st position is always the flag 200 msg := fmt.Sprintf("option provided but not defined: %v", args[0]) 201 c.Assert(err, gc.ErrorMatches, msg) 202 } 203 204 func (s *ModelCommandSuite) TestInnerCommand(c *gc.C) { 205 cmd := new(testCommand) 206 wrapped := modelcmd.Wrap(cmd) 207 c.Assert(modelcmd.InnerCommand(wrapped), gc.Equals, cmd) 208 } 209 210 func (*ModelCommandSuite) TestSplitModelName(c *gc.C) { 211 assert := func(in, controller, model string) { 212 outController, outModel := modelcmd.SplitModelName(in) 213 c.Assert(outController, gc.Equals, controller) 214 c.Assert(outModel, gc.Equals, model) 215 } 216 assert("model", "", "model") 217 assert("ctrl:model", "ctrl", "model") 218 assert("ctrl:", "ctrl", "") 219 assert(":model", "", "model") 220 } 221 222 func (*ModelCommandSuite) TestJoinModelName(c *gc.C) { 223 assert := func(controller, model, expect string) { 224 out := modelcmd.JoinModelName(controller, model) 225 c.Assert(out, gc.Equals, expect) 226 } 227 assert("ctrl", "", "ctrl:") 228 assert("", "model", ":model") 229 assert("ctrl", "model", "ctrl:model") 230 } 231 232 // assertRunHasModel asserts that a command, when run with the given arguments, 233 // ends up with the given controller and model names. 234 func (s *ModelCommandSuite) assertRunHasModel(c *gc.C, expectControllerName, expectModelName string, args ...string) { 235 cmd, err := runTestCommand(c, s.store, args...) 236 c.Assert(err, jc.ErrorIsNil) 237 controllerName, err := cmd.ControllerName() 238 c.Assert(err, jc.ErrorIsNil) 239 c.Assert(controllerName, gc.Equals, expectControllerName) 240 modelName, err := cmd.ModelName() 241 c.Assert(err, jc.ErrorIsNil) 242 c.Assert(modelName, gc.Equals, expectModelName) 243 } 244 245 func (s *ModelCommandSuite) TestIAASOnlyCommandIAASModel(c *gc.C) { 246 s.store.Controllers["foo"] = jujuclient.ControllerDetails{} 247 s.store.CurrentControllerName = "foo" 248 s.store.Accounts["foo"] = jujuclient.AccountDetails{ 249 User: "bar", Password: "hunter2", 250 } 251 err := s.store.UpdateModel("foo", "bar/currentfoo", 252 jujuclient.ModelDetails{ModelUUID: "uuidfoo1", ModelType: model.IAAS}) 253 c.Assert(err, jc.ErrorIsNil) 254 err = s.store.SetCurrentModel("foo", "bar/currentfoo") 255 c.Assert(err, jc.ErrorIsNil) 256 257 cmd, err := runTestCommand(c, s.store) 258 c.Assert(err, jc.ErrorIsNil) 259 modelType, err := cmd.ModelType() 260 c.Assert(err, jc.ErrorIsNil) 261 c.Assert(modelType, gc.Equals, model.IAAS) 262 } 263 264 func (s *ModelCommandSuite) TestIAASOnlyCommandCAASModel(c *gc.C) { 265 s.store.Controllers["foo"] = jujuclient.ControllerDetails{} 266 s.store.CurrentControllerName = "foo" 267 s.store.Accounts["foo"] = jujuclient.AccountDetails{ 268 User: "bar", Password: "hunter2", 269 } 270 err := s.store.UpdateModel("foo", "bar/currentfoo", 271 jujuclient.ModelDetails{ModelUUID: "uuidfoo1", ModelType: model.CAAS}) 272 c.Assert(err, jc.ErrorIsNil) 273 err = s.store.SetCurrentModel("foo", "bar/currentfoo") 274 c.Assert(err, jc.ErrorIsNil) 275 276 _, err = runTestCommand(c, s.store) 277 c.Assert(err, gc.ErrorMatches, `Juju command "test-command" not supported on kubernetes models`) 278 } 279 280 func (s *ModelCommandSuite) TestCAASOnlyCommandIAASModel(c *gc.C) { 281 s.store.Controllers["foo"] = jujuclient.ControllerDetails{} 282 s.store.CurrentControllerName = "foo" 283 s.store.Accounts["foo"] = jujuclient.AccountDetails{ 284 User: "bar", Password: "hunter2", 285 } 286 err := s.store.UpdateModel("foo", "bar/currentfoo", 287 jujuclient.ModelDetails{ModelUUID: "uuidfoo1", ModelType: model.IAAS}) 288 c.Assert(err, jc.ErrorIsNil) 289 err = s.store.SetCurrentModel("foo", "bar/currentfoo") 290 c.Assert(err, jc.ErrorIsNil) 291 292 _, err = runCaasCommand(c, s.store) 293 c.Assert(err, gc.ErrorMatches, `Juju command "caas-command" not supported on non-container models`) 294 } 295 296 func (s *ModelCommandSuite) TestAllowedCommandCAASModel(c *gc.C) { 297 s.store.Controllers["foo"] = jujuclient.ControllerDetails{} 298 s.store.CurrentControllerName = "foo" 299 s.store.Accounts["foo"] = jujuclient.AccountDetails{ 300 User: "bar", Password: "hunter2", 301 } 302 err := s.store.UpdateModel("foo", "bar/currentfoo", 303 jujuclient.ModelDetails{ModelUUID: "uuidfoo1", ModelType: model.CAAS}) 304 c.Assert(err, jc.ErrorIsNil) 305 err = s.store.SetCurrentModel("foo", "bar/currentfoo") 306 c.Assert(err, jc.ErrorIsNil) 307 308 cmd, err := runAllowedCAASCommand(c, s.store) 309 c.Assert(err, jc.ErrorIsNil) 310 modelType, err := cmd.ModelType() 311 c.Assert(err, jc.ErrorIsNil) 312 c.Assert(modelType, gc.Equals, model.CAAS) 313 } 314 315 func noOpRefresh(_ jujuclient.ClientStore, _ string) error { 316 return nil 317 } 318 319 func runTestCommand(c *gc.C, store jujuclient.ClientStore, args ...string) (modelcmd.ModelCommand, error) { 320 modelCmd := new(testCommand) 321 modelcmd.SetModelRefresh(noOpRefresh, modelCmd) 322 cmd := modelcmd.Wrap(modelCmd) 323 cmd.SetClientStore(store) 324 _, err := cmdtesting.RunCommand(c, cmd, args...) 325 return cmd, errors.Trace(err) 326 } 327 328 type testCommand struct { 329 modelcmd.ModelCommandBase 330 modelcmd.IAASOnlyCommand 331 } 332 333 func (c *testCommand) Info() *cmd.Info { 334 return jujucmd.Info(&cmd.Info{ 335 Name: "test-command", 336 }) 337 } 338 339 func (c *testCommand) Run(ctx *cmd.Context) error { 340 return nil 341 } 342 343 func runCaasCommand(c *gc.C, store jujuclient.ClientStore, args ...string) (modelcmd.ModelCommand, error) { 344 modelCmd := new(caasCommand) 345 modelcmd.SetModelRefresh(noOpRefresh, modelCmd) 346 cmd := modelcmd.Wrap(modelCmd) 347 cmd.SetClientStore(store) 348 _, err := cmdtesting.RunCommand(c, cmd, args...) 349 return cmd, errors.Trace(err) 350 } 351 352 type caasCommand struct { 353 modelcmd.ModelCommandBase 354 modelcmd.CAASOnlyCommand 355 } 356 357 func (c *caasCommand) Info() *cmd.Info { 358 return jujucmd.Info(&cmd.Info{ 359 Name: "caas-command", 360 }) 361 } 362 363 func (c *caasCommand) Run(ctx *cmd.Context) error { 364 return nil 365 } 366 367 func runAllowedCAASCommand(c *gc.C, store jujuclient.ClientStore, args ...string) (modelcmd.ModelCommand, error) { 368 modelCmd := new(allowedCAASCommand) 369 modelcmd.SetModelRefresh(noOpRefresh, modelCmd) 370 cmd := modelcmd.Wrap(modelCmd) 371 cmd.SetClientStore(store) 372 _, err := cmdtesting.RunCommand(c, cmd, args...) 373 return cmd, errors.Trace(err) 374 } 375 376 type allowedCAASCommand struct { 377 modelcmd.ModelCommandBase 378 } 379 380 func (c *allowedCAASCommand) Info() *cmd.Info { 381 return jujucmd.Info(&cmd.Info{ 382 Name: "allowed-caas-command", 383 }) 384 } 385 386 func (c *allowedCAASCommand) Run(ctx *cmd.Context) error { 387 return nil 388 } 389 390 var _ = gc.Suite(&macaroonLoginSuite{}) 391 392 type macaroonLoginSuite struct { 393 apitesting.MacaroonSuite 394 store *jujuclient.MemStore 395 controllerName string 396 modelName string 397 apiOpen api.OpenFunc 398 } 399 400 const testUser = "testuser@somewhere" 401 402 func (s *macaroonLoginSuite) SetUpTest(c *gc.C) { 403 s.MacaroonSuite.SetUpTest(c) 404 s.MacaroonSuite.AddModelUser(c, testUser) 405 s.MacaroonSuite.AddControllerUser(c, testUser, permission.LoginAccess) 406 407 s.controllerName = "my-controller" 408 s.modelName = testUser + "/my-model" 409 modelTag := names.NewModelTag(s.State.ModelUUID()) 410 apiInfo := s.APIInfo(c) 411 412 s.store = jujuclient.NewMemStore() 413 s.store.Controllers[s.controllerName] = jujuclient.ControllerDetails{ 414 APIEndpoints: apiInfo.Addrs, 415 ControllerUUID: s.State.ControllerUUID(), 416 CACert: apiInfo.CACert, 417 } 418 s.store.Accounts[s.controllerName] = jujuclient.AccountDetails{ 419 // External user forces use of macaroons. 420 User: "me@external", 421 } 422 s.store.Models[s.controllerName] = &jujuclient.ControllerModels{ 423 Models: map[string]jujuclient.ModelDetails{ 424 s.modelName: {ModelUUID: modelTag.Id(), ModelType: model.IAAS}, 425 }, 426 } 427 s.apiOpen = func(info *api.Info, dialOpts api.DialOpts) (api.Connection, error) { 428 mac, err := apitesting.NewMacaroon("test") 429 c.Assert(err, jc.ErrorIsNil) 430 info.Macaroons = []macaroon.Slice{{mac}} 431 return api.Open(info, dialOpts) 432 } 433 } 434 435 func (s *macaroonLoginSuite) newModelCommandBase() *modelcmd.ModelCommandBase { 436 var c modelcmd.ModelCommandBase 437 c.SetClientStore(s.store) 438 modelcmd.InitContexts(&cmd.Context{Stderr: ioutil.Discard}, &c) 439 modelcmd.SetRunStarted(&c) 440 err := c.SetModelName(s.controllerName+":"+s.modelName, false) 441 if err != nil { 442 panic(err) 443 } 444 return &c 445 } 446 447 func (s *macaroonLoginSuite) TestsSuccessfulLogin(c *gc.C) { 448 s.DischargerLogin = func() string { 449 return testUser 450 } 451 452 cmd := s.newModelCommandBase() 453 _, err := cmd.NewAPIRoot() 454 c.Assert(err, jc.ErrorIsNil) 455 } 456 457 func (s *macaroonLoginSuite) TestsFailToObtainDischargeLogin(c *gc.C) { 458 s.DischargerLogin = func() string { 459 return "" 460 } 461 462 cmd := s.newModelCommandBase() 463 cmd.SetAPIOpen(s.apiOpen) 464 _, err := cmd.NewAPIRoot() 465 c.Assert(err, gc.ErrorMatches, "cannot get discharge.*", gc.Commentf("%s", errors.Details(err))) 466 } 467 468 func (s *macaroonLoginSuite) TestsUnknownUserLogin(c *gc.C) { 469 s.DischargerLogin = func() string { 470 return "testUnknown@nowhere" 471 } 472 473 cmd := s.newModelCommandBase() 474 cmd.SetAPIOpen(s.apiOpen) 475 _, err := cmd.NewAPIRoot() 476 c.Assert(err, gc.ErrorMatches, "invalid entity name or password \\(unauthorized access\\)", gc.Commentf("details: %s", errors.Details(err))) 477 }