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  }