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