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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package controller_test
     5  
     6  import (
     7  	"regexp"
     8  	"time"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/cmd/cmdtesting"
    12  	"github.com/juju/collections/set"
    13  	gitjujutesting "github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/version"
    16  	gc "gopkg.in/check.v1"
    17  	"gopkg.in/juju/names.v2"
    18  
    19  	"github.com/juju/juju/api/base"
    20  	"github.com/juju/juju/apiserver/common"
    21  	"github.com/juju/juju/apiserver/params"
    22  	"github.com/juju/juju/cmd/juju/controller"
    23  	"github.com/juju/juju/core/model"
    24  	"github.com/juju/juju/core/status"
    25  	"github.com/juju/juju/jujuclient"
    26  	"github.com/juju/juju/testing"
    27  )
    28  
    29  type BaseModelsSuite struct {
    30  	testing.FakeJujuXDGDataHomeSuite
    31  	api   *fakeModelMgrAPIClient
    32  	store *jujuclient.MemStore
    33  }
    34  
    35  type ModelsSuiteV3 struct {
    36  	BaseModelsSuite
    37  }
    38  
    39  type ModelsSuiteV4 struct {
    40  	BaseModelsSuite
    41  }
    42  
    43  var _ = gc.Suite(&ModelsSuiteV3{})
    44  var _ = gc.Suite(&ModelsSuiteV4{})
    45  
    46  type fakeModelMgrAPIClient struct {
    47  	*gitjujutesting.Stub
    48  
    49  	err   error
    50  	infos []params.ModelInfoResult
    51  
    52  	version int
    53  }
    54  
    55  func (f *fakeModelMgrAPIClient) BestAPIVersion() int {
    56  	f.MethodCall(f, "BestAPIVersion")
    57  	return f.version
    58  }
    59  
    60  func (f *fakeModelMgrAPIClient) Close() error {
    61  	f.MethodCall(f, "Close")
    62  	return nil
    63  }
    64  
    65  func (f *fakeModelMgrAPIClient) ListModels(user string) ([]base.UserModel, error) {
    66  	f.MethodCall(f, "ListModels", user)
    67  	if f.err != nil {
    68  		return nil, f.err
    69  	}
    70  	return f.convertInfosToUserModels(), nil
    71  }
    72  
    73  func (f *fakeModelMgrAPIClient) AllModels() ([]base.UserModel, error) {
    74  	f.MethodCall(f, "AllModels")
    75  	if f.err != nil {
    76  		return nil, f.err
    77  	}
    78  	return f.convertInfosToUserModels(), nil
    79  }
    80  
    81  func (f *fakeModelMgrAPIClient) ListModelSummaries(user string, all bool) ([]base.UserModelSummary, error) {
    82  	f.MethodCall(f, "ListModelSummaries", user, all)
    83  	if f.err != nil {
    84  		return nil, f.err
    85  	}
    86  	results := make([]base.UserModelSummary, len(f.infos))
    87  	for i, info := range f.infos {
    88  		results[i] = base.UserModelSummary{}
    89  		if info.Error != nil {
    90  			results[i].Error = info.Error
    91  			continue
    92  		}
    93  		cloud, err := names.ParseCloudTag(info.Result.CloudTag)
    94  		if err != nil {
    95  			cloud = names.NewCloudTag("aws")
    96  
    97  		}
    98  		cred, err := names.ParseCloudCredentialTag(info.Result.CloudCredentialTag)
    99  		if err != nil {
   100  			cred = names.NewCloudCredentialTag("foo/bob/one")
   101  
   102  		}
   103  		owner, err := names.ParseUserTag(info.Result.OwnerTag)
   104  		if err != nil {
   105  			owner = names.NewUserTag("admin")
   106  
   107  		}
   108  		results[i] = base.UserModelSummary{
   109  			Name:            info.Result.Name,
   110  			Type:            model.ModelType(info.Result.Type),
   111  			UUID:            info.Result.UUID,
   112  			ControllerUUID:  info.Result.ControllerUUID,
   113  			IsController:    info.Result.IsController,
   114  			ProviderType:    info.Result.ProviderType,
   115  			DefaultSeries:   info.Result.DefaultSeries,
   116  			Cloud:           cloud.Id(),
   117  			CloudRegion:     info.Result.CloudRegion,
   118  			CloudCredential: cred.Id(),
   119  			Owner:           owner.Id(),
   120  			Life:            string(info.Result.Life),
   121  			Status: base.Status{
   122  				Status: info.Result.Status.Status,
   123  				Info:   info.Result.Status.Info,
   124  				Data:   make(map[string]interface{}),
   125  				Since:  info.Result.Status.Since,
   126  			},
   127  			AgentVersion: info.Result.AgentVersion,
   128  		}
   129  		if info.Result.Migration != nil {
   130  			migration := info.Result.Migration
   131  			results[i].Migration = &base.MigrationSummary{
   132  				Status:    migration.Status,
   133  				StartTime: migration.Start,
   134  				EndTime:   migration.End,
   135  			}
   136  		}
   137  		if info.Result.SLA != nil {
   138  			results[i].SLA = &base.SLASummary{
   139  				Level: info.Result.SLA.Level,
   140  				Owner: info.Result.SLA.Owner,
   141  			}
   142  		}
   143  		if len(info.Result.Users) > 0 {
   144  			for _, u := range info.Result.Users {
   145  				if u.UserName == user {
   146  					results[i].ModelUserAccess = string(u.Access)
   147  					results[i].UserLastConnection = u.LastConnection
   148  					break
   149  				}
   150  			}
   151  		}
   152  		if len(info.Result.Machines) > 0 {
   153  			results[i].Counts = []base.EntityCount{
   154  				{string(params.Machines), int64(len(info.Result.Machines))},
   155  			}
   156  			cores := uint64(0)
   157  			for _, machine := range info.Result.Machines {
   158  				if machine.Hardware != nil && machine.Hardware.Cores != nil {
   159  					cores += *machine.Hardware.Cores
   160  				}
   161  			}
   162  			if cores > 0 {
   163  				results[i].Counts = append(results[i].Counts, base.EntityCount{string(params.Cores), int64(cores)})
   164  			}
   165  		}
   166  	}
   167  	return results, nil
   168  }
   169  
   170  func (f *fakeModelMgrAPIClient) ModelInfo(tags []names.ModelTag) ([]params.ModelInfoResult, error) {
   171  	f.MethodCall(f, "ModelInfo", tags)
   172  	if f.infos != nil {
   173  		return f.infos, nil
   174  	}
   175  	results := make([]params.ModelInfoResult, len(tags))
   176  	for i, tag := range tags {
   177  		for _, model := range f.infos {
   178  			if model.Error == nil {
   179  				if model.Result.UUID != tag.Id() {
   180  					continue
   181  				}
   182  				results[i] = model
   183  			}
   184  		}
   185  	}
   186  	return results, nil
   187  }
   188  
   189  func (f *fakeModelMgrAPIClient) convertInfosToUserModels() []base.UserModel {
   190  	models := make([]base.UserModel, len(f.infos))
   191  	for i, info := range f.infos {
   192  		if info.Error == nil {
   193  			models[i] = base.UserModel{UUID: info.Result.UUID, Type: "local"}
   194  		}
   195  	}
   196  	return models
   197  }
   198  
   199  func (s *BaseModelsSuite) SetUpTest(c *gc.C) {
   200  	s.FakeJujuXDGDataHomeSuite.SetUpTest(c)
   201  
   202  	models := []base.UserModel{
   203  		{
   204  			Name:  "test-model1",
   205  			Owner: "admin",
   206  			UUID:  "test-model1-UUID",
   207  			Type:  model.IAAS,
   208  		}, {
   209  			Name:  "test-model2",
   210  			Owner: "carlotta",
   211  			UUID:  "test-model2-UUID",
   212  			Type:  model.IAAS,
   213  		}, {
   214  			Name:  "test-model3",
   215  			Owner: "daiwik@external",
   216  			UUID:  "test-model3-UUID",
   217  			Type:  model.IAAS,
   218  		},
   219  	}
   220  
   221  	s.store = jujuclient.NewMemStore()
   222  	s.store.CurrentControllerName = "fake"
   223  	s.store.Controllers["fake"] = jujuclient.ControllerDetails{}
   224  	s.store.Models["fake"] = &jujuclient.ControllerModels{
   225  		CurrentModel: "admin/test-model1",
   226  	}
   227  	s.store.Accounts["fake"] = jujuclient.AccountDetails{
   228  		User:     "admin",
   229  		Password: "password",
   230  	}
   231  
   232  	s.api = &fakeModelMgrAPIClient{
   233  		Stub:    &gitjujutesting.Stub{},
   234  		version: 3,
   235  	}
   236  	s.api.infos = convert(models)
   237  
   238  	// Make api results interesting...
   239  	// 1st model
   240  	firstModel := s.api.infos[0].Result
   241  	last1 := time.Date(2015, 3, 20, 0, 0, 0, 0, time.UTC)
   242  	firstModel.Users = []params.ModelUserInfo{{
   243  		UserName:       "admin",
   244  		LastConnection: &last1,
   245  		Access:         params.ModelReadAccess,
   246  	}}
   247  	//2nd model
   248  	secondModel := s.api.infos[1].Result
   249  	last2 := time.Date(2015, 3, 1, 0, 0, 0, 0, time.UTC)
   250  	secondModel.Users = []params.ModelUserInfo{{
   251  		UserName:       "admin",
   252  		LastConnection: &last2,
   253  		Access:         params.ModelWriteAccess,
   254  	}}
   255  	// 3rd model
   256  	s.api.infos[2].Result.Status.Status = status.Destroying
   257  }
   258  
   259  func (s *BaseModelsSuite) TestModelsOwner(c *gc.C) {
   260  	context, err := cmdtesting.RunCommand(c, s.newCommand())
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	c.Assert(cmdtesting.Stdout(context), gc.Equals, ""+
   263  		"Controller: fake\n"+
   264  		"\n"+
   265  		"Model                        Cloud/Region  Type   Status      Access  Last connection\n"+
   266  		"test-model1*                 dummy         local  active      read    2015-03-20\n"+
   267  		"carlotta/test-model2         dummy         local  active      write   2015-03-01\n"+
   268  		"daiwik@external/test-model3  dummy         local  destroying  -       never connected\n"+
   269  		"\n")
   270  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   271  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   272  }
   273  
   274  // TestModelsForAdmin tests that a model admin user will get model credential.
   275  // Credential will only appear in non-tabular format - either yaml or json.
   276  func (s *BaseModelsSuite) TestModelsWithCredentials(c *gc.C) {
   277  	for i, infoResult := range s.api.infos {
   278  		// let's say only some models will have credentials returned from api...
   279  		if i%2 == 0 {
   280  			infoResult.Result.CloudCredentialTag = "cloudcred-some-cloud_some-owner_some-credential"
   281  		}
   282  	}
   283  
   284  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "--format=yaml")
   285  	c.Assert(err, jc.ErrorIsNil)
   286  	c.Assert(cmdtesting.Stdout(context), jc.Contains, "credential")
   287  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   288  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   289  }
   290  
   291  func (s *BaseModelsSuite) TestModelsNonOwner(c *gc.C) {
   292  	// Ensure fake api caters to user 'bob'
   293  	for _, apiInfo := range s.api.infos {
   294  		if apiInfo.Error == nil {
   295  			bobs := make([]params.ModelUserInfo, len(apiInfo.Result.Users))
   296  			for i, u := range apiInfo.Result.Users {
   297  				u.UserName = "bob"
   298  				bobs[i] = u
   299  			}
   300  			apiInfo.Result.Users = bobs
   301  		}
   302  	}
   303  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "--user", "bob")
   304  	c.Assert(err, jc.ErrorIsNil)
   305  	c.Assert(cmdtesting.Stdout(context), gc.Equals, ""+
   306  		"Controller: fake\n"+
   307  		"\n"+
   308  		"Model                        Cloud/Region  Type   Status      Access  Last connection\n"+
   309  		"admin/test-model1*           dummy         local  active      read    2015-03-20\n"+
   310  		"carlotta/test-model2         dummy         local  active      write   2015-03-01\n"+
   311  		"daiwik@external/test-model3  dummy         local  destroying  -       never connected\n"+
   312  		"\n")
   313  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   314  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   315  }
   316  
   317  func (s *BaseModelsSuite) TestModelsNoneCurrent(c *gc.C) {
   318  	delete(s.store.Models, "fake")
   319  	context, err := cmdtesting.RunCommand(c, s.newCommand())
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	c.Assert(cmdtesting.Stdout(context), gc.Equals, ""+
   322  		"Controller: fake\n"+
   323  		"\n"+
   324  		"Model                        Cloud/Region  Type   Status      Access  Last connection\n"+
   325  		"test-model1                  dummy         local  active      read    2015-03-20\n"+
   326  		"carlotta/test-model2         dummy         local  active      write   2015-03-01\n"+
   327  		"daiwik@external/test-model3  dummy         local  destroying  -       never connected\n"+
   328  		"\n")
   329  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   330  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   331  }
   332  
   333  func (s *BaseModelsSuite) TestModelsUUID(c *gc.C) {
   334  	one := uint64(1)
   335  	s.api.infos[0].Result.Machines = []params.ModelMachineInfo{
   336  		{Id: "0", Hardware: &params.MachineHardware{Cores: &one}}, {Id: "1"},
   337  	}
   338  
   339  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "--uuid")
   340  	c.Assert(err, jc.ErrorIsNil)
   341  	c.Assert(cmdtesting.Stdout(context), gc.Equals, ""+
   342  		"Controller: fake\n"+
   343  		"\n"+
   344  		"Model                        UUID              Cloud/Region  Type   Status      Machines  Cores  Access  Last connection\n"+
   345  		"test-model1*                 test-model1-UUID  dummy         local  active             2      1  read    2015-03-20\n"+
   346  		"carlotta/test-model2         test-model2-UUID  dummy         local  active             0      -  write   2015-03-01\n"+
   347  		"daiwik@external/test-model3  test-model3-UUID  dummy         local  destroying         0      -  -       never connected\n"+
   348  		"\n")
   349  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   350  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   351  }
   352  
   353  func (s *BaseModelsSuite) TestModelsMachineInfo(c *gc.C) {
   354  	one := uint64(1)
   355  	s.api.infos[0].Result.Machines = []params.ModelMachineInfo{
   356  		{Id: "0", Hardware: &params.MachineHardware{Cores: &one}}, {Id: "1"},
   357  	}
   358  
   359  	context, err := cmdtesting.RunCommand(c, s.newCommand())
   360  	c.Assert(err, jc.ErrorIsNil)
   361  	c.Assert(cmdtesting.Stdout(context), gc.Equals, ""+
   362  		"Controller: fake\n"+
   363  		"\n"+
   364  		"Model                        Cloud/Region  Type   Status      Machines  Cores  Access  Last connection\n"+
   365  		"test-model1*                 dummy         local  active             2      1  read    2015-03-20\n"+
   366  		"carlotta/test-model2         dummy         local  active             0      -  write   2015-03-01\n"+
   367  		"daiwik@external/test-model3  dummy         local  destroying         0      -  -       never connected\n"+
   368  		"\n")
   369  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   370  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   371  }
   372  
   373  func (s *BaseModelsSuite) TestUnrecognizedArg(c *gc.C) {
   374  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "whoops")
   375  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["whoops"\]`)
   376  	c.Assert(cmdtesting.Stdout(context), gc.Equals, "")
   377  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "ERROR unrecognized args: [\"whoops\"]\n")
   378  	s.api.CheckNoCalls(c)
   379  }
   380  
   381  func (s *BaseModelsSuite) TestInvalidUser(c *gc.C) {
   382  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "--user", "+bob")
   383  	c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`user "+bob" not valid`))
   384  	c.Assert(cmdtesting.Stdout(context), gc.Equals, "")
   385  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "user \"+bob\" not valid\n")
   386  	s.api.CheckNoCalls(c)
   387  }
   388  
   389  func (s *BaseModelsSuite) TestModelsError(c *gc.C) {
   390  	s.api.err = common.ErrPerm
   391  	context, err := cmdtesting.RunCommand(c, s.newCommand())
   392  	c.Assert(err, gc.ErrorMatches, ".*: permission denied")
   393  	c.Assert(cmdtesting.Stdout(context), gc.Equals, "")
   394  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "cannot list models: permission denied\n")
   395  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "Close")
   396  }
   397  
   398  func (s *BaseModelsSuite) TestWithIncompleteModels(c *gc.C) {
   399  	basicAndStatusInfo := createBasicModelInfo()
   400  	basicAndStatusInfo.Status = params.EntityStatus{
   401  		Status: status.Busy,
   402  	}
   403  
   404  	basicAndUsersInfo := createBasicModelInfo()
   405  	basicAndUsersInfo.Users = []params.ModelUserInfo{
   406  		{"admin", "display name", nil, params.UserAccessPermission("admin")},
   407  	}
   408  
   409  	basicAndMachinesInfo := createBasicModelInfo()
   410  	basicAndMachinesInfo.Machines = []params.ModelMachineInfo{
   411  		{Id: "2"},
   412  		{Id: "12"},
   413  	}
   414  
   415  	s.api.infos = []params.ModelInfoResult{
   416  		{Result: createBasicModelInfo()},
   417  		{Result: basicAndStatusInfo},
   418  		{Result: basicAndUsersInfo},
   419  		{Result: basicAndMachinesInfo},
   420  	}
   421  	context, err := cmdtesting.RunCommand(c, s.newCommand())
   422  	c.Assert(err, jc.ErrorIsNil)
   423  	c.Assert(cmdtesting.Stdout(context), gc.Equals, `
   424  Controller: fake
   425  
   426  Model              Cloud/Region           Type   Status  Machines  Access  Last connection
   427  owner/basic-model  altostratus/mid-level  local  -              0  -       never connected
   428  owner/basic-model  altostratus/mid-level  local  busy           0  -       never connected
   429  owner/basic-model  altostratus/mid-level  local  -              0  admin   never connected
   430  owner/basic-model  altostratus/mid-level  local  -              2  -       never connected
   431  
   432  `[1:])
   433  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   434  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   435  }
   436  
   437  func (s *BaseModelsSuite) TestListModelsWithAgent(c *gc.C) {
   438  	basicInfo := createBasicModelInfo()
   439  	s.assertAgentVersionPresent(c, basicInfo, jc.Contains)
   440  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   441  }
   442  
   443  func (s *BaseModelsSuite) TestListModelsWithNoAgent(c *gc.C) {
   444  	basicInfo := createBasicModelInfo()
   445  	basicInfo.AgentVersion = nil
   446  	s.assertAgentVersionPresent(c, basicInfo, gc.Not(jc.Contains))
   447  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   448  }
   449  
   450  func (s *BaseModelsSuite) TestNoModelsMessage(c *gc.C) {
   451  	assertExpectedOutput := func(context *cmd.Context) {
   452  		c.Assert(cmdtesting.Stdout(context), gc.Equals, `
   453  Controller: fake
   454  
   455  Model  Cloud/Region  Type  Status  Access  Last connection
   456  
   457  `[1:])
   458  		c.Assert(cmdtesting.Stderr(context), gc.Equals, controller.NoModelsMessage+"\n")
   459  		s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   460  	}
   461  
   462  	s.api.infos = nil
   463  	context, err := cmdtesting.RunCommand(c, s.newCommand())
   464  	c.Assert(err, jc.ErrorIsNil)
   465  	assertExpectedOutput(context)
   466  
   467  	s.api.ResetCalls()
   468  
   469  	s.api.infos = []params.ModelInfoResult{}
   470  	context, err = cmdtesting.RunCommand(c, s.newCommand())
   471  	c.Assert(err, jc.ErrorIsNil)
   472  	assertExpectedOutput(context)
   473  }
   474  
   475  func (s *BaseModelsSuite) newCommand() cmd.Command {
   476  	return controller.NewListModelsCommandForTest(s.api, s.api, s.store)
   477  }
   478  
   479  func (s *BaseModelsSuite) assertAgentVersionPresent(c *gc.C, testInfo *params.ModelInfo, checker gc.Checker) {
   480  	s.api.infos = []params.ModelInfoResult{
   481  		{Result: testInfo},
   482  	}
   483  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "--format=yaml")
   484  	c.Assert(err, jc.ErrorIsNil)
   485  	c.Assert(cmdtesting.Stdout(context), checker, "agent-version")
   486  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   487  }
   488  
   489  func (s *BaseModelsSuite) checkAPICalls(c *gc.C, expectedCalls ...string) {
   490  	actualCalls := []string{}
   491  
   492  	switch s.api.version {
   493  	case 4:
   494  		oldCalls := set.NewStrings("ModelInfo", "AllModels", "ListModels")
   495  		// need to add Close here too because in previous implementations it could
   496  		// have been called more than once.
   497  		oldCalls.Add("Close")
   498  		for _, call := range expectedCalls {
   499  			if !oldCalls.Contains(call) {
   500  				actualCalls = append(actualCalls, call)
   501  			}
   502  		}
   503  		actualCalls = append(actualCalls, "ListModelSummaries", "Close")
   504  	default:
   505  		actualCalls = expectedCalls
   506  	}
   507  
   508  	s.api.CheckCallNames(c, actualCalls...)
   509  }
   510  
   511  func createBasicModelInfo() *params.ModelInfo {
   512  	agentVersion, _ := version.Parse("2.55.5")
   513  	return &params.ModelInfo{
   514  		Name:           "basic-model",
   515  		UUID:           testing.ModelTag.Id(),
   516  		Type:           "iaas",
   517  		ProviderType:   "local",
   518  		ControllerUUID: testing.ControllerTag.Id(),
   519  		IsController:   false,
   520  		OwnerTag:       names.NewUserTag("owner").String(),
   521  		Life:           params.Dead,
   522  		CloudTag:       names.NewCloudTag("altostratus").String(),
   523  		CloudRegion:    "mid-level",
   524  		AgentVersion:   &agentVersion,
   525  	}
   526  }
   527  
   528  func convert(models []base.UserModel) []params.ModelInfoResult {
   529  	agentVersion, _ := version.Parse("2.55.5")
   530  	infoResults := make([]params.ModelInfoResult, len(models))
   531  	for i, model := range models {
   532  		infoResult := params.ModelInfoResult{}
   533  		infoResult.Result = &params.ModelInfo{
   534  			Name:         model.Name,
   535  			UUID:         model.UUID,
   536  			Type:         model.Type.String(),
   537  			OwnerTag:     names.NewUserTag(model.Owner).String(),
   538  			CloudTag:     "cloud-dummy",
   539  			ProviderType: "local",
   540  			AgentVersion: &agentVersion,
   541  			Status:       params.EntityStatus{Status: status.Active},
   542  		}
   543  		infoResults[i] = infoResult
   544  	}
   545  	return infoResults
   546  }
   547  
   548  func (s *ModelsSuiteV3) SetUpTest(c *gc.C) {
   549  	s.BaseModelsSuite.SetUpTest(c)
   550  	// re-run all the test for ModelManager v3
   551  	s.BaseModelsSuite.api.version = 3
   552  }
   553  
   554  func (s *ModelsSuiteV3) TestModelsWithOneUnauthorised(c *gc.C) {
   555  	c.Assert(s.store.Models["fake"].Models, gc.HasLen, 0)
   556  	s.api.infos[2].Error = &params.Error{
   557  		Message: "permission denied",
   558  		Code:    params.CodeUnauthorized,
   559  	}
   560  
   561  	context, err := cmdtesting.RunCommand(c, s.newCommand())
   562  	c.Assert(err, jc.ErrorIsNil)
   563  	c.Assert(cmdtesting.Stdout(context), gc.Equals, ""+
   564  		"Controller: fake\n"+
   565  		"\n"+
   566  		"Model                 Cloud/Region  Type   Status  Access  Last connection\n"+
   567  		"test-model1*          dummy         local  active  read    2015-03-20\n"+
   568  		"carlotta/test-model2  dummy         local  active  write   2015-03-01\n"+
   569  		"\n")
   570  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   571  	c.Assert(s.store.Models["fake"].Models, gc.DeepEquals, map[string]jujuclient.ModelDetails{
   572  		"admin/test-model1":    {ModelUUID: "test-model1-UUID", ModelType: model.IAAS},
   573  		"carlotta/test-model2": {ModelUUID: "test-model2-UUID", ModelType: model.IAAS},
   574  	})
   575  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   576  }
   577  
   578  func (s *ModelsSuiteV3) TestModelsJson(c *gc.C) {
   579  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "--format", "json")
   580  	c.Assert(err, jc.ErrorIsNil)
   581  	c.Assert(cmdtesting.Stdout(context), gc.Equals, `{"models":[{"name":"admin/test-model1","short-name":"test-model1","model-uuid":"test-model1-UUID","model-type":"iaas","controller-uuid":"","controller-name":"fake","is-controller":false,"owner":"admin","cloud":"dummy","type":"local","life":"","status":{"current":"active"},"users":{"admin":{"access":"read","last-connection":"2015-03-20"}},"agent-version":"2.55.5"},{"name":"carlotta/test-model2","short-name":"test-model2","model-uuid":"test-model2-UUID","model-type":"iaas","controller-uuid":"","controller-name":"fake","is-controller":false,"owner":"carlotta","cloud":"dummy","type":"local","life":"","status":{"current":"active"},"users":{"admin":{"access":"write","last-connection":"2015-03-01"}},"agent-version":"2.55.5"},{"name":"daiwik@external/test-model3","short-name":"test-model3","model-uuid":"test-model3-UUID","model-type":"iaas","controller-uuid":"","controller-name":"fake","is-controller":false,"owner":"daiwik@external","cloud":"dummy","type":"local","life":"","status":{"current":"destroying"},"agent-version":"2.55.5"}],"current-model":"test-model1"}
   582  `)
   583  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   584  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   585  }
   586  
   587  func (s *ModelsSuiteV3) TestModelsYaml(c *gc.C) {
   588  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "--format", "yaml")
   589  	c.Assert(err, jc.ErrorIsNil)
   590  	c.Assert(cmdtesting.Stdout(context), gc.Equals, `
   591  models:
   592  - name: admin/test-model1
   593    short-name: test-model1
   594    model-uuid: test-model1-UUID
   595    model-type: iaas
   596    controller-uuid: ""
   597    controller-name: fake
   598    is-controller: false
   599    owner: admin
   600    cloud: dummy
   601    type: local
   602    life: ""
   603    status:
   604      current: active
   605    users:
   606      admin:
   607        access: read
   608        last-connection: "2015-03-20"
   609    agent-version: 2.55.5
   610  - name: carlotta/test-model2
   611    short-name: test-model2
   612    model-uuid: test-model2-UUID
   613    model-type: iaas
   614    controller-uuid: ""
   615    controller-name: fake
   616    is-controller: false
   617    owner: carlotta
   618    cloud: dummy
   619    type: local
   620    life: ""
   621    status:
   622      current: active
   623    users:
   624      admin:
   625        access: write
   626        last-connection: "2015-03-01"
   627    agent-version: 2.55.5
   628  - name: daiwik@external/test-model3
   629    short-name: test-model3
   630    model-uuid: test-model3-UUID
   631    model-type: iaas
   632    controller-uuid: ""
   633    controller-name: fake
   634    is-controller: false
   635    owner: daiwik@external
   636    cloud: dummy
   637    type: local
   638    life: ""
   639    status:
   640      current: destroying
   641    agent-version: 2.55.5
   642  current-model: test-model1
   643  `[1:])
   644  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   645  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   646  }
   647  
   648  func (s *ModelsSuiteV3) TestAllModels(c *gc.C) {
   649  	c.Assert(s.store.Models["fake"].Models, gc.HasLen, 0)
   650  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "--all")
   651  	c.Assert(err, jc.ErrorIsNil)
   652  	c.Assert(cmdtesting.Stdout(context), gc.Equals, ""+
   653  		"Controller: fake\n"+
   654  		"\n"+
   655  		"Model                        Cloud/Region  Type   Status      Access  Last connection\n"+
   656  		"test-model1*                 dummy         local  active      read    2015-03-20\n"+
   657  		"carlotta/test-model2         dummy         local  active      write   2015-03-01\n"+
   658  		"daiwik@external/test-model3  dummy         local  destroying  -       never connected\n"+
   659  		"\n")
   660  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   661  	c.Assert(s.store.Models["fake"].Models, gc.DeepEquals, map[string]jujuclient.ModelDetails{
   662  		"admin/test-model1":           {ModelUUID: "test-model1-UUID", ModelType: model.IAAS},
   663  		"carlotta/test-model2":        {ModelUUID: "test-model2-UUID", ModelType: model.IAAS},
   664  		"daiwik@external/test-model3": {ModelUUID: "test-model3-UUID", ModelType: model.IAAS},
   665  	})
   666  	s.api.CheckCallNames(c, "BestAPIVersion", "AllModels", "Close", "ModelInfo", "Close")
   667  }
   668  
   669  func (s *ModelsSuiteV4) SetUpTest(c *gc.C) {
   670  	s.BaseModelsSuite.SetUpTest(c)
   671  	// re-run all the test for ModelManager v4
   672  	s.BaseModelsSuite.api.version = 4
   673  }
   674  
   675  func (s *ModelsSuiteV4) TestModelsJson(c *gc.C) {
   676  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "--format", "json")
   677  	c.Assert(err, jc.ErrorIsNil)
   678  	c.Assert(cmdtesting.Stdout(context), gc.Equals, `{"models":[{"name":"admin/test-model1","short-name":"test-model1","model-uuid":"test-model1-UUID","model-type":"iaas","controller-uuid":"","controller-name":"fake","is-controller":false,"owner":"admin","cloud":"dummy","credential":{"name":"one","owner":"bob","cloud":"foo"},"type":"local","life":"","status":{"current":"active"},"access":"read","last-connection":"2015-03-20","agent-version":"2.55.5"},{"name":"carlotta/test-model2","short-name":"test-model2","model-uuid":"test-model2-UUID","model-type":"iaas","controller-uuid":"","controller-name":"fake","is-controller":false,"owner":"carlotta","cloud":"dummy","credential":{"name":"one","owner":"bob","cloud":"foo"},"type":"local","life":"","status":{"current":"active"},"access":"write","last-connection":"2015-03-01","agent-version":"2.55.5"},{"name":"daiwik@external/test-model3","short-name":"test-model3","model-uuid":"test-model3-UUID","model-type":"iaas","controller-uuid":"","controller-name":"fake","is-controller":false,"owner":"daiwik@external","cloud":"dummy","credential":{"name":"one","owner":"bob","cloud":"foo"},"type":"local","life":"","status":{"current":"destroying"},"access":"","last-connection":"never connected","agent-version":"2.55.5"}],"current-model":"test-model1"}
   679  `)
   680  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   681  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   682  }
   683  
   684  func (s *ModelsSuiteV4) TestModelsYaml(c *gc.C) {
   685  	context, err := cmdtesting.RunCommand(c, s.newCommand(), "--format", "yaml")
   686  	c.Assert(err, jc.ErrorIsNil)
   687  	c.Assert(cmdtesting.Stdout(context), gc.Equals, `
   688  models:
   689  - name: admin/test-model1
   690    short-name: test-model1
   691    model-uuid: test-model1-UUID
   692    model-type: iaas
   693    controller-uuid: ""
   694    controller-name: fake
   695    is-controller: false
   696    owner: admin
   697    cloud: dummy
   698    credential:
   699      name: one
   700      owner: bob
   701      cloud: foo
   702    type: local
   703    life: ""
   704    status:
   705      current: active
   706    access: read
   707    last-connection: "2015-03-20"
   708    agent-version: 2.55.5
   709  - name: carlotta/test-model2
   710    short-name: test-model2
   711    model-uuid: test-model2-UUID
   712    model-type: iaas
   713    controller-uuid: ""
   714    controller-name: fake
   715    is-controller: false
   716    owner: carlotta
   717    cloud: dummy
   718    credential:
   719      name: one
   720      owner: bob
   721      cloud: foo
   722    type: local
   723    life: ""
   724    status:
   725      current: active
   726    access: write
   727    last-connection: "2015-03-01"
   728    agent-version: 2.55.5
   729  - name: daiwik@external/test-model3
   730    short-name: test-model3
   731    model-uuid: test-model3-UUID
   732    model-type: iaas
   733    controller-uuid: ""
   734    controller-name: fake
   735    is-controller: false
   736    owner: daiwik@external
   737    cloud: dummy
   738    credential:
   739      name: one
   740      owner: bob
   741      cloud: foo
   742    type: local
   743    life: ""
   744    status:
   745      current: destroying
   746    access: ""
   747    last-connection: never connected
   748    agent-version: 2.55.5
   749  current-model: test-model1
   750  `[1:])
   751  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   752  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   753  }
   754  
   755  func (s *ModelsSuiteV4) TestModelsWithOneModelInError(c *gc.C) {
   756  	c.Assert(s.store.Models["fake"].Models, gc.HasLen, 0)
   757  	s.api.infos[2].Error = &params.Error{
   758  		Message: "some model error",
   759  	}
   760  
   761  	context, err := cmdtesting.RunCommand(c, s.newCommand())
   762  	c.Assert(err, jc.ErrorIsNil)
   763  	c.Assert(cmdtesting.Stdout(context), gc.Equals, ""+
   764  		"Controller: fake\n"+
   765  		"\n"+
   766  		"Model                 Cloud/Region  Type   Status  Access  Last connection\n"+
   767  		"test-model1*          dummy         local  active  read    2015-03-20\n"+
   768  		"carlotta/test-model2  dummy         local  active  write   2015-03-01\n"+
   769  		"\n")
   770  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "some model error\n")
   771  	c.Assert(s.store.Models["fake"].Models, gc.DeepEquals, map[string]jujuclient.ModelDetails{
   772  		"admin/test-model1":    {ModelUUID: "test-model1-UUID", ModelType: model.IAAS},
   773  		"carlotta/test-model2": {ModelUUID: "test-model2-UUID", ModelType: model.IAAS},
   774  	})
   775  	s.checkAPICalls(c, "BestAPIVersion", "ListModels", "ModelInfo", "Close")
   776  }
   777  
   778  func (s *ModelsSuiteV4) TestAllModels(c *gc.C) {
   779  	assertAPICallsArgs := func(all bool) {
   780  		s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   781  			"BestAPIVersion", []interface{}{},
   782  		}, {
   783  			"ListModelSummaries", []interface{}{"admin", all},
   784  		}, {
   785  			"Close", []interface{}{},
   786  		},
   787  		})
   788  	}
   789  
   790  	_, err := cmdtesting.RunCommand(c, s.newCommand(), "--all")
   791  	c.Assert(err, jc.ErrorIsNil)
   792  	assertAPICallsArgs(true)
   793  
   794  	s.api.ResetCalls()
   795  
   796  	_, err = cmdtesting.RunCommand(c, s.newCommand())
   797  	c.Assert(err, jc.ErrorIsNil)
   798  	assertAPICallsArgs(false)
   799  }