github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/modelsummaries_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  	"time"
    10  
    11  	"github.com/juju/mgo/v3/bson"
    12  	"github.com/juju/names/v5"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils/v3"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/cloud"
    18  	"github.com/juju/juju/core/arch"
    19  	"github.com/juju/juju/core/instance"
    20  	"github.com/juju/juju/core/permission"
    21  	"github.com/juju/juju/core/status"
    22  	"github.com/juju/juju/state"
    23  	"github.com/juju/juju/storage"
    24  	"github.com/juju/juju/testing"
    25  	"github.com/juju/juju/testing/factory"
    26  )
    27  
    28  type ModelSummariesSuite struct {
    29  	ConnSuite
    30  }
    31  
    32  var _ = gc.Suite(&ModelSummariesSuite{})
    33  
    34  func (s *ModelSummariesSuite) Setup4Models(c *gc.C) map[string]string {
    35  	modelUUIDs := make(map[string]string)
    36  	user1 := s.Factory.MakeUser(c, &factory.UserParams{
    37  		Name:        "user1write",
    38  		NoModelUser: true,
    39  	})
    40  	st1 := s.Factory.MakeModel(c, &factory.ModelParams{
    41  		Name:  "user1model",
    42  		Owner: user1.Tag(),
    43  	})
    44  	modelUUIDs["user1model"] = st1.ModelUUID()
    45  	st1.Close()
    46  	user2 := s.Factory.MakeUser(c, &factory.UserParams{
    47  		Name:        "user2read",
    48  		NoModelUser: true,
    49  	})
    50  	st2 := s.Factory.MakeModel(c, &factory.ModelParams{
    51  		Name:  "user2model",
    52  		Owner: user2.Tag(),
    53  		Type:  state.ModelTypeCAAS,
    54  	})
    55  	modelUUIDs["user2model"] = st2.ModelUUID()
    56  	f2 := factory.NewFactory(st2, s.StatePool)
    57  	f2.MakeUnit(c, nil)
    58  	st2.Close()
    59  	user3 := s.Factory.MakeUser(c, &factory.UserParams{
    60  		Name:        "user3admin",
    61  		NoModelUser: true,
    62  	})
    63  	st3 := s.Factory.MakeModel(c, &factory.ModelParams{
    64  		Name:  "user3model",
    65  		Owner: user3.Tag(),
    66  	})
    67  	modelUUIDs["user3model"] = st3.ModelUUID()
    68  	st3.Close()
    69  	owner := s.Model.Owner()
    70  	err := s.State.AddCloud(cloud.Cloud{
    71  		Name:      "stratus",
    72  		Type:      "low",
    73  		AuthTypes: cloud.AuthTypes{cloud.AccessKeyAuthType, cloud.UserPassAuthType},
    74  		Regions: []cloud.Region{
    75  			{
    76  				Name:             "dummy-region",
    77  				Endpoint:         "dummy-endpoint",
    78  				IdentityEndpoint: "dummy-identity-endpoint",
    79  				StorageEndpoint:  "dummy-storage-endpoint",
    80  			},
    81  		},
    82  	}, s.Owner.Name())
    83  	c.Assert(err, jc.ErrorIsNil)
    84  
    85  	cred := cloud.NewCredential(cloud.AccessKeyAuthType, map[string]string{
    86  		"foo": "foo val",
    87  		"bar": "bar val",
    88  	})
    89  	tag := names.NewCloudCredentialTag(fmt.Sprintf("stratus/%v/foobar", owner.Name()))
    90  	err = s.State.UpdateCloudCredential(tag, cred)
    91  	c.Assert(err, jc.ErrorIsNil)
    92  
    93  	sharedSt := s.Factory.MakeModel(c, &factory.ModelParams{
    94  		Name: "shared",
    95  		// Owned by test-admin
    96  		Owner:           owner,
    97  		CloudName:       "stratus",
    98  		CloudCredential: tag,
    99  	})
   100  	modelUUIDs["shared"] = sharedSt.ModelUUID()
   101  	defer sharedSt.Close()
   102  	sharedModel, err := sharedSt.Model()
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	_, err = sharedModel.AddUser(state.UserAccessSpec{
   105  		User:      user1.UserTag(),
   106  		CreatedBy: owner,
   107  		Access:    "write",
   108  	})
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	// User 2 has read access to the shared model
   111  	_, err = sharedModel.AddUser(state.UserAccessSpec{
   112  		User:      user2.UserTag(),
   113  		CreatedBy: owner,
   114  		Access:    "read",
   115  	})
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	_, err = sharedModel.AddUser(state.UserAccessSpec{
   118  		User:      user3.UserTag(),
   119  		CreatedBy: owner,
   120  		Access:    "admin",
   121  	})
   122  	c.Assert(err, jc.ErrorIsNil)
   123  	return modelUUIDs
   124  }
   125  
   126  func (s *ModelSummariesSuite) modelNamesForUser(c *gc.C, user string, isSuperuser bool) []string {
   127  	tag := names.NewUserTag(user)
   128  	modelQuery, closer, err := s.State.ModelQueryForUser(tag, isSuperuser)
   129  	defer closer()
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	var docs []struct {
   132  		Name string `bson:"name"`
   133  	}
   134  	modelQuery.Select(bson.M{"name": 1})
   135  	err = modelQuery.All(&docs)
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	names := make([]string, 0)
   138  	for _, doc := range docs {
   139  		names = append(names, doc.Name)
   140  	}
   141  	sort.Strings(names)
   142  	return names
   143  }
   144  
   145  func (s *ModelSummariesSuite) TestModelsForUserAdmin(c *gc.C) {
   146  	s.Setup4Models(c)
   147  	names := s.modelNamesForUser(c, s.Model.Owner().Name(), true)
   148  	// Admin always gets to see all models
   149  	c.Check(names, gc.DeepEquals, []string{"shared", "testmodel", "user1model", "user2model", "user3model"})
   150  }
   151  
   152  func (s *ModelSummariesSuite) TestModelsForSuperuserWithoutAll(c *gc.C) {
   153  	s.Setup4Models(c)
   154  	summaries, err := s.State.ModelSummariesForUser(s.Model.Owner(), false)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	names := make([]string, len(summaries))
   157  	for i, summary := range summaries {
   158  		names[i] = summary.Name
   159  	}
   160  	sort.Strings(names)
   161  	c.Check(names, gc.DeepEquals, []string{"shared", "testmodel"})
   162  }
   163  
   164  func (s *ModelSummariesSuite) TestModelsForSuperuserWithAll(c *gc.C) {
   165  	s.Setup4Models(c)
   166  	summaries, err := s.State.ModelSummariesForUser(s.Model.Owner(), true)
   167  	c.Assert(err, jc.ErrorIsNil)
   168  	names := make([]string, len(summaries))
   169  	access := make(map[string]string)
   170  	isController := make(map[string]bool)
   171  	for i, summary := range summaries {
   172  		names[i] = summary.Name
   173  		access[summary.Name] = string(summary.Access)
   174  		isController[summary.Name] = summary.IsController
   175  	}
   176  	sort.Strings(names)
   177  	c.Check(names, gc.DeepEquals, []string{"shared", "testmodel", "user1model", "user2model", "user3model"})
   178  	c.Check(access, gc.DeepEquals, map[string]string{
   179  		"shared":     "admin",
   180  		"testmodel":  "admin",
   181  		"user1model": "",
   182  		"user2model": "",
   183  		"user3model": "",
   184  	})
   185  	c.Check(isController, gc.DeepEquals, map[string]bool{
   186  		"shared":     false,
   187  		"testmodel":  true,
   188  		"user1model": false,
   189  		"user2model": false,
   190  		"user3model": false,
   191  	})
   192  }
   193  
   194  func (s *ModelSummariesSuite) TestModelsForUser1(c *gc.C) {
   195  	// User1 is only added to the model they own and the shared model as write
   196  	s.Setup4Models(c)
   197  	names := s.modelNamesForUser(c, "user1write", false)
   198  	c.Check(names, gc.DeepEquals, []string{"shared", "user1model"})
   199  }
   200  
   201  func (s *ModelSummariesSuite) TestModelsForUser2(c *gc.C) {
   202  	// User2 is only added to the model they own and the shared model as read
   203  	s.Setup4Models(c)
   204  	names := s.modelNamesForUser(c, "user2read", false)
   205  	c.Check(names, gc.DeepEquals, []string{"shared", "user2model"})
   206  }
   207  
   208  func (s *ModelSummariesSuite) TestModelsForUser3(c *gc.C) {
   209  	// User2 is only added to the model they own and the shared model as admin
   210  	s.Setup4Models(c)
   211  	names := s.modelNamesForUser(c, "user3admin", false)
   212  	c.Check(names, gc.DeepEquals, []string{"shared", "user3model"})
   213  }
   214  
   215  // NOTE: (jam 2017-12-11) We probably only ever stripped Importing models because there details might not be complete.
   216  // We probably actually want to include importing models, and just handle when they don't have complete data.
   217  func (s *ModelSummariesSuite) TestModelsForIgnoresImportingModels(c *gc.C) {
   218  	s.Setup4Models(c)
   219  	cfg := testing.CustomModelConfig(c, testing.Attrs{
   220  		"name": "importing",
   221  		"uuid": utils.MustNewUUID().String(),
   222  		"type": state.ModelTypeIAAS,
   223  	})
   224  	_, stImporting, err := s.Controller.NewModel(state.ModelArgs{
   225  		Type:                    state.ModelTypeIAAS,
   226  		CloudName:               "dummy",
   227  		CloudRegion:             "dummy-region",
   228  		Config:                  cfg,
   229  		Owner:                   names.NewUserTag("user1write"),
   230  		MigrationMode:           state.MigrationModeImporting,
   231  		EnvironVersion:          s.Model.EnvironVersion(),
   232  		StorageProviderRegistry: storage.StaticProviderRegistry{},
   233  	})
   234  	defer stImporting.Close()
   235  	c.Assert(err, jc.ErrorIsNil)
   236  
   237  	// Since the new model is importing, when we do the list we shouldn't see it.
   238  	names := s.modelNamesForUser(c, "user3admin", false)
   239  	c.Check(names, gc.DeepEquals, []string{"shared", "user3model"})
   240  	// Superuser doesn't see importing models, either
   241  	names = s.modelNamesForUser(c, s.Model.Owner().Name(), true)
   242  	c.Check(names, gc.DeepEquals, []string{"shared", "testmodel", "user1model", "user2model", "user3model"})
   243  }
   244  
   245  func (s *ModelSummariesSuite) TestContainsConfigInformation(c *gc.C) {
   246  	s.Setup4Models(c)
   247  	summaries, err := s.State.ModelSummariesForUser(names.NewUserTag("user1write"), false)
   248  	c.Assert(err, jc.ErrorIsNil)
   249  	c.Check(summaries, gc.HasLen, 2)
   250  	// We don't guarantee the order of the summaries, but the data for each model should match the same
   251  	// information you would get if you instantiate the model directly
   252  	summaryA := summaries[0]
   253  	model, ph, err := s.StatePool.GetModel(summaryA.UUID)
   254  	defer ph.Release()
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	conf, err := model.Config()
   257  	c.Assert(err, jc.ErrorIsNil)
   258  	c.Check(summaryA.ProviderType, gc.Equals, conf.Type())
   259  	version, ok := conf.AgentVersion()
   260  	c.Assert(ok, jc.IsTrue)
   261  	c.Check(summaryA.AgentVersion, gc.NotNil)
   262  	c.Check(*summaryA.AgentVersion, gc.Equals, version)
   263  }
   264  
   265  func (s *ModelSummariesSuite) TestContainsProviderType(c *gc.C) {
   266  	s.Setup4Models(c)
   267  	summaries, err := s.State.ModelSummariesForUser(names.NewUserTag("user1write"), false)
   268  	c.Assert(err, jc.ErrorIsNil)
   269  	c.Check(summaries, gc.HasLen, 2)
   270  	// We don't guarantee the order of the summaries, but both should have the same ProviderType
   271  	summaryA := summaries[0]
   272  	model, ph, err := s.StatePool.GetModel(summaryA.UUID)
   273  	defer ph.Release()
   274  	c.Assert(err, jc.ErrorIsNil)
   275  	conf, err := model.Config()
   276  	c.Assert(err, jc.ErrorIsNil)
   277  	c.Check(summaryA.ProviderType, gc.Equals, conf.Type())
   278  }
   279  
   280  func (s *ModelSummariesSuite) TestContainsModelStatus(c *gc.C) {
   281  	modelNameToUUID := s.Setup4Models(c)
   282  	expectedStatus := map[string]status.StatusInfo{
   283  		"shared": {
   284  			Status:  status.Available,
   285  			Message: "human message",
   286  		},
   287  		"user1model": {
   288  			Status:  status.Busy,
   289  			Message: "human message",
   290  		},
   291  	}
   292  	shared, ph, err := s.StatePool.GetModel(modelNameToUUID["shared"])
   293  	defer ph.Release()
   294  	c.Assert(err, jc.ErrorIsNil)
   295  	err = shared.SetStatus(expectedStatus["shared"])
   296  	c.Assert(err, jc.ErrorIsNil)
   297  	user1, ph, err := s.StatePool.GetModel(modelNameToUUID["user1model"])
   298  	defer ph.Release()
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	err = user1.SetStatus(expectedStatus["user1model"])
   301  	c.Assert(err, jc.ErrorIsNil)
   302  	summaries, err := s.State.ModelSummariesForUser(names.NewUserTag("user1write"), false)
   303  	c.Assert(err, jc.ErrorIsNil)
   304  	c.Check(summaries, gc.HasLen, 2)
   305  	statuses := make(map[string]status.StatusInfo)
   306  	for _, summary := range summaries {
   307  		// We nil the time, because we don't want to compare it, we nil the Data map to avoid comparing an
   308  		// empty map to a nil map
   309  		st := summary.Status
   310  		st.Since = nil
   311  		st.Data = nil
   312  		statuses[summary.Name] = st
   313  	}
   314  	c.Check(statuses, jc.DeepEquals, expectedStatus)
   315  }
   316  
   317  func (s *ModelSummariesSuite) TestContainsModelStatusSuspended(c *gc.C) {
   318  	modelNameToUUID := s.Setup4Models(c)
   319  	expectedStatus := map[string]status.StatusInfo{
   320  		"shared": {
   321  			Status:  status.Suspended,
   322  			Message: "suspended since cloud credential is not valid",
   323  			Data:    map[string]interface{}{"reason": "test"},
   324  		},
   325  		"user1model": {
   326  			Status:  status.Busy,
   327  			Message: "human message",
   328  			Data:    map[string]interface{}{},
   329  		},
   330  	}
   331  	shared, err := s.StatePool.Get(modelNameToUUID["shared"])
   332  	defer shared.Release()
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	c.Assert(shared.InvalidateModelCredential("test"), jc.ErrorIsNil)
   335  
   336  	user1, ph, err := s.StatePool.GetModel(modelNameToUUID["user1model"])
   337  	defer ph.Release()
   338  	c.Assert(err, jc.ErrorIsNil)
   339  	err = user1.SetStatus(expectedStatus["user1model"])
   340  	c.Assert(err, jc.ErrorIsNil)
   341  	summaries, err := s.State.ModelSummariesForUser(names.NewUserTag("user1write"), false)
   342  	c.Assert(err, jc.ErrorIsNil)
   343  	c.Check(summaries, gc.HasLen, 2)
   344  	statuses := make(map[string]status.StatusInfo)
   345  	for _, summary := range summaries {
   346  		// We nil the time, because we don't want to compare it, we nil the Data map to avoid comparing an
   347  		// empty map to a nil map
   348  		st := summary.Status
   349  		st.Since = nil
   350  		statuses[summary.Name] = st
   351  	}
   352  	c.Check(statuses, jc.DeepEquals, expectedStatus)
   353  }
   354  
   355  func (s *ModelSummariesSuite) TestContainsAccessInformation(c *gc.C) {
   356  	modelNameToUUID := s.Setup4Models(c)
   357  	shared, ph, err := s.StatePool.GetModel(modelNameToUUID["shared"])
   358  	defer ph.Release()
   359  	c.Assert(err, jc.ErrorIsNil)
   360  	err = shared.UpdateLastModelConnection(names.NewUserTag("auser"))
   361  	s.Clock.Advance(time.Hour)
   362  	c.Assert(err, jc.ErrorIsNil)
   363  	timeShared := s.Clock.Now().Round(time.Second).UTC()
   364  	err = shared.UpdateLastModelConnection(names.NewUserTag("user1write"))
   365  	c.Assert(err, jc.ErrorIsNil)
   366  	s.Clock.Advance(time.Hour) // give a different time for user2 accessing the shared model
   367  	err = shared.UpdateLastModelConnection(names.NewUserTag("user2read"))
   368  	c.Assert(err, jc.ErrorIsNil)
   369  	user1, ph, err := s.StatePool.GetModel(modelNameToUUID["user1model"])
   370  	defer ph.Release()
   371  	c.Assert(err, jc.ErrorIsNil)
   372  	s.Clock.Advance(time.Hour)
   373  	timeUser1 := s.Clock.Now().Round(time.Second).UTC()
   374  	err = user1.UpdateLastModelConnection(names.NewUserTag("user1write"))
   375  	c.Assert(err, jc.ErrorIsNil)
   376  
   377  	summaries, err := s.State.ModelSummariesForUser(names.NewUserTag("user1write"), false)
   378  	c.Assert(err, jc.ErrorIsNil)
   379  	c.Check(summaries, gc.HasLen, 2)
   380  	times := make(map[string]time.Time)
   381  	access := make(map[string]permission.Access)
   382  	for _, summary := range summaries {
   383  		c.Assert(summary.UserLastConnection, gc.NotNil, gc.Commentf("nil time for %v", summary.Name))
   384  		times[summary.Name] = summary.UserLastConnection.UTC()
   385  		access[summary.Name] = summary.Access
   386  	}
   387  	c.Check(times, gc.DeepEquals, map[string]time.Time{
   388  		"shared":     timeShared,
   389  		"user1model": timeUser1,
   390  	})
   391  	c.Check(access, gc.DeepEquals, map[string]permission.Access{
   392  		"shared":     permission.WriteAccess,
   393  		"user1model": permission.AdminAccess,
   394  	})
   395  }
   396  
   397  func (s *ModelSummariesSuite) TestContainsMachineInformation(c *gc.C) {
   398  	modelNameToUUID := s.Setup4Models(c)
   399  	shared, err := s.StatePool.Get(modelNameToUUID["shared"])
   400  	defer shared.Release()
   401  	c.Assert(err, jc.ErrorIsNil)
   402  	onecore := uint64(1)
   403  	twocores := uint64(2)
   404  	threecores := uint64(3)
   405  	m0, err := shared.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   406  	c.Assert(err, jc.ErrorIsNil)
   407  	c.Assert(m0.Life(), gc.Equals, state.Alive)
   408  	err = m0.SetInstanceInfo("i-12345", "", "nonce", &instance.HardwareCharacteristics{
   409  		CpuCores: &onecore,
   410  	}, nil, nil, nil, nil, nil)
   411  	c.Assert(err, jc.ErrorIsNil)
   412  	m1, err := shared.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   413  	c.Assert(err, jc.ErrorIsNil)
   414  	err = m1.SetInstanceInfo("i-45678", "", "nonce", &instance.HardwareCharacteristics{
   415  		CpuCores: &twocores,
   416  	}, nil, nil, nil, nil, nil)
   417  	c.Assert(err, jc.ErrorIsNil)
   418  	m2, err := shared.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   419  	c.Assert(err, jc.ErrorIsNil)
   420  	err = m2.SetInstanceInfo("i-78901", "", "nonce", &instance.HardwareCharacteristics{
   421  		CpuCores: &threecores,
   422  	}, nil, nil, nil, nil, nil)
   423  	c.Assert(err, jc.ErrorIsNil)
   424  	// No instance
   425  	_, err = shared.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   426  	c.Assert(err, jc.ErrorIsNil)
   427  	// Dying instance, should not count to Cores or Machine count
   428  	mDying, err := shared.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   429  	c.Assert(err, jc.ErrorIsNil)
   430  	err = mDying.SetInstanceInfo("i-78901", "", "nonce", &instance.HardwareCharacteristics{
   431  		CpuCores: &threecores,
   432  	}, nil, nil, nil, nil, nil)
   433  	c.Assert(err, jc.ErrorIsNil)
   434  	err = mDying.Destroy()
   435  	c.Assert(err, jc.ErrorIsNil)
   436  	// Instance data, but no core count
   437  	m4, err := shared.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   438  	c.Assert(err, jc.ErrorIsNil)
   439  	arch := arch.DefaultArchitecture
   440  	err = m4.SetInstanceInfo("i-78901", "", "nonce", &instance.HardwareCharacteristics{
   441  		Arch: &arch,
   442  	}, nil, nil, nil, nil, nil)
   443  	c.Assert(err, jc.ErrorIsNil)
   444  
   445  	summaries, err := s.State.ModelSummariesForUser(names.NewUserTag("user1write"), false)
   446  	c.Assert(err, jc.ErrorIsNil)
   447  	c.Check(summaries, gc.HasLen, 2)
   448  	summaryMap := make(map[string]*state.ModelSummary)
   449  	for i := range summaries {
   450  		summaryMap[summaries[i].Name] = &summaries[i]
   451  	}
   452  	sharedSummary := summaryMap["shared"]
   453  	c.Assert(sharedSummary, gc.NotNil)
   454  	c.Check(sharedSummary.MachineCount, gc.Equals, int64(5))
   455  	c.Check(sharedSummary.CoreCount, gc.Equals, int64(1+2+3))
   456  	userSummary := summaryMap["user1model"]
   457  	c.Assert(userSummary, gc.NotNil)
   458  	c.Check(userSummary.MachineCount, gc.Equals, int64(0))
   459  	c.Check(userSummary.CoreCount, gc.Equals, int64(0))
   460  }
   461  
   462  func (s *ModelSummariesSuite) TestContainsMigrationInformation(c *gc.C) {
   463  	//modelNameToUUID := s.Setup4Models(c)
   464  	// TODO: Figure out how to create a multiple-attempt migration information, and assert that we expose the right info
   465  }
   466  
   467  func (s *ModelSummariesSuite) namedSummariesForUser(c *gc.C, user string) map[string]*state.ModelSummary {
   468  	summaries, err := s.State.ModelSummariesForUser(names.NewUserTag(user), false)
   469  	c.Assert(err, jc.ErrorIsNil)
   470  	summaryMap := make(map[string]*state.ModelSummary, len(summaries))
   471  	for i := range summaries {
   472  		summaryMap[summaries[i].Name] = &summaries[i]
   473  	}
   474  	return summaryMap
   475  }
   476  
   477  func (s *ModelSummariesSuite) TestModelsWithNoSettings(c *gc.C) {
   478  	modelNameToUUID := s.Setup4Models(c)
   479  	m2uuid := modelNameToUUID["user2model"]
   480  	// Mark the model as dying, and move to start tearing it down
   481  	model, ph, err := s.StatePool.GetModel(m2uuid)
   482  	c.Assert(err, jc.ErrorIsNil)
   483  	defer ph.Release()
   484  	err = model.SetStatus(status.StatusInfo{
   485  		Status:  status.Available,
   486  		Message: "running",
   487  	})
   488  	c.Assert(err, jc.ErrorIsNil)
   489  
   490  	summaryMap := s.namedSummariesForUser(c, "user2read")
   491  	// Even though user2model is dying/dead, it should still be in the output.
   492  	c.Check(summaryMap, gc.HasLen, 2)
   493  	userSummary := summaryMap["user2model"]
   494  	c.Assert(userSummary, gc.NotNil)
   495  	c.Check(userSummary.Status.Message, gc.Equals, "running")
   496  
   497  	err = model.Destroy(state.DestroyModelParams{})
   498  	c.Assert(err, jc.ErrorIsNil)
   499  	err = model.SetStatus(status.StatusInfo{
   500  		Status:  status.Destroying,
   501  		Message: "stopping",
   502  	})
   503  	c.Assert(err, jc.ErrorIsNil)
   504  
   505  	summaryMap = s.namedSummariesForUser(c, "user2read")
   506  	// Even though user2model is dying/dead, it should still be in the output.
   507  	c.Check(summaryMap, gc.HasLen, 2)
   508  	userSummary = summaryMap["user2model"]
   509  	c.Assert(userSummary, gc.NotNil)
   510  	c.Check(userSummary.Status.Message, gc.Equals, "stopping")
   511  
   512  	// Now we start tearing down some of the collections for this model, and see that it still shows up.
   513  	settings := s.Session.DB("juju").C("settings")
   514  	// The settings document for this model
   515  	err = settings.Remove(bson.M{"_id": m2uuid + ":e"})
   516  	c.Assert(err, jc.ErrorIsNil)
   517  	summaryMap = s.namedSummariesForUser(c, "user2read")
   518  	c.Assert(err, jc.ErrorIsNil)
   519  	// Even though user2model is dying/dead, it should still be in the output.
   520  	c.Check(summaryMap, gc.HasLen, 2)
   521  	userSummary = summaryMap["user2model"]
   522  	c.Assert(userSummary, gc.NotNil)
   523  	c.Check(userSummary.Status.Message, gc.Equals, "stopping")
   524  }
   525  
   526  func (s *ModelSummariesSuite) TestCAASModel(c *gc.C) {
   527  	s.Setup4Models(c)
   528  
   529  	summaryMap := s.namedSummariesForUser(c, "user2read")
   530  	c.Check(summaryMap, gc.HasLen, 2)
   531  	userSummary := summaryMap["user2model"]
   532  	c.Assert(userSummary, gc.NotNil)
   533  	c.Assert(userSummary.MachineCount, gc.Equals, int64(0))
   534  	c.Assert(userSummary.CoreCount, gc.Equals, int64(0))
   535  	c.Assert(userSummary.UnitCount, gc.Equals, int64(1))
   536  }