github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/modelmanager/modelinfo_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package modelmanager_test
     5  
     6  import (
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/juju/collections/set"
    11  	"github.com/juju/description/v5"
    12  	"github.com/juju/errors"
    13  	"github.com/juju/names/v5"
    14  	jujutesting "github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/version/v2"
    17  	gc "gopkg.in/check.v1"
    18  	"gopkg.in/juju/environschema.v1"
    19  
    20  	"github.com/juju/juju/apiserver/common"
    21  	commonsecrets "github.com/juju/juju/apiserver/common/secrets"
    22  	"github.com/juju/juju/apiserver/facades/client/modelmanager"
    23  	apiservertesting "github.com/juju/juju/apiserver/testing"
    24  	"github.com/juju/juju/caas"
    25  	"github.com/juju/juju/cloud"
    26  	"github.com/juju/juju/controller"
    27  	"github.com/juju/juju/core/assumes"
    28  	"github.com/juju/juju/core/base"
    29  	"github.com/juju/juju/core/instance"
    30  	"github.com/juju/juju/core/life"
    31  	"github.com/juju/juju/core/network"
    32  	"github.com/juju/juju/core/permission"
    33  	"github.com/juju/juju/core/secrets"
    34  	"github.com/juju/juju/core/status"
    35  	"github.com/juju/juju/environs"
    36  	environscloudspec "github.com/juju/juju/environs/cloudspec"
    37  	"github.com/juju/juju/environs/config"
    38  	"github.com/juju/juju/environs/context"
    39  	"github.com/juju/juju/rpc/params"
    40  	"github.com/juju/juju/secrets/provider"
    41  	"github.com/juju/juju/state"
    42  	coretesting "github.com/juju/juju/testing"
    43  )
    44  
    45  type modelInfoSuite struct {
    46  	coretesting.BaseSuite
    47  	authorizer   apiservertesting.FakeAuthorizer
    48  	st           *mockState
    49  	ctlrSt       *mockState
    50  	modelmanager *modelmanager.ModelManagerAPI
    51  
    52  	callContext context.ProviderCallContext
    53  }
    54  
    55  func pUint64(v uint64) *uint64 {
    56  	return &v
    57  }
    58  
    59  var _ = gc.Suite(&modelInfoSuite{})
    60  
    61  func (s *modelInfoSuite) SetUpTest(c *gc.C) {
    62  	s.BaseSuite.SetUpTest(c)
    63  	s.authorizer = apiservertesting.FakeAuthorizer{
    64  		Tag: names.NewUserTag("admin@local"),
    65  	}
    66  	s.st = &mockState{
    67  		controllerUUID: coretesting.ControllerTag.Id(),
    68  		cloud: cloud.Cloud{
    69  			Type:      "dummy",
    70  			AuthTypes: []cloud.AuthType{cloud.EmptyAuthType},
    71  		},
    72  		cfgDefaults: config.ModelDefaultAttributes{
    73  			"attr": config.AttributeDefaultValues{
    74  				Default:    "",
    75  				Controller: "val",
    76  				Regions: []config.RegionDefaultValue{{
    77  					Name:  "dummy",
    78  					Value: "val++"}}},
    79  			"attr2": config.AttributeDefaultValues{
    80  				Controller: "val3",
    81  				Default:    "val2",
    82  				Regions: []config.RegionDefaultValue{{
    83  					Name:  "left",
    84  					Value: "spam"}}},
    85  		},
    86  	}
    87  	controllerModel := &mockModel{
    88  		owner: names.NewUserTag("admin@local"),
    89  		life:  state.Alive,
    90  		cfg:   coretesting.ModelConfig(c),
    91  		// This feels kind of wrong as both controller model and
    92  		// default model will end up with the same model tag.
    93  		tag:            coretesting.ModelTag,
    94  		controllerUUID: s.st.controllerUUID,
    95  		isController:   true,
    96  		status: status.StatusInfo{
    97  			Status: status.Available,
    98  			Since:  &time.Time{},
    99  		},
   100  		users: []*mockModelUser{{
   101  			userName: "admin",
   102  			access:   permission.AdminAccess,
   103  		}, {
   104  			userName: "otheruser",
   105  			access:   permission.AdminAccess,
   106  		}},
   107  	}
   108  	s.st.controllerModel = controllerModel
   109  
   110  	s.ctlrSt = &mockState{
   111  		model:           controllerModel,
   112  		controllerModel: controllerModel,
   113  	}
   114  
   115  	s.st.model = &mockModel{
   116  		owner:          names.NewUserTag("bob@local"),
   117  		cfg:            coretesting.ModelConfig(c),
   118  		tag:            coretesting.ModelTag,
   119  		controllerUUID: s.st.controllerUUID,
   120  		isController:   false,
   121  		life:           state.Dying,
   122  		cloud: cloud.Cloud{
   123  			Type:      "dummy",
   124  			AuthTypes: []cloud.AuthType{cloud.EmptyAuthType},
   125  		},
   126  		status: status.StatusInfo{
   127  			Status: status.Destroying,
   128  			Since:  &time.Time{},
   129  		},
   130  		users: []*mockModelUser{{
   131  			userName: "admin",
   132  			access:   permission.AdminAccess,
   133  		}, {
   134  			userName:    "bob",
   135  			displayName: "Bob",
   136  			access:      permission.ReadAccess,
   137  		}, {
   138  			userName:    "charlotte",
   139  			displayName: "Charlotte",
   140  			access:      permission.ReadAccess,
   141  		}, {
   142  			userName:    "mary",
   143  			displayName: "Mary",
   144  			access:      permission.WriteAccess,
   145  		}},
   146  	}
   147  	s.st.machines = []common.Machine{
   148  		&mockMachine{
   149  			id:            "1",
   150  			containerType: "none",
   151  			life:          state.Alive,
   152  			hw:            &instance.HardwareCharacteristics{CpuCores: pUint64(1)},
   153  		},
   154  		&mockMachine{
   155  			id:            "2",
   156  			life:          state.Alive,
   157  			containerType: "lxc",
   158  		},
   159  		&mockMachine{
   160  			id:   "3",
   161  			life: state.Dead,
   162  		},
   163  	}
   164  	s.st.controllerNodes = []common.ControllerNode{
   165  		&mockControllerNode{
   166  			id:        "1",
   167  			hasVote:   true,
   168  			wantsVote: true,
   169  		},
   170  		&mockControllerNode{
   171  			id:        "2",
   172  			hasVote:   false,
   173  			wantsVote: true,
   174  		},
   175  	}
   176  
   177  	s.callContext = context.NewEmptyCloudCallContext()
   178  
   179  	var err error
   180  	s.modelmanager, err = modelmanager.NewModelManagerAPI(
   181  		s.st, s.ctlrSt, nil, nil, common.NewBlockChecker(s.st),
   182  		&s.authorizer, s.st.model, s.callContext,
   183  	)
   184  	c.Assert(err, jc.ErrorIsNil)
   185  
   186  	var fs assumes.FeatureSet
   187  	fs.Add(assumes.Feature{Name: "example"})
   188  	modelmanager.MockSupportedFeatures(fs)
   189  }
   190  
   191  func (s *modelInfoSuite) TearDownTest(c *gc.C) {
   192  	modelmanager.ResetSupportedFeaturesGetter()
   193  }
   194  
   195  func (s *modelInfoSuite) setAPIUser(c *gc.C, user names.UserTag) {
   196  	s.authorizer.Tag = user
   197  	var err error
   198  	s.modelmanager, err = modelmanager.NewModelManagerAPI(
   199  		s.st, s.ctlrSt, nil, nil,
   200  		common.NewBlockChecker(s.st), s.authorizer, s.st.model, s.callContext,
   201  	)
   202  	c.Assert(err, jc.ErrorIsNil)
   203  }
   204  
   205  func (s *modelInfoSuite) expectedModelInfo(c *gc.C, credentialValidity *bool) params.ModelInfo {
   206  	expectedAgentVersion, exists := s.st.model.cfg.AgentVersion()
   207  	c.Assert(exists, jc.IsTrue)
   208  	info := params.ModelInfo{
   209  		Name:               "testmodel",
   210  		UUID:               s.st.model.cfg.UUID(),
   211  		Type:               string(s.st.model.Type()),
   212  		ControllerUUID:     "deadbeef-1bad-500d-9000-4b1d0d06f00d",
   213  		IsController:       false,
   214  		OwnerTag:           "user-bob",
   215  		ProviderType:       "someprovider",
   216  		CloudTag:           "cloud-some-cloud",
   217  		CloudRegion:        "some-region",
   218  		CloudCredentialTag: "cloudcred-some-cloud_bob_some-credential",
   219  		Life:               life.Dying,
   220  		Status: params.EntityStatus{
   221  			Status: status.Destroying,
   222  			Since:  &time.Time{},
   223  		},
   224  		Users: []params.ModelUserInfo{{
   225  			UserName:       "admin",
   226  			LastConnection: &time.Time{},
   227  			Access:         params.ModelAdminAccess,
   228  		}, {
   229  			UserName:       "bob",
   230  			DisplayName:    "Bob",
   231  			LastConnection: &time.Time{},
   232  			Access:         params.ModelReadAccess,
   233  		}, {
   234  			UserName:       "charlotte",
   235  			DisplayName:    "Charlotte",
   236  			LastConnection: &time.Time{},
   237  			Access:         params.ModelReadAccess,
   238  		}, {
   239  			UserName:       "mary",
   240  			DisplayName:    "Mary",
   241  			LastConnection: &time.Time{},
   242  			Access:         params.ModelWriteAccess,
   243  		}},
   244  		Machines: []params.ModelMachineInfo{{
   245  			Id:        "1",
   246  			Hardware:  &params.MachineHardware{Cores: pUint64(1)},
   247  			HasVote:   true,
   248  			WantsVote: true,
   249  		}, {
   250  			Id:        "2",
   251  			WantsVote: true,
   252  		}},
   253  		SecretBackends: []params.SecretBackendResult{{
   254  			Result: params.SecretBackend{
   255  				Name:        "myvault",
   256  				BackendType: "vault",
   257  				Config: map[string]interface{}{
   258  					"endpoint": "http://vault",
   259  				},
   260  			},
   261  			Status:     "active",
   262  			NumSecrets: 2,
   263  		}},
   264  		SLA: &params.ModelSLAInfo{
   265  			Level: "essential",
   266  			Owner: "user",
   267  		},
   268  		AgentVersion: &expectedAgentVersion,
   269  		SupportedFeatures: []params.SupportedFeature{
   270  			{Name: "example"},
   271  		},
   272  	}
   273  	info.CloudCredentialValidity = credentialValidity
   274  	return info
   275  }
   276  
   277  func (s *modelInfoSuite) TestModelInfo(c *gc.C) {
   278  	s.PatchValue(&commonsecrets.GetProvider, func(string) (provider.SecretBackendProvider, error) {
   279  		return mockSecretProvider{}, nil
   280  	})
   281  	info := s.getModelInfo(c, s.modelmanager, s.st.model.cfg.UUID())
   282  	_true := true
   283  	s.assertModelInfo(c, info, s.expectedModelInfo(c, &_true))
   284  	s.st.CheckCalls(c, []jujutesting.StubCall{
   285  		{"ControllerTag", nil},
   286  		{"GetBackend", []interface{}{s.st.model.cfg.UUID()}},
   287  		{"Model", nil},
   288  		{"IsController", nil},
   289  		{"AllMachines", nil},
   290  		{"ControllerNodes", nil},
   291  		{"HAPrimaryMachine", nil},
   292  		{"ControllerUUID", nil},
   293  		{"LatestMigration", nil},
   294  		{"CloudCredential", []interface{}{names.NewCloudCredentialTag("some-cloud/bob/some-credential")}},
   295  	})
   296  }
   297  
   298  func (s *modelInfoSuite) TestModelInfoV9(c *gc.C) {
   299  	s.PatchValue(&commonsecrets.GetProvider, func(string) (provider.SecretBackendProvider, error) {
   300  		return mockSecretProvider{}, nil
   301  	})
   302  	modelManagerV9 := &modelmanager.ModelManagerAPIV9{s.modelmanager}
   303  	info := s.getModelInfo(c, modelManagerV9, s.st.model.cfg.UUID())
   304  	_true := true
   305  	expectedInfo := s.expectedModelInfo(c, &_true)
   306  	expectedInfo.DefaultSeries = "jammy"
   307  	expectedInfo.DefaultBase = "ubuntu@22.04/stable"
   308  	s.assertModelInfo(c, info, expectedInfo)
   309  
   310  	s.st.CheckCalls(c, []jujutesting.StubCall{
   311  		{"ControllerTag", nil},
   312  		{"GetBackend", []interface{}{s.st.model.cfg.UUID()}},
   313  		{"Model", nil},
   314  		{"IsController", nil},
   315  		{"AllMachines", nil},
   316  		{"ControllerNodes", nil},
   317  		{"HAPrimaryMachine", nil},
   318  		{"ControllerUUID", nil},
   319  		{"LatestMigration", nil},
   320  		{"CloudCredential", []interface{}{names.NewCloudCredentialTag("some-cloud/bob/some-credential")}},
   321  	})
   322  }
   323  
   324  func (s *modelInfoSuite) assertModelInfo(c *gc.C, got, expected params.ModelInfo) {
   325  	c.Assert(got, jc.DeepEquals, expected)
   326  	s.st.model.CheckCalls(c, []jujutesting.StubCall{
   327  		{"Name", nil},
   328  		{"Type", nil},
   329  		{"UUID", nil},
   330  		{"ControllerUUID", nil},
   331  		{"UUID", nil},
   332  		{"Owner", nil},
   333  		{"Life", nil},
   334  		{"CloudName", nil},
   335  		{"CloudRegion", nil},
   336  		{"CloudCredentialTag", nil},
   337  		{"SLALevel", nil},
   338  		{"SLAOwner", nil},
   339  		{"Life", nil},
   340  		{"Config", nil},
   341  		{"Status", nil},
   342  		{"Users", nil},
   343  		{"ModelTag", nil},
   344  		{"ModelTag", nil},
   345  		{"ModelTag", nil},
   346  		{"ModelTag", nil},
   347  		{"LastModelConnection", []interface{}{names.NewUserTag("admin")}},
   348  		{"LastModelConnection", []interface{}{names.NewLocalUserTag("bob")}},
   349  		{"LastModelConnection", []interface{}{names.NewLocalUserTag("charlotte")}},
   350  		{"LastModelConnection", []interface{}{names.NewLocalUserTag("mary")}},
   351  		{"Type", nil},
   352  	})
   353  }
   354  
   355  func (s *modelInfoSuite) TestModelInfoWriteAccess(c *gc.C) {
   356  	mary := names.NewUserTag("mary@local")
   357  	s.authorizer.HasWriteTag = mary
   358  	s.setAPIUser(c, mary)
   359  	info := s.getModelInfo(c, s.modelmanager, s.st.model.cfg.UUID())
   360  	c.Assert(info.Users, gc.HasLen, 1)
   361  	c.Assert(info.Users[0].UserName, gc.Equals, "mary")
   362  	c.Assert(info.Machines, gc.HasLen, 2)
   363  }
   364  
   365  func (s *modelInfoSuite) TestModelInfoNonOwner(c *gc.C) {
   366  	s.setAPIUser(c, names.NewUserTag("charlotte@local"))
   367  	info := s.getModelInfo(c, s.modelmanager, s.st.model.cfg.UUID())
   368  	c.Assert(info.Users, gc.HasLen, 1)
   369  	c.Assert(info.Users[0].UserName, gc.Equals, "charlotte")
   370  	c.Assert(info.Machines, gc.HasLen, 0)
   371  }
   372  
   373  type modelInfo interface {
   374  	ModelInfo(params.Entities) (params.ModelInfoResults, error)
   375  }
   376  
   377  func (s *modelInfoSuite) getModelInfo(c *gc.C, modelInfo modelInfo, modelUUID string) params.ModelInfo {
   378  	results, err := modelInfo.ModelInfo(params.Entities{
   379  		Entities: []params.Entity{{
   380  			names.NewModelTag(modelUUID).String(),
   381  		}},
   382  	})
   383  	c.Assert(err, jc.ErrorIsNil)
   384  	c.Assert(results.Results, gc.HasLen, 1)
   385  	c.Check(results.Results[0].Result, gc.NotNil)
   386  	c.Check(results.Results[0].Error, gc.IsNil)
   387  	return *results.Results[0].Result
   388  }
   389  
   390  func (s *modelInfoSuite) TestModelInfoErrorInvalidTag(c *gc.C) {
   391  	s.testModelInfoError(c, "user-bob", `"user-bob" is not a valid model tag`)
   392  }
   393  
   394  func (s *modelInfoSuite) TestModelInfoErrorGetModelNotFound(c *gc.C) {
   395  	s.st.SetErrors(errors.NotFoundf("model"))
   396  	s.testModelInfoError(c, coretesting.ModelTag.String(), `permission denied`)
   397  }
   398  
   399  func (s *modelInfoSuite) TestModelInfoErrorModelConfig(c *gc.C) {
   400  	s.st.model.SetErrors(errors.Errorf("no config for you"))
   401  	s.testModelInfoError(c, coretesting.ModelTag.String(), `no config for you`)
   402  }
   403  
   404  func (s *modelInfoSuite) TestModelInfoErrorModelUsers(c *gc.C) {
   405  	s.st.model.SetErrors(
   406  		nil,                               //Config
   407  		nil,                               //Status
   408  		errors.Errorf("no users for you"), // Users
   409  	)
   410  	s.testModelInfoError(c, coretesting.ModelTag.String(), `no users for you`)
   411  }
   412  
   413  func (s *modelInfoSuite) TestModelInfoErrorNoModelUsers(c *gc.C) {
   414  	s.st.model.users = nil
   415  	s.testModelInfoError(c, coretesting.ModelTag.String(), `permission denied`)
   416  }
   417  
   418  func (s *modelInfoSuite) TestModelInfoErrorNoAccess(c *gc.C) {
   419  	s.setAPIUser(c, names.NewUserTag("nemo@local"))
   420  	s.testModelInfoError(c, coretesting.ModelTag.String(), `permission denied`)
   421  }
   422  
   423  func (s *modelInfoSuite) TestRunningMigration(c *gc.C) {
   424  	start := time.Now().Add(-20 * time.Minute)
   425  	s.st.migration = &mockMigration{
   426  		status: "computing optimal bin packing",
   427  		start:  start,
   428  	}
   429  
   430  	results, err := s.modelmanager.ModelInfo(params.Entities{
   431  		Entities: []params.Entity{{coretesting.ModelTag.String()}},
   432  	})
   433  
   434  	c.Assert(err, jc.ErrorIsNil)
   435  	migrationResult := results.Results[0].Result.Migration
   436  	c.Assert(migrationResult.Status, gc.Equals, "computing optimal bin packing")
   437  	c.Assert(*migrationResult.Start, gc.Equals, start)
   438  	c.Assert(migrationResult.End, gc.IsNil)
   439  }
   440  
   441  func (s *modelInfoSuite) TestFailedMigration(c *gc.C) {
   442  	start := time.Now().Add(-20 * time.Minute)
   443  	end := time.Now().Add(-10 * time.Minute)
   444  	s.st.migration = &mockMigration{
   445  		status: "couldn't realign alternate time frames",
   446  		start:  start,
   447  		end:    end,
   448  	}
   449  
   450  	results, err := s.modelmanager.ModelInfo(params.Entities{
   451  		Entities: []params.Entity{{coretesting.ModelTag.String()}},
   452  	})
   453  
   454  	c.Assert(err, jc.ErrorIsNil)
   455  	migrationResult := results.Results[0].Result.Migration
   456  	c.Assert(migrationResult.Status, gc.Equals, "couldn't realign alternate time frames")
   457  	c.Assert(*migrationResult.Start, gc.Equals, start)
   458  	c.Assert(*migrationResult.End, gc.Equals, end)
   459  }
   460  
   461  func (s *modelInfoSuite) TestNoMigration(c *gc.C) {
   462  	results, err := s.modelmanager.ModelInfo(params.Entities{
   463  		Entities: []params.Entity{{coretesting.ModelTag.String()}},
   464  	})
   465  	c.Assert(err, jc.ErrorIsNil)
   466  	c.Assert(results.Results[0].Result.Migration, gc.IsNil)
   467  }
   468  
   469  func (s *modelInfoSuite) TestAliveModelGetsAllInfo(c *gc.C) {
   470  	s.assertSuccess(c, s.st.model.cfg.UUID(), state.Alive, life.Alive)
   471  }
   472  
   473  func (s *modelInfoSuite) TestAliveModelWithConfigFailure(c *gc.C) {
   474  	s.st.model.life = state.Alive
   475  	s.setModelConfigError()
   476  	s.testModelInfoError(c, s.st.model.tag.String(), "config not found")
   477  }
   478  
   479  func (s *modelInfoSuite) TestAliveModelWithStatusFailure(c *gc.C) {
   480  	s.st.model.life = state.Alive
   481  	s.setModelStatusError()
   482  	s.testModelInfoError(c, s.st.model.tag.String(), "status not found")
   483  }
   484  
   485  func (s *modelInfoSuite) TestAliveModelWithUsersFailure(c *gc.C) {
   486  	s.st.model.life = state.Alive
   487  	s.setModelUsersError()
   488  	s.testModelInfoError(c, s.st.model.tag.String(), "users not found")
   489  }
   490  
   491  func (s *modelInfoSuite) TestDeadModelGetsAllInfo(c *gc.C) {
   492  	s.assertSuccess(c, s.st.model.cfg.UUID(), state.Dead, life.Dead)
   493  }
   494  
   495  func (s *modelInfoSuite) TestDeadModelWithConfigFailure(c *gc.C) {
   496  	testData := incompleteModelInfoTest{
   497  		failModel:    s.setModelConfigError,
   498  		desiredLife:  state.Dead,
   499  		expectedLife: life.Dead,
   500  	}
   501  	s.assertSuccessWithMissingData(c, testData)
   502  }
   503  
   504  func (s *modelInfoSuite) TestDeadModelWithStatusFailure(c *gc.C) {
   505  	testData := incompleteModelInfoTest{
   506  		failModel:    s.setModelStatusError,
   507  		desiredLife:  state.Dead,
   508  		expectedLife: life.Dead,
   509  	}
   510  	s.assertSuccessWithMissingData(c, testData)
   511  }
   512  
   513  func (s *modelInfoSuite) TestDeadModelWithUsersFailure(c *gc.C) {
   514  	testData := incompleteModelInfoTest{
   515  		failModel:    s.setModelUsersError,
   516  		desiredLife:  state.Dead,
   517  		expectedLife: life.Dead,
   518  	}
   519  	s.assertSuccessWithMissingData(c, testData)
   520  }
   521  
   522  func (s *modelInfoSuite) TestDyingModelWithConfigFailure(c *gc.C) {
   523  	testData := incompleteModelInfoTest{
   524  		failModel:    s.setModelConfigError,
   525  		desiredLife:  state.Dying,
   526  		expectedLife: life.Dying,
   527  	}
   528  	s.assertSuccessWithMissingData(c, testData)
   529  }
   530  
   531  func (s *modelInfoSuite) TestDyingModelWithStatusFailure(c *gc.C) {
   532  	testData := incompleteModelInfoTest{
   533  		failModel:    s.setModelStatusError,
   534  		desiredLife:  state.Dying,
   535  		expectedLife: life.Dying,
   536  	}
   537  	s.assertSuccessWithMissingData(c, testData)
   538  }
   539  
   540  func (s *modelInfoSuite) TestDyingModelWithUsersFailure(c *gc.C) {
   541  	testData := incompleteModelInfoTest{
   542  		failModel:    s.setModelUsersError,
   543  		desiredLife:  state.Dying,
   544  		expectedLife: life.Dying,
   545  	}
   546  	s.assertSuccessWithMissingData(c, testData)
   547  }
   548  
   549  func (s *modelInfoSuite) TestImportingModelGetsAllInfo(c *gc.C) {
   550  	s.st.model.migrationStatus = state.MigrationModeImporting
   551  	s.assertSuccess(c, s.st.model.cfg.UUID(), state.Alive, life.Alive)
   552  }
   553  
   554  func (s *modelInfoSuite) TestImportingModelWithConfigFailure(c *gc.C) {
   555  	s.st.model.migrationStatus = state.MigrationModeImporting
   556  	testData := incompleteModelInfoTest{
   557  		failModel:    s.setModelConfigError,
   558  		desiredLife:  state.Alive,
   559  		expectedLife: life.Alive,
   560  	}
   561  	s.assertSuccessWithMissingData(c, testData)
   562  }
   563  
   564  func (s *modelInfoSuite) TestImportingModelWithStatusFailure(c *gc.C) {
   565  	s.st.model.migrationStatus = state.MigrationModeImporting
   566  	testData := incompleteModelInfoTest{
   567  		failModel:    s.setModelStatusError,
   568  		desiredLife:  state.Alive,
   569  		expectedLife: life.Alive,
   570  	}
   571  	s.assertSuccessWithMissingData(c, testData)
   572  }
   573  
   574  func (s *modelInfoSuite) TestImportingModelWithUsersFailure(c *gc.C) {
   575  	s.st.model.migrationStatus = state.MigrationModeImporting
   576  	testData := incompleteModelInfoTest{
   577  		failModel:    s.setModelUsersError,
   578  		desiredLife:  state.Alive,
   579  		expectedLife: life.Alive,
   580  	}
   581  	s.assertSuccessWithMissingData(c, testData)
   582  }
   583  
   584  type incompleteModelInfoTest struct {
   585  	failModel    func()
   586  	desiredLife  state.Life
   587  	expectedLife life.Value
   588  }
   589  
   590  func (s *modelInfoSuite) setModelConfigError() {
   591  	s.st.model.SetErrors(errors.NotFoundf("config"))
   592  }
   593  
   594  func (s *modelInfoSuite) setModelStatusError() {
   595  	s.st.model.SetErrors(
   596  		nil,                        //Config
   597  		errors.NotFoundf("status"), //Status
   598  	)
   599  }
   600  
   601  func (s *modelInfoSuite) setModelUsersError() {
   602  	s.st.model.SetErrors(
   603  		nil,                       //Config
   604  		nil,                       //Status
   605  		errors.NotFoundf("users"), //Users
   606  	)
   607  }
   608  
   609  func (s *modelInfoSuite) assertSuccessWithMissingData(c *gc.C, test incompleteModelInfoTest) {
   610  	test.failModel()
   611  	// We do not expect any errors to surface and still want to get basic model info.
   612  	s.assertSuccess(c, s.st.model.cfg.UUID(), test.desiredLife, test.expectedLife)
   613  }
   614  
   615  func (s *modelInfoSuite) assertSuccess(c *gc.C, modelUUID string, desiredLife state.Life, expectedLife life.Value) {
   616  	s.st.model.life = desiredLife
   617  	// should get no errors
   618  	info := s.getModelInfo(c, s.modelmanager, modelUUID)
   619  	c.Assert(info.UUID, gc.Equals, modelUUID)
   620  	c.Assert(info.Life, gc.Equals, expectedLife)
   621  }
   622  
   623  func (s *modelInfoSuite) testModelInfoError(c *gc.C, modelTag, expectedErr string) {
   624  	results, err := s.modelmanager.ModelInfo(params.Entities{
   625  		Entities: []params.Entity{{modelTag}},
   626  	})
   627  	c.Assert(err, jc.ErrorIsNil)
   628  	c.Assert(results.Results, gc.HasLen, 1)
   629  	c.Assert(results.Results[0].Result, gc.IsNil)
   630  	c.Assert(results.Results[0].Error, gc.ErrorMatches, expectedErr)
   631  }
   632  
   633  type unitRetriever interface {
   634  	Unit(name string) (*state.Unit, error)
   635  }
   636  
   637  // metricSender defines methods required by the metricsender package.
   638  type metricSender interface {
   639  	MetricsManager() (*state.MetricsManager, error)
   640  	MetricsToSend(batchSize int) ([]*state.MetricBatch, error)
   641  	SetMetricBatchesSent(batchUUIDs []string) error
   642  	CountOfUnsentMetrics() (int, error)
   643  	CountOfSentMetrics() (int, error)
   644  	CleanupOldMetrics() error
   645  }
   646  
   647  type mockCaasBroker struct {
   648  	jujutesting.Stub
   649  	caas.Broker
   650  
   651  	namespace string
   652  }
   653  
   654  func (m *mockCaasBroker) Create(context.ProviderCallContext, environs.CreateParams) error {
   655  	m.MethodCall(m, "Create")
   656  	if m.namespace == "existing-ns" {
   657  		return errors.AlreadyExistsf("namespace %q", m.namespace)
   658  	}
   659  	return nil
   660  }
   661  
   662  type mockState struct {
   663  	jujutesting.Stub
   664  
   665  	environs.EnvironConfigGetter
   666  	common.APIHostPortsForAgentsGetter
   667  	common.ToolsStorageGetter
   668  	common.BlockGetter
   669  	metricSender
   670  	unitRetriever
   671  
   672  	controllerCfg   *controller.Config
   673  	controllerUUID  string
   674  	cloud           cloud.Cloud
   675  	clouds          map[names.CloudTag]cloud.Cloud
   676  	cloudUsers      map[string]permission.Access
   677  	model           *mockModel
   678  	controllerModel *mockModel
   679  	users           []permission.UserAccess
   680  	cred            state.Credential
   681  	machines        []common.Machine
   682  	controllerNodes []common.ControllerNode
   683  	cfgDefaults     config.ModelDefaultAttributes
   684  	blockMsg        string
   685  	block           state.BlockType
   686  	migration       *mockMigration
   687  	modelConfig     *config.Config
   688  
   689  	modelDetailsForUser func() ([]state.ModelSummary, error)
   690  }
   691  
   692  type fakeModelDescription struct {
   693  	description.Model `yaml:"-"`
   694  
   695  	UUID string `yaml:"model-uuid"`
   696  }
   697  
   698  func (st *mockState) ModelUUID() string {
   699  	st.MethodCall(st, "ModelUUID")
   700  	return st.model.UUID()
   701  }
   702  
   703  func (st *mockState) Name() string {
   704  	st.MethodCall(st, "Name")
   705  	return "test-model"
   706  }
   707  
   708  func (st *mockState) ControllerModelUUID() string {
   709  	st.MethodCall(st, "ControllerModelUUID")
   710  	return st.controllerModel.tag.Id()
   711  }
   712  
   713  func (st *mockState) ControllerModelTag() names.ModelTag {
   714  	st.MethodCall(st, "ControllerModelTag")
   715  	return st.controllerModel.tag
   716  }
   717  
   718  func (st *mockState) Export(leaders map[string]string) (description.Model, error) {
   719  	st.MethodCall(st, "Export", leaders)
   720  	return &fakeModelDescription{UUID: st.model.UUID()}, nil
   721  }
   722  
   723  func (st *mockState) ExportPartial(cfg state.ExportConfig) (description.Model, error) {
   724  	st.MethodCall(st, "ExportPartial", cfg)
   725  	if !cfg.IgnoreIncompleteModel {
   726  		return nil, errors.New("expected IgnoreIncompleteModel=true")
   727  	}
   728  	return &fakeModelDescription{UUID: st.model.UUID()}, nil
   729  }
   730  
   731  func (st *mockState) AllModelUUIDs() ([]string, error) {
   732  	st.MethodCall(st, "AllModelUUIDs")
   733  	return []string{st.model.UUID()}, st.NextErr()
   734  }
   735  
   736  func (st *mockState) GetBackend(modelUUID string) (common.ModelManagerBackend, func() bool, error) {
   737  	st.MethodCall(st, "GetBackend", modelUUID)
   738  	return st, func() bool { return true }, st.NextErr()
   739  }
   740  
   741  func (st *mockState) GetModel(modelUUID string) (common.Model, func() bool, error) {
   742  	st.MethodCall(st, "GetModel", modelUUID)
   743  	return st.model, func() bool { return true }, st.NextErr()
   744  }
   745  
   746  func (st *mockState) ModelUUIDsForUser(user names.UserTag) ([]string, error) {
   747  	st.MethodCall(st, "ModelUUIDsForUser", user)
   748  	return nil, st.NextErr()
   749  }
   750  
   751  func (st *mockState) AllApplications() ([]common.Application, error) {
   752  	st.MethodCall(st, "AllApplications")
   753  	return nil, st.NextErr()
   754  }
   755  
   756  func (st *mockState) AllVolumes() ([]state.Volume, error) {
   757  	st.MethodCall(st, "AllVolumes")
   758  	return nil, st.NextErr()
   759  }
   760  
   761  func (st *mockState) AllFilesystems() ([]state.Filesystem, error) {
   762  	st.MethodCall(st, "AllFilesystems")
   763  	return nil, st.NextErr()
   764  }
   765  
   766  func (st *mockState) IsControllerAdmin(user names.UserTag) (bool, error) {
   767  	st.MethodCall(st, "IsControllerAdmin", user)
   768  	if st.controllerModel == nil {
   769  		return user.Id() == "admin", st.NextErr()
   770  	}
   771  	if st.controllerModel.users == nil {
   772  		return user.Id() == "admin", st.NextErr()
   773  	}
   774  
   775  	for _, u := range st.controllerModel.users {
   776  		if user.Name() == u.userName && u.access == permission.AdminAccess {
   777  			nextErr := st.NextErr()
   778  			if user.Name() != "admin" {
   779  				panic(user.Name())
   780  			}
   781  			return true, nextErr
   782  		}
   783  	}
   784  	return false, st.NextErr()
   785  }
   786  
   787  func (st *mockState) GetCloudAccess(cloud string, user names.UserTag) (permission.Access, error) {
   788  	st.MethodCall(st, "GetCloudAccess", user)
   789  	if perm, ok := st.cloudUsers[user.Id()]; ok {
   790  		return perm, nil
   791  	}
   792  	return permission.NoAccess, errors.NotFoundf("user %v", user.Id())
   793  }
   794  
   795  func (st *mockState) NewModel(args state.ModelArgs) (common.Model, common.ModelManagerBackend, error) {
   796  	st.MethodCall(st, "NewModel", args)
   797  	st.model.tag = names.NewModelTag(args.Config.UUID())
   798  	return st.model, st, st.NextErr()
   799  }
   800  
   801  func (st *mockState) ControllerModel() (common.Model, error) {
   802  	st.MethodCall(st, "ControllerModel")
   803  	return st.controllerModel, st.NextErr()
   804  }
   805  
   806  func (st *mockState) ControllerTag() names.ControllerTag {
   807  	st.MethodCall(st, "ControllerTag")
   808  	return names.NewControllerTag(st.controllerUUID)
   809  }
   810  
   811  func (st *mockState) ComposeNewModelConfig(modelAttr map[string]interface{}, regionSpec *environscloudspec.CloudRegionSpec) (map[string]interface{}, error) {
   812  	st.MethodCall(st, "ComposeNewModelConfig")
   813  	attr := make(map[string]interface{})
   814  	for attrName, val := range modelAttr {
   815  		attr[attrName] = val
   816  	}
   817  	attr["something"] = "value"
   818  	return attr, st.NextErr()
   819  }
   820  
   821  func (st *mockState) ControllerUUID() string {
   822  	st.MethodCall(st, "ControllerUUID")
   823  	return st.controllerUUID
   824  }
   825  
   826  func (st *mockState) IsController() bool {
   827  	st.MethodCall(st, "IsController")
   828  	return st.controllerUUID == st.model.UUID()
   829  }
   830  
   831  func (st *mockState) ControllerConfig() (controller.Config, error) {
   832  	st.MethodCall(st, "ControllerConfig")
   833  	if st.controllerCfg != nil {
   834  		return *st.controllerCfg, st.NextErr()
   835  	}
   836  
   837  	return controller.Config{
   838  		controller.ControllerUUIDKey: "deadbeef-1bad-500d-9000-4b1d0d06f00d",
   839  	}, st.NextErr()
   840  }
   841  
   842  func (st *mockState) ControllerNodes() ([]common.ControllerNode, error) {
   843  	st.MethodCall(st, "ControllerNodes")
   844  	return st.controllerNodes, st.NextErr()
   845  }
   846  
   847  func (st *mockState) Model() (common.Model, error) {
   848  	st.MethodCall(st, "Model")
   849  	return st.model, st.NextErr()
   850  }
   851  
   852  func (st *mockState) ModelTag() names.ModelTag {
   853  	st.MethodCall(st, "ModelTag")
   854  	return st.model.ModelTag()
   855  }
   856  
   857  func (st *mockState) AllMachines() ([]common.Machine, error) {
   858  	st.MethodCall(st, "AllMachines")
   859  	return st.machines, st.NextErr()
   860  }
   861  
   862  func (st *mockState) Clouds() (map[names.CloudTag]cloud.Cloud, error) {
   863  	st.MethodCall(st, "Clouds")
   864  	return st.clouds, st.NextErr()
   865  }
   866  
   867  func (st *mockState) SetModelAgentVersion(newVersion version.Number, stream *string, ignoreAgentVersions bool) error {
   868  	return errors.NotImplementedf("SetModelAgentVersion")
   869  }
   870  
   871  func (st *mockState) AbortCurrentUpgrade() error {
   872  	return errors.NotImplementedf("AbortCurrentUpgrade")
   873  }
   874  
   875  func (st *mockState) Cloud(name string) (cloud.Cloud, error) {
   876  	st.MethodCall(st, "Cloud", name)
   877  	return st.cloud, st.NextErr()
   878  }
   879  
   880  func (st *mockState) CloudCredential(tag names.CloudCredentialTag) (state.Credential, error) {
   881  	st.MethodCall(st, "CloudCredential", tag)
   882  	return st.cred, st.NextErr()
   883  }
   884  
   885  func (st *mockState) Close() error {
   886  	st.MethodCall(st, "Close")
   887  	return st.NextErr()
   888  }
   889  
   890  func (st *mockState) AddControllerUser(spec state.UserAccessSpec) (permission.UserAccess, error) {
   891  	st.MethodCall(st, "AddControllerUser", spec)
   892  	return permission.UserAccess{}, st.NextErr()
   893  }
   894  
   895  func (st *mockState) UserAccess(tag names.UserTag, target names.Tag) (permission.UserAccess, error) {
   896  	st.MethodCall(st, "ModelUser", tag, target)
   897  	for _, user := range st.users {
   898  		if user.UserTag != tag {
   899  			continue
   900  		}
   901  		nextErr := st.NextErr()
   902  		if nextErr != nil {
   903  			return permission.UserAccess{}, nextErr
   904  		}
   905  		return user, nil
   906  	}
   907  	return permission.UserAccess{}, st.NextErr()
   908  }
   909  
   910  func (st *mockState) ModelSummariesForUser(user names.UserTag, isSuperuser bool) ([]state.ModelSummary, error) {
   911  	st.MethodCall(st, "ModelSummariesForUser", user, isSuperuser)
   912  	return st.modelDetailsForUser()
   913  }
   914  
   915  func (st *mockState) ModelBasicInfoForUser(user names.UserTag, isSuperuser bool) ([]state.ModelAccessInfo, error) {
   916  	st.MethodCall(st, "ModelBasicInfoForUser", user, isSuperuser)
   917  	return []state.ModelAccessInfo{}, st.NextErr()
   918  }
   919  
   920  func (st *mockState) RemoveUserAccess(subject names.UserTag, target names.Tag) error {
   921  	st.MethodCall(st, "RemoveUserAccess", subject, target)
   922  	return st.NextErr()
   923  }
   924  
   925  func (st *mockState) SetUserAccess(subject names.UserTag, target names.Tag, access permission.Access) (permission.UserAccess, error) {
   926  	st.MethodCall(st, "SetUserAccess", subject, target, access)
   927  	return permission.UserAccess{}, st.NextErr()
   928  }
   929  
   930  func (st *mockState) ModelConfigDefaultValues(cloud string) (config.ModelDefaultAttributes, error) {
   931  	st.MethodCall(st, "ModelConfigDefaultValues", cloud)
   932  	return st.cfgDefaults, nil
   933  }
   934  
   935  func (st *mockState) UpdateModelConfigDefaultValues(update map[string]interface{}, remove []string, rspec *environscloudspec.CloudRegionSpec) error {
   936  	st.MethodCall(st, "UpdateModelConfigDefaultValues", update, remove, rspec)
   937  	for k, v := range update {
   938  		if rspec != nil {
   939  			adv := st.cfgDefaults[k]
   940  			adv.Regions = append(adv.Regions, config.RegionDefaultValue{
   941  				Name:  rspec.Region,
   942  				Value: v})
   943  
   944  		} else {
   945  			st.cfgDefaults[k] = config.AttributeDefaultValues{Controller: v}
   946  		}
   947  	}
   948  	for _, n := range remove {
   949  		if rspec != nil {
   950  			for i, r := range st.cfgDefaults[n].Regions {
   951  				if r.Name == rspec.Region {
   952  					adv := st.cfgDefaults[n]
   953  					adv.Regions = append(adv.Regions[:i], adv.Regions[i+1:]...)
   954  					st.cfgDefaults[n] = adv
   955  				}
   956  			}
   957  		} else {
   958  			if len(st.cfgDefaults[n].Regions) == 0 {
   959  				delete(st.cfgDefaults, n)
   960  			} else {
   961  
   962  				st.cfgDefaults[n] = config.AttributeDefaultValues{
   963  					Regions: st.cfgDefaults[n].Regions}
   964  			}
   965  		}
   966  	}
   967  	return nil
   968  }
   969  
   970  func (st *mockState) GetBlockForType(t state.BlockType) (state.Block, bool, error) {
   971  	st.MethodCall(st, "GetBlockForType", t)
   972  	if st.block == t {
   973  		return &mockBlock{t: t, m: st.blockMsg}, true, nil
   974  	} else {
   975  		return nil, false, nil
   976  	}
   977  }
   978  
   979  func (st *mockState) SaveProviderSubnets(subnets []network.SubnetInfo, spaceID string) error {
   980  	st.MethodCall(st, "SaveProviderSubnets", subnets, spaceID)
   981  	return st.NextErr()
   982  }
   983  
   984  func (st *mockState) DumpAll() (map[string]interface{}, error) {
   985  	st.MethodCall(st, "DumpAll")
   986  	return map[string]interface{}{
   987  		"models": "lots of data",
   988  	}, st.NextErr()
   989  }
   990  
   991  func (st *mockState) LatestMigration() (state.ModelMigration, error) {
   992  	st.MethodCall(st, "LatestMigration")
   993  	if st.migration == nil {
   994  		// Handle nil->notfound directly here rather than having to
   995  		// count errors.
   996  		return nil, errors.NotFoundf("")
   997  	}
   998  	return st.migration, st.NextErr()
   999  }
  1000  
  1001  func (st *mockState) SetModelMeterStatus(level, message string) error {
  1002  	st.MethodCall(st, "SetModelMeterStatus", level, message)
  1003  	return st.NextErr()
  1004  }
  1005  
  1006  func (st *mockState) ModelConfig() (*config.Config, error) {
  1007  	st.MethodCall(st, "ModelConfig")
  1008  	return st.modelConfig, st.NextErr()
  1009  }
  1010  
  1011  func (st *mockState) MetricsManager() (*state.MetricsManager, error) {
  1012  	st.MethodCall(st, "MetricsManager")
  1013  	return nil, errors.New("nope")
  1014  }
  1015  
  1016  func (st *mockState) HAPrimaryMachine() (names.MachineTag, error) {
  1017  	st.MethodCall(st, "HAPrimaryMachine")
  1018  	return names.MachineTag{}, nil
  1019  }
  1020  
  1021  func (st *mockState) AddSpace(name string, provider network.Id, subnetIds []string, public bool) (*state.Space, error) {
  1022  	st.MethodCall(st, "AddSpace", name, provider, subnetIds, public)
  1023  	return nil, st.NextErr()
  1024  }
  1025  
  1026  func (st *mockState) AllEndpointBindingsSpaceNames() (set.Strings, error) {
  1027  	st.MethodCall(st, "AllEndpointBindingsSpaceNames")
  1028  	return set.NewStrings(), nil
  1029  }
  1030  
  1031  func (st *mockState) DefaultEndpointBindingSpace() (string, error) {
  1032  	st.MethodCall(st, "DefaultEndpointBindingSpace")
  1033  	return "alpha", nil
  1034  }
  1035  
  1036  func (st *mockState) AllSpaces() ([]*state.Space, error) {
  1037  	st.MethodCall(st, "AllSpaces")
  1038  	return nil, st.NextErr()
  1039  }
  1040  
  1041  func (st *mockState) ConstraintsBySpaceName(spaceName string) ([]*state.Constraints, error) {
  1042  	st.MethodCall(st, "ConstraintsBySpaceName", spaceName)
  1043  	return nil, st.NextErr()
  1044  }
  1045  
  1046  func (st *mockState) ListModelSecrets(all bool) (map[string]set.Strings, error) {
  1047  	return map[string]set.Strings{
  1048  		"backend-id": set.NewStrings("a", "b"),
  1049  	}, nil
  1050  }
  1051  
  1052  func (st *mockState) GetSecretBackendByID(id string) (*secrets.SecretBackend, error) {
  1053  	if id != "backend-id" {
  1054  		return nil, errors.NotFoundf("backend %q", id)
  1055  	}
  1056  	return &secrets.SecretBackend{
  1057  		ID:          "backend-id",
  1058  		Name:        "myvault",
  1059  		BackendType: "vault",
  1060  		Config: map[string]interface{}{
  1061  			"endpoint": "http://vault",
  1062  			"token":    "secret",
  1063  		},
  1064  	}, nil
  1065  }
  1066  
  1067  func (st *mockState) ListSecretBackends() ([]*secrets.SecretBackend, error) {
  1068  	return []*secrets.SecretBackend{{
  1069  		ID:          "backend-id",
  1070  		Name:        "myvault",
  1071  		BackendType: "vault",
  1072  		Config: map[string]interface{}{
  1073  			"endpoint": "http://vault",
  1074  			"token":    "secret",
  1075  		},
  1076  	}}, nil
  1077  }
  1078  
  1079  type mockSecretProvider struct {
  1080  	provider.ProviderConfig
  1081  	provider.SecretBackendProvider
  1082  }
  1083  
  1084  func (mockSecretProvider) Type() string {
  1085  	return "vault"
  1086  }
  1087  
  1088  func (mockSecretProvider) NewBackend(cfg *provider.ModelBackendConfig) (provider.SecretsBackend, error) {
  1089  	return mockSecretBackend{}, nil
  1090  }
  1091  
  1092  func (mockSecretProvider) ConfigSchema() environschema.Fields {
  1093  	return environschema.Fields{
  1094  		"token": {
  1095  			Secret: true,
  1096  		},
  1097  	}
  1098  }
  1099  
  1100  type mockSecretBackend struct {
  1101  	provider.SecretsBackend
  1102  }
  1103  
  1104  func (mockSecretBackend) Ping() error {
  1105  	return nil
  1106  }
  1107  
  1108  type mockBlock struct {
  1109  	state.Block
  1110  	t state.BlockType
  1111  	m string
  1112  }
  1113  
  1114  func (m mockBlock) Id() string { return "" }
  1115  
  1116  func (m mockBlock) Tag() (names.Tag, error) { return names.NewModelTag("mocktesting"), nil }
  1117  
  1118  func (m mockBlock) Type() state.BlockType { return m.t }
  1119  
  1120  func (m mockBlock) Message() string { return m.m }
  1121  
  1122  func (m mockBlock) ModelUUID() string { return "" }
  1123  
  1124  type mockControllerNode struct {
  1125  	id        string
  1126  	hasVote   bool
  1127  	wantsVote bool
  1128  }
  1129  
  1130  func (m *mockControllerNode) Id() string {
  1131  	return m.id
  1132  }
  1133  
  1134  func (m *mockControllerNode) WantsVote() bool {
  1135  	return m.wantsVote
  1136  }
  1137  
  1138  func (m *mockControllerNode) HasVote() bool {
  1139  	return m.hasVote
  1140  }
  1141  
  1142  type mockMachine struct {
  1143  	common.Machine
  1144  	id            string
  1145  	life          state.Life
  1146  	containerType instance.ContainerType
  1147  	hw            *instance.HardwareCharacteristics
  1148  }
  1149  
  1150  func (m *mockMachine) Id() string {
  1151  	return m.id
  1152  }
  1153  
  1154  func (m *mockMachine) Life() state.Life {
  1155  	return m.life
  1156  }
  1157  
  1158  func (m *mockMachine) ContainerType() instance.ContainerType {
  1159  	return m.containerType
  1160  }
  1161  
  1162  func (m *mockMachine) HardwareCharacteristics() (*instance.HardwareCharacteristics, error) {
  1163  	return m.hw, nil
  1164  }
  1165  
  1166  func (m *mockMachine) InstanceId() (instance.Id, error) {
  1167  	return "", nil
  1168  }
  1169  
  1170  func (m *mockMachine) InstanceNames() (instance.Id, string, error) {
  1171  	return "", "", nil
  1172  }
  1173  
  1174  func (m *mockMachine) HasVote() bool {
  1175  	return false
  1176  }
  1177  
  1178  func (m *mockMachine) Status() (status.StatusInfo, error) {
  1179  	return status.StatusInfo{}, nil
  1180  }
  1181  
  1182  type mockModel struct {
  1183  	jujutesting.Stub
  1184  	owner               names.UserTag
  1185  	life                state.Life
  1186  	tag                 names.ModelTag
  1187  	status              status.StatusInfo
  1188  	cfg                 *config.Config
  1189  	users               []*mockModelUser
  1190  	migrationStatus     state.MigrationMode
  1191  	controllerUUID      string
  1192  	isController        bool
  1193  	cloud               cloud.Cloud
  1194  	cred                state.Credential
  1195  	setCloudCredentialF func(tag names.CloudCredentialTag) (bool, error)
  1196  }
  1197  
  1198  func (m *mockModel) Config() (*config.Config, error) {
  1199  	m.MethodCall(m, "Config")
  1200  	return m.cfg, m.NextErr()
  1201  }
  1202  
  1203  func (m *mockModel) Owner() names.UserTag {
  1204  	m.MethodCall(m, "Owner")
  1205  	return m.owner
  1206  }
  1207  
  1208  func (m *mockModel) ModelTag() names.ModelTag {
  1209  	m.MethodCall(m, "ModelTag")
  1210  	return m.tag
  1211  }
  1212  
  1213  func (m *mockModel) Type() state.ModelType {
  1214  	m.MethodCall(m, "Type")
  1215  	return state.ModelTypeIAAS
  1216  }
  1217  
  1218  func (m *mockModel) Life() state.Life {
  1219  	m.MethodCall(m, "Life")
  1220  	return m.life
  1221  }
  1222  
  1223  func (m *mockModel) Status() (status.StatusInfo, error) {
  1224  	m.MethodCall(m, "Status")
  1225  	return m.status, m.NextErr()
  1226  }
  1227  
  1228  func (m *mockModel) CloudName() string {
  1229  	m.MethodCall(m, "CloudName")
  1230  	return "some-cloud"
  1231  }
  1232  
  1233  func (m *mockModel) Cloud() (cloud.Cloud, error) {
  1234  	m.MethodCall(m, "CloudValue")
  1235  	return m.cloud, nil
  1236  }
  1237  
  1238  func (m *mockModel) CloudRegion() string {
  1239  	m.MethodCall(m, "CloudRegion")
  1240  	return "some-region"
  1241  }
  1242  
  1243  func (m *mockModel) CloudCredentialTag() (names.CloudCredentialTag, bool) {
  1244  	m.MethodCall(m, "CloudCredentialTag")
  1245  	return names.NewCloudCredentialTag("some-cloud/bob/some-credential"), true
  1246  }
  1247  
  1248  func (m *mockModel) CloudCredential() (state.Credential, bool, error) {
  1249  	m.MethodCall(m, "CloudCredential")
  1250  	return m.cred, true, nil
  1251  }
  1252  
  1253  func (m *mockModel) Users() ([]permission.UserAccess, error) {
  1254  	m.MethodCall(m, "Users")
  1255  	if err := m.NextErr(); err != nil {
  1256  		return nil, err
  1257  	}
  1258  	users := make([]permission.UserAccess, len(m.users))
  1259  	for i, user := range m.users {
  1260  		users[i] = permission.UserAccess{
  1261  			UserID:      strings.ToLower(user.userName),
  1262  			UserTag:     names.NewUserTag(user.userName),
  1263  			Object:      m.ModelTag(),
  1264  			Access:      user.access,
  1265  			DisplayName: user.displayName,
  1266  			UserName:    user.userName,
  1267  		}
  1268  	}
  1269  	return users, nil
  1270  }
  1271  
  1272  func (m *mockModel) Destroy(args state.DestroyModelParams) error {
  1273  	m.MethodCall(m, "Destroy", args)
  1274  	return m.NextErr()
  1275  }
  1276  
  1277  func (m *mockModel) SLALevel() string {
  1278  	m.MethodCall(m, "SLALevel")
  1279  	return "essential"
  1280  }
  1281  
  1282  func (m *mockModel) SLAOwner() string {
  1283  	m.MethodCall(m, "SLAOwner")
  1284  	return "user"
  1285  }
  1286  
  1287  func (m *mockModel) ControllerUUID() string {
  1288  	m.MethodCall(m, "ControllerUUID")
  1289  	return m.controllerUUID
  1290  }
  1291  
  1292  func (m *mockModel) UUID() string {
  1293  	m.MethodCall(m, "UUID")
  1294  	return m.cfg.UUID()
  1295  }
  1296  
  1297  func (m *mockModel) Name() string {
  1298  	m.MethodCall(m, "Name")
  1299  	return m.cfg.Name()
  1300  }
  1301  
  1302  func (m *mockModel) MigrationMode() state.MigrationMode {
  1303  	m.MethodCall(m, "MigrationMode")
  1304  	return m.migrationStatus
  1305  }
  1306  
  1307  func (m *mockModel) AddUser(spec state.UserAccessSpec) (permission.UserAccess, error) {
  1308  	m.MethodCall(m, "AddUser", spec)
  1309  	return permission.UserAccess{}, m.NextErr()
  1310  }
  1311  func (m *mockModel) LastModelConnection(user names.UserTag) (time.Time, error) {
  1312  	m.MethodCall(m, "LastModelConnection", user)
  1313  	return time.Time{}, m.NextErr()
  1314  }
  1315  
  1316  func (m *mockModel) AutoConfigureContainerNetworking(environ environs.BootstrapEnviron) error {
  1317  	m.MethodCall(m, "AutoConfigureContainerNetworking", environ)
  1318  	return m.NextErr()
  1319  }
  1320  
  1321  func (m *mockModel) getModelDetails() state.ModelSummary {
  1322  	cred, _ := m.CloudCredentialTag()
  1323  	return state.ModelSummary{
  1324  		Name:               m.Name(),
  1325  		UUID:               m.UUID(),
  1326  		Type:               m.Type(),
  1327  		Life:               m.Life(),
  1328  		Owner:              m.Owner().Id(),
  1329  		ControllerUUID:     m.ControllerUUID(),
  1330  		SLALevel:           m.SLALevel(),
  1331  		SLAOwner:           m.SLAOwner(),
  1332  		DefaultSeries:      "jammy",
  1333  		DefaultBase:        base.MustParseBaseFromString("ubuntu@22.04"),
  1334  		CloudTag:           m.CloudName(),
  1335  		CloudRegion:        m.CloudRegion(),
  1336  		CloudCredentialTag: cred.String(),
  1337  	}
  1338  }
  1339  
  1340  func (m *mockModel) SetCloudCredential(tag names.CloudCredentialTag) (bool, error) {
  1341  	m.MethodCall(m, "SetCloudCredential", tag)
  1342  	return m.setCloudCredentialF(tag)
  1343  }
  1344  
  1345  type mockModelUser struct {
  1346  	jujutesting.Stub
  1347  	userName    string
  1348  	displayName string
  1349  	access      permission.Access
  1350  }
  1351  
  1352  type mockMigration struct {
  1353  	state.ModelMigration
  1354  
  1355  	status string
  1356  	start  time.Time
  1357  	end    time.Time
  1358  }
  1359  
  1360  func (m *mockMigration) StatusMessage() string {
  1361  	return m.status
  1362  }
  1363  
  1364  func (m *mockMigration) StartTime() time.Time {
  1365  	return m.start
  1366  }
  1367  
  1368  func (m *mockMigration) EndTime() time.Time {
  1369  	return m.end
  1370  }