github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/model.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	jujutxn "github.com/juju/txn"
    11  	"github.com/juju/version"
    12  	"gopkg.in/juju/names.v2"
    13  	"gopkg.in/mgo.v2"
    14  	"gopkg.in/mgo.v2/bson"
    15  	"gopkg.in/mgo.v2/txn"
    16  
    17  	jujucloud "github.com/juju/juju/cloud"
    18  	"github.com/juju/juju/constraints"
    19  	"github.com/juju/juju/environs/config"
    20  	"github.com/juju/juju/mongo"
    21  	"github.com/juju/juju/permission"
    22  	"github.com/juju/juju/status"
    23  	"github.com/juju/juju/storage"
    24  )
    25  
    26  // modelGlobalKey is the key for the model, its
    27  // settings and constraints.
    28  const modelGlobalKey = "e"
    29  
    30  // modelKey will create the kei for a given model using the modelGlobalKey.
    31  func modelKey(modelUUID string) string {
    32  	return fmt.Sprintf("%s#%s", modelGlobalKey, modelUUID)
    33  }
    34  
    35  // MigrationMode specifies where the Model is with respect to migration.
    36  type MigrationMode string
    37  
    38  const (
    39  	// MigrationModeNone is the default mode for a model and reflects
    40  	// that it isn't involved with a model migration.
    41  	MigrationModeNone MigrationMode = ""
    42  
    43  	// MigrationModeExporting reflects a model that is in the process of being
    44  	// exported from one controller to another.
    45  	MigrationModeExporting MigrationMode = "exporting"
    46  
    47  	// MigrationModeImporting reflects a model that is being imported into a
    48  	// controller, but is not yet fully active.
    49  	MigrationModeImporting MigrationMode = "importing"
    50  )
    51  
    52  // Model represents the state of a model.
    53  type Model struct {
    54  	// st is not necessarily the state of this model. Though it is
    55  	// usually safe to assume that it is. The only times it isn't is when we
    56  	// get models other than the current one - which is mostly in
    57  	// controller api endpoints.
    58  	st  *State
    59  	doc modelDoc
    60  }
    61  
    62  // modelDoc represents the internal state of the model in MongoDB.
    63  type modelDoc struct {
    64  	UUID           string `bson:"_id"`
    65  	Name           string
    66  	Life           Life
    67  	Owner          string        `bson:"owner"`
    68  	ControllerUUID string        `bson:"controller-uuid"`
    69  	MigrationMode  MigrationMode `bson:"migration-mode"`
    70  
    71  	// Cloud is the name of the cloud to which the model is deployed.
    72  	Cloud string `bson:"cloud"`
    73  
    74  	// CloudRegion is the name of the cloud region to which the model is
    75  	// deployed. This will be empty for clouds that do not support regions.
    76  	CloudRegion string `bson:"cloud-region,omitempty"`
    77  
    78  	// CloudCredential is the ID of the cloud credential that is used
    79  	// for managing cloud resources for this model. This will be empty
    80  	// for clouds that do not require credentials.
    81  	CloudCredential string `bson:"cloud-credential,omitempty"`
    82  
    83  	// LatestAvailableTools is a string representing the newest version
    84  	// found while checking streams for new versions.
    85  	LatestAvailableTools string `bson:"available-tools,omitempty"`
    86  }
    87  
    88  // modelEntityRefsDoc records references to the top-level entities
    89  // in the model.
    90  type modelEntityRefsDoc struct {
    91  	UUID string `bson:"_id"`
    92  
    93  	// Machines contains the names of the top-level machines in the model.
    94  	Machines []string `bson:"machines"`
    95  
    96  	// Applicatons contains the names of the applications in the model.
    97  	Applications []string `bson:"applications"`
    98  }
    99  
   100  // ControllerModel returns the model that was bootstrapped.
   101  // This is the only model that can have controller machines.
   102  // The owner of this model is also considered "special", in that
   103  // they are the only user that is able to create other users (until we
   104  // have more fine grained permissions), and they cannot be disabled.
   105  func (st *State) ControllerModel() (*Model, error) {
   106  	ssinfo, err := st.ControllerInfo()
   107  	if err != nil {
   108  		return nil, errors.Annotate(err, "could not get controller info")
   109  	}
   110  
   111  	models, closer := st.getCollection(modelsC)
   112  	defer closer()
   113  
   114  	env := &Model{st: st}
   115  	uuid := ssinfo.ModelTag.Id()
   116  	if err := env.refresh(models.FindId(uuid)); err != nil {
   117  		return nil, errors.Trace(err)
   118  	}
   119  	return env, nil
   120  }
   121  
   122  // Model returns the model entity.
   123  func (st *State) Model() (*Model, error) {
   124  	return st.GetModel(st.modelTag)
   125  }
   126  
   127  // GetModel looks for the model identified by the uuid passed in.
   128  func (st *State) GetModel(tag names.ModelTag) (*Model, error) {
   129  	models, closer := st.getCollection(modelsC)
   130  	defer closer()
   131  
   132  	model := &Model{st: st}
   133  	if err := model.refresh(models.FindId(tag.Id())); err != nil {
   134  		return nil, errors.Trace(err)
   135  	}
   136  	return model, nil
   137  }
   138  
   139  // AllModels returns all the models in the system.
   140  func (st *State) AllModels() ([]*Model, error) {
   141  	models, closer := st.getCollection(modelsC)
   142  	defer closer()
   143  
   144  	var modelDocs []modelDoc
   145  	err := models.Find(nil).Sort("name", "owner").All(&modelDocs)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	result := make([]*Model, len(modelDocs))
   151  	for i, doc := range modelDocs {
   152  		result[i] = &Model{st: st, doc: doc}
   153  	}
   154  
   155  	return result, nil
   156  }
   157  
   158  // ModelArgs is a params struct for creating a new model.
   159  type ModelArgs struct {
   160  	// CloudName is the name of the cloud to which the model is deployed.
   161  	CloudName string
   162  
   163  	// CloudRegion is the name of the cloud region to which the model is
   164  	// deployed. This will be empty for clouds that do not support regions.
   165  	CloudRegion string
   166  
   167  	// CloudCredential is the tag of the cloud credential that will be
   168  	// used for managing cloud resources for this model. This will be
   169  	// empty for clouds that do not require credentials.
   170  	CloudCredential names.CloudCredentialTag
   171  
   172  	// Config is the model config.
   173  	Config *config.Config
   174  
   175  	// Constraints contains the initial constraints for the model.
   176  	Constraints constraints.Value
   177  
   178  	// StorageProviderRegistry is used to determine and store the
   179  	// details of the default storage pools.
   180  	StorageProviderRegistry storage.ProviderRegistry
   181  
   182  	// Owner is the user that owns the model.
   183  	Owner names.UserTag
   184  
   185  	// MigrationMode is the initial migration mode of the model.
   186  	MigrationMode MigrationMode
   187  }
   188  
   189  // Validate validates the ModelArgs.
   190  func (m ModelArgs) Validate() error {
   191  	if m.Config == nil {
   192  		return errors.NotValidf("nil Config")
   193  	}
   194  	if !names.IsValidCloud(m.CloudName) {
   195  		return errors.NotValidf("Cloud Name %q", m.CloudName)
   196  	}
   197  	if m.Owner == (names.UserTag{}) {
   198  		return errors.NotValidf("empty Owner")
   199  	}
   200  	if m.StorageProviderRegistry == nil {
   201  		return errors.NotValidf("nil StorageProviderRegistry")
   202  	}
   203  	switch m.MigrationMode {
   204  	case MigrationModeNone, MigrationModeImporting:
   205  	default:
   206  		return errors.NotValidf("initial migration mode %q", m.MigrationMode)
   207  	}
   208  	return nil
   209  }
   210  
   211  // NewModel creates a new model with its own UUID and
   212  // prepares it for use. Model and State instances for the new
   213  // model are returned.
   214  //
   215  // The controller model's UUID is attached to the new
   216  // model's document. Having the server UUIDs stored with each
   217  // model document means that we have a way to represent external
   218  // models, perhaps for future use around cross model
   219  // relations.
   220  func (st *State) NewModel(args ModelArgs) (_ *Model, _ *State, err error) {
   221  	if err := args.Validate(); err != nil {
   222  		return nil, nil, errors.Trace(err)
   223  	}
   224  	// For now, the model cloud must be the same as the controller cloud.
   225  	controllerInfo, err := st.ControllerInfo()
   226  	if err != nil {
   227  		return nil, nil, errors.Trace(err)
   228  	}
   229  	if controllerInfo.CloudName != args.CloudName {
   230  		return nil, nil, errors.NewNotValid(
   231  			nil, fmt.Sprintf("controller cloud %s does not match model cloud %s", controllerInfo.CloudName, args.CloudName))
   232  	}
   233  
   234  	// Ensure that the cloud region is valid, or if one is not specified,
   235  	// that the cloud does not support regions.
   236  	controllerCloud, err := st.Cloud(args.CloudName)
   237  	if err != nil {
   238  		return nil, nil, errors.Trace(err)
   239  	}
   240  	assertCloudRegionOp, err := validateCloudRegion(controllerCloud, args.CloudName, args.CloudRegion)
   241  	if err != nil {
   242  		return nil, nil, errors.Trace(err)
   243  	}
   244  
   245  	// Ensure that the cloud credential is valid, or if one is not
   246  	// specified, that the cloud supports the "empty" authentication
   247  	// type.
   248  	owner := args.Owner
   249  	cloudCredentials, err := st.CloudCredentials(owner, args.CloudName)
   250  	if err != nil {
   251  		return nil, nil, errors.Trace(err)
   252  	}
   253  	assertCloudCredentialOp, err := validateCloudCredential(
   254  		controllerCloud, args.CloudName, cloudCredentials, args.CloudCredential,
   255  	)
   256  	if err != nil {
   257  		return nil, nil, errors.Trace(err)
   258  	}
   259  
   260  	if owner.IsLocal() {
   261  		if _, err := st.User(owner); err != nil {
   262  			return nil, nil, errors.Annotate(err, "cannot create model")
   263  		}
   264  	}
   265  
   266  	uuid := args.Config.UUID()
   267  	session := st.session.Copy()
   268  	newSt, err := newState(names.NewModelTag(uuid), controllerInfo.ModelTag, session, st.mongoInfo, st.newPolicy, st.clock)
   269  	if err != nil {
   270  		return nil, nil, errors.Annotate(err, "could not create state for new model")
   271  	}
   272  	defer func() {
   273  		if err != nil {
   274  			newSt.Close()
   275  		}
   276  	}()
   277  	newSt.controllerModelTag = st.controllerModelTag
   278  
   279  	modelOps, err := newSt.modelSetupOps(st.controllerTag.Id(), args, nil)
   280  	if err != nil {
   281  		return nil, nil, errors.Annotate(err, "failed to create new model")
   282  	}
   283  
   284  	prereqOps := []txn.Op{
   285  		assertCloudRegionOp,
   286  		assertCloudCredentialOp,
   287  	}
   288  	ops := append(prereqOps, modelOps...)
   289  	err = newSt.runTransaction(ops)
   290  	if err == txn.ErrAborted {
   291  
   292  		// We have a  unique key restriction on the "owner" and "name" fields,
   293  		// which will cause the insert to fail if there is another record with
   294  		// the same "owner" and "name" in the collection. If the txn is
   295  		// aborted, check if it is due to the unique key restriction.
   296  		name := args.Config.Name()
   297  		models, closer := st.getCollection(modelsC)
   298  		defer closer()
   299  		envCount, countErr := models.Find(bson.D{
   300  			{"owner", owner.Canonical()},
   301  			{"name", name}},
   302  		).Count()
   303  		if countErr != nil {
   304  			err = errors.Trace(countErr)
   305  		} else if envCount > 0 {
   306  			err = errors.AlreadyExistsf("model %q for %s", name, owner.Canonical())
   307  		} else {
   308  			err = errors.New("model already exists")
   309  		}
   310  	}
   311  	if err != nil {
   312  		return nil, nil, errors.Trace(err)
   313  	}
   314  
   315  	err = newSt.start(st.controllerTag)
   316  	if err != nil {
   317  		return nil, nil, errors.Annotate(err, "could not start state for new model")
   318  	}
   319  
   320  	newModel, err := newSt.Model()
   321  	if err != nil {
   322  		return nil, nil, errors.Trace(err)
   323  	}
   324  	_, err = newSt.SetUserAccess(newModel.Owner(), newModel.ModelTag(), permission.AdminAccess)
   325  	if err != nil {
   326  		return nil, nil, errors.Annotate(err, "granting admin permission to the owner")
   327  	}
   328  
   329  	return newModel, newSt, nil
   330  }
   331  
   332  // validateCloudRegion validates the given region name against the
   333  // provided Cloud definition, and returns a txn.Op to include in a
   334  // transaction to assert the same.
   335  func validateCloudRegion(cloud jujucloud.Cloud, cloudName, regionName string) (txn.Op, error) {
   336  	// Ensure that the cloud region is valid, or if one is not specified,
   337  	// that the cloud does not support regions.
   338  	assertCloudRegionOp := txn.Op{
   339  		C:  cloudsC,
   340  		Id: cloudName,
   341  	}
   342  	if regionName != "" {
   343  		region, err := jujucloud.RegionByName(cloud.Regions, regionName)
   344  		if err != nil {
   345  			return txn.Op{}, errors.Trace(err)
   346  		}
   347  		assertCloudRegionOp.Assert = bson.D{
   348  			{"regions." + region.Name, bson.D{{"$exists", true}}},
   349  		}
   350  	} else {
   351  		if len(cloud.Regions) > 0 {
   352  			return txn.Op{}, errors.NotValidf("missing CloudRegion")
   353  		}
   354  		assertCloudRegionOp.Assert = bson.D{
   355  			{"regions", bson.D{{"$exists", false}}},
   356  		}
   357  	}
   358  	return assertCloudRegionOp, nil
   359  }
   360  
   361  // validateCloudCredential validates the given cloud credential
   362  // name against the provided cloud definition and credentials,
   363  // and returns a txn.Op to include in a transaction to assert the
   364  // same. A user is supplied, for which access to the credential
   365  // will be asserted.
   366  func validateCloudCredential(
   367  	cloud jujucloud.Cloud,
   368  	cloudName string,
   369  	cloudCredentials map[string]jujucloud.Credential,
   370  	cloudCredential names.CloudCredentialTag,
   371  ) (txn.Op, error) {
   372  	if cloudCredential != (names.CloudCredentialTag{}) {
   373  		if cloudCredential.Cloud().Id() != cloudName {
   374  			return txn.Op{}, errors.NotValidf("credential %q", cloudCredential.Id())
   375  		}
   376  		var found bool
   377  		for tag := range cloudCredentials {
   378  			if tag == cloudCredential.Canonical() {
   379  				found = true
   380  				break
   381  			}
   382  		}
   383  		if !found {
   384  			return txn.Op{}, errors.NotFoundf("credential %q", cloudCredential.Id())
   385  		}
   386  		// NOTE(axw) if we add ACLs for credentials,
   387  		// we'll need to check access here. The map
   388  		// we check above contains only the credentials
   389  		// that the model owner has access to.
   390  		return txn.Op{
   391  			C:      cloudCredentialsC,
   392  			Id:     cloudCredentialDocID(cloudCredential),
   393  			Assert: txn.DocExists,
   394  		}, nil
   395  	}
   396  	var hasEmptyAuth bool
   397  	for _, authType := range cloud.AuthTypes {
   398  		if authType != jujucloud.EmptyAuthType {
   399  			continue
   400  		}
   401  		hasEmptyAuth = true
   402  		break
   403  	}
   404  	if !hasEmptyAuth {
   405  		return txn.Op{}, errors.NotValidf("missing CloudCredential")
   406  	}
   407  	return txn.Op{
   408  		C:      cloudsC,
   409  		Id:     cloudName,
   410  		Assert: bson.D{{"auth-types", string(jujucloud.EmptyAuthType)}},
   411  	}, nil
   412  }
   413  
   414  // Tag returns a name identifying the model.
   415  // The returned name will be different from other Tag values returned
   416  // by any other entities from the same state.
   417  func (m *Model) Tag() names.Tag {
   418  	return m.ModelTag()
   419  }
   420  
   421  // ModelTag is the concrete model tag for this model.
   422  func (m *Model) ModelTag() names.ModelTag {
   423  	return names.NewModelTag(m.doc.UUID)
   424  }
   425  
   426  // ControllerTag is the tag for the controller that the model is
   427  // running within.
   428  func (m *Model) ControllerTag() names.ControllerTag {
   429  	return names.NewControllerTag(m.doc.ControllerUUID)
   430  }
   431  
   432  // UUID returns the universally unique identifier of the model.
   433  func (m *Model) UUID() string {
   434  	return m.doc.UUID
   435  }
   436  
   437  // ControllerUUID returns the universally unique identifier of the controller
   438  // in which the model is running.
   439  func (m *Model) ControllerUUID() string {
   440  	return m.doc.ControllerUUID
   441  }
   442  
   443  // Name returns the human friendly name of the model.
   444  func (m *Model) Name() string {
   445  	return m.doc.Name
   446  }
   447  
   448  // Cloud returns the name of the cloud to which the model is deployed.
   449  func (m *Model) Cloud() string {
   450  	return m.doc.Cloud
   451  }
   452  
   453  // CloudRegion returns the name of the cloud region to which the model is deployed.
   454  func (m *Model) CloudRegion() string {
   455  	return m.doc.CloudRegion
   456  }
   457  
   458  // CloudCredential returns the tag of the cloud credential used for managing the
   459  // model's cloud resources, and a boolean indicating whether a credential is set.
   460  func (m *Model) CloudCredential() (names.CloudCredentialTag, bool) {
   461  	if names.IsValidCloudCredential(m.doc.CloudCredential) {
   462  		return names.NewCloudCredentialTag(m.doc.CloudCredential), true
   463  	}
   464  	return names.CloudCredentialTag{}, false
   465  }
   466  
   467  // MigrationMode returns whether the model is active or being migrated.
   468  func (m *Model) MigrationMode() MigrationMode {
   469  	return m.doc.MigrationMode
   470  }
   471  
   472  // SetMigrationMode updates the migration mode of the model.
   473  func (m *Model) SetMigrationMode(mode MigrationMode) error {
   474  	st, closeState, err := m.getState()
   475  	if err != nil {
   476  		return errors.Trace(err)
   477  	}
   478  	defer closeState()
   479  
   480  	ops := []txn.Op{{
   481  		C:      modelsC,
   482  		Id:     m.doc.UUID,
   483  		Assert: txn.DocExists,
   484  		Update: bson.D{{"$set", bson.D{{"migration-mode", mode}}}},
   485  	}}
   486  	if err := st.runTransaction(ops); err != nil {
   487  		return errors.Trace(err)
   488  	}
   489  	return m.Refresh()
   490  }
   491  
   492  // Life returns whether the model is Alive, Dying or Dead.
   493  func (m *Model) Life() Life {
   494  	return m.doc.Life
   495  }
   496  
   497  // Owner returns tag representing the owner of the model.
   498  // The owner is the user that created the model.
   499  func (m *Model) Owner() names.UserTag {
   500  	return names.NewUserTag(m.doc.Owner)
   501  }
   502  
   503  // Status returns the status of the model.
   504  func (m *Model) Status() (status.StatusInfo, error) {
   505  	st, closeState, err := m.getState()
   506  	if err != nil {
   507  		return status.StatusInfo{}, errors.Trace(err)
   508  	}
   509  	defer closeState()
   510  	status, err := getStatus(st, m.globalKey(), "model")
   511  	if err != nil {
   512  		return status, err
   513  	}
   514  	return status, nil
   515  }
   516  
   517  // SetStatus sets the status of the model.
   518  func (m *Model) SetStatus(sInfo status.StatusInfo) error {
   519  	st, closeState, err := m.getState()
   520  	if err != nil {
   521  		return errors.Trace(err)
   522  	}
   523  	defer closeState()
   524  	if !status.ValidModelStatus(sInfo.Status) {
   525  		return errors.Errorf("cannot set invalid status %q", sInfo.Status)
   526  	}
   527  	return setStatus(st, setStatusParams{
   528  		badge:     "model",
   529  		globalKey: m.globalKey(),
   530  		status:    sInfo.Status,
   531  		message:   sInfo.Message,
   532  		rawData:   sInfo.Data,
   533  		updated:   sInfo.Since,
   534  	})
   535  }
   536  
   537  // Config returns the config for the model.
   538  func (m *Model) Config() (*config.Config, error) {
   539  	st, closeState, err := m.getState()
   540  	if err != nil {
   541  		return nil, errors.Trace(err)
   542  	}
   543  	defer closeState()
   544  	return st.ModelConfig()
   545  }
   546  
   547  // ConfigValues returns the config values for the model.
   548  func (m *Model) ConfigValues() (config.ConfigValues, error) {
   549  	st, closeState, err := m.getState()
   550  	if err != nil {
   551  		return nil, errors.Trace(err)
   552  	}
   553  	defer closeState()
   554  	return st.ModelConfigValues()
   555  }
   556  
   557  // UpdateLatestToolsVersion looks up for the latest available version of
   558  // juju tools and updates environementDoc with it.
   559  func (m *Model) UpdateLatestToolsVersion(ver version.Number) error {
   560  	v := ver.String()
   561  	// TODO(perrito666): I need to assert here that there isn't a newer
   562  	// version in place.
   563  	ops := []txn.Op{{
   564  		C:      modelsC,
   565  		Id:     m.doc.UUID,
   566  		Update: bson.D{{"$set", bson.D{{"available-tools", v}}}},
   567  	}}
   568  	err := m.st.runTransaction(ops)
   569  	if err != nil {
   570  		return errors.Trace(err)
   571  	}
   572  	return m.Refresh()
   573  }
   574  
   575  // LatestToolsVersion returns the newest version found in the last
   576  // check in the streams.
   577  // Bear in mind that the check was performed filtering only
   578  // new patches for the current major.minor. (major.minor.patch)
   579  func (m *Model) LatestToolsVersion() version.Number {
   580  	ver := m.doc.LatestAvailableTools
   581  	if ver == "" {
   582  		return version.Zero
   583  	}
   584  	v, err := version.Parse(ver)
   585  	if err != nil {
   586  		// This is being stored from a valid version but
   587  		// in case this data would beacame corrupt It is not
   588  		// worth to fail because of it.
   589  		return version.Zero
   590  	}
   591  	return v
   592  }
   593  
   594  // globalKey returns the global database key for the model.
   595  func (m *Model) globalKey() string {
   596  	return modelGlobalKey
   597  }
   598  
   599  func (m *Model) Refresh() error {
   600  	models, closer := m.st.getCollection(modelsC)
   601  	defer closer()
   602  	return m.refresh(models.FindId(m.UUID()))
   603  }
   604  
   605  func (m *Model) refresh(query mongo.Query) error {
   606  	err := query.One(&m.doc)
   607  	if err == mgo.ErrNotFound {
   608  		return errors.NotFoundf("model")
   609  	}
   610  	return err
   611  }
   612  
   613  // Users returns a slice of all users for this model.
   614  func (m *Model) Users() ([]permission.UserAccess, error) {
   615  	if m.st.ModelUUID() != m.UUID() {
   616  		return nil, errors.New("cannot lookup model users outside the current model")
   617  	}
   618  	coll, closer := m.st.getCollection(modelUsersC)
   619  	defer closer()
   620  
   621  	var userDocs []userAccessDoc
   622  	err := coll.Find(nil).All(&userDocs)
   623  	if err != nil {
   624  		return nil, errors.Trace(err)
   625  	}
   626  
   627  	var modelUsers []permission.UserAccess
   628  	for _, doc := range userDocs {
   629  		// check if the User belonging to this model user has
   630  		// been deleted, in this case we should not return it.
   631  		userTag := names.NewUserTag(doc.UserName)
   632  		if userTag.IsLocal() {
   633  			_, err := m.st.User(userTag)
   634  			if errors.IsUserNotFound(err) {
   635  				continue
   636  			}
   637  			if err != nil {
   638  				return nil, errors.Trace(err)
   639  			}
   640  		}
   641  		mu, err := NewModelUserAccess(m.st, doc)
   642  		if err != nil {
   643  			return nil, errors.Trace(err)
   644  		}
   645  		modelUsers = append(modelUsers, mu)
   646  	}
   647  
   648  	return modelUsers, nil
   649  }
   650  
   651  func (m *Model) isControllerModel() bool {
   652  	return m.st.controllerModelTag.Id() == m.doc.UUID
   653  }
   654  
   655  // Destroy sets the models's lifecycle to Dying, preventing
   656  // addition of services or machines to state. If called on
   657  // an empty hosted model, the lifecycle will be advanced
   658  // straight to Dead.
   659  //
   660  // If called on a controller model, and that controller is
   661  // hosting any non-Dead models, this method will return an
   662  // error satisfying IsHasHostedsError.
   663  func (m *Model) Destroy() error {
   664  	ensureNoHostedModels := false
   665  	if m.isControllerModel() {
   666  		ensureNoHostedModels = true
   667  	}
   668  	return m.destroy(ensureNoHostedModels)
   669  }
   670  
   671  // DestroyIncludingHosted sets the model's lifecycle to Dying, preventing
   672  // addition of services or machines to state. If this model is a controller
   673  // hosting other models, they will also be destroyed.
   674  func (m *Model) DestroyIncludingHosted() error {
   675  	ensureNoHostedModels := false
   676  	return m.destroy(ensureNoHostedModels)
   677  }
   678  
   679  func (m *Model) destroy(ensureNoHostedModels bool) (err error) {
   680  	defer errors.DeferredAnnotatef(&err, "failed to destroy model")
   681  
   682  	st, closeState, err := m.getState()
   683  	if err != nil {
   684  		return errors.Trace(err)
   685  	}
   686  	defer closeState()
   687  
   688  	buildTxn := func(attempt int) ([]txn.Op, error) {
   689  		// On the first attempt, we assume memory state is recent
   690  		// enough to try using...
   691  		if attempt != 0 {
   692  			// ...but on subsequent attempts, we read fresh environ
   693  			// state from the DB. Note that we do *not* refresh `e`
   694  			// itself, as detailed in doc/hacking-state.txt
   695  			if m, err = st.Model(); err != nil {
   696  				return nil, errors.Trace(err)
   697  			}
   698  		}
   699  
   700  		ops, err := m.destroyOps(ensureNoHostedModels, false)
   701  		if err == errModelNotAlive {
   702  			return nil, jujutxn.ErrNoOperations
   703  		} else if err != nil {
   704  			return nil, errors.Trace(err)
   705  		}
   706  
   707  		return ops, nil
   708  	}
   709  
   710  	return st.run(buildTxn)
   711  }
   712  
   713  // errModelNotAlive is a signal emitted from destroyOps to indicate
   714  // that model destruction is already underway.
   715  var errModelNotAlive = errors.New("model is no longer alive")
   716  
   717  type hasHostedModelsError int
   718  
   719  func (e hasHostedModelsError) Error() string {
   720  	return fmt.Sprintf("hosting %d other models", e)
   721  }
   722  
   723  func IsHasHostedModelsError(err error) bool {
   724  	_, ok := errors.Cause(err).(hasHostedModelsError)
   725  	return ok
   726  }
   727  
   728  // destroyOps returns the txn operations necessary to begin model
   729  // destruction, or an error indicating why it can't.
   730  //
   731  // If ensureNoHostedModels is true, then destroyOps will
   732  // fail if there are any non-Dead hosted models
   733  func (m *Model) destroyOps(ensureNoHostedModels, ensureEmpty bool) ([]txn.Op, error) {
   734  	if m.Life() != Alive {
   735  		return nil, errModelNotAlive
   736  	}
   737  
   738  	// Ensure we're using the model's state.
   739  	st, closeState, err := m.getState()
   740  	if err != nil {
   741  		return nil, errors.Trace(err)
   742  	}
   743  	defer closeState()
   744  
   745  	// Check if the model is empty. If it is, we can advance the model's
   746  	// lifecycle state directly to Dead.
   747  	checkEmptyErr := m.checkEmpty()
   748  	isEmpty := checkEmptyErr == nil
   749  	if ensureEmpty && !isEmpty {
   750  		return nil, errors.Trace(checkEmptyErr)
   751  	}
   752  
   753  	modelUUID := m.UUID()
   754  	nextLife := Dying
   755  	var prereqOps []txn.Op
   756  	if isEmpty {
   757  		prereqOps = []txn.Op{{
   758  			C:  modelEntityRefsC,
   759  			Id: modelUUID,
   760  			Assert: bson.D{
   761  				{"machines", bson.D{{"$size", 0}}},
   762  				{"applications", bson.D{{"$size", 0}}},
   763  			},
   764  		}}
   765  		if !m.isControllerModel() {
   766  			// The model is empty, and is not the controller
   767  			// model, so we can move it straight to Dead.
   768  			nextLife = Dead
   769  		}
   770  	}
   771  
   772  	if ensureNoHostedModels {
   773  		// Check for any Dying or alive but non-empty models. If there
   774  		// are any, we return an error indicating that there are hosted
   775  		// models.
   776  		models, err := st.AllModels()
   777  		if err != nil {
   778  			return nil, errors.Trace(err)
   779  		}
   780  		var aliveEmpty, aliveNonEmpty, dying, dead int
   781  		for _, model := range models {
   782  			if model.UUID() == m.UUID() {
   783  				// Ignore the controller model.
   784  				continue
   785  			}
   786  			if model.Life() == Dead {
   787  				// Dead hosted models don't affect
   788  				// whether the controller can be
   789  				// destroyed or not, but they are
   790  				// still counted in the hosted models.
   791  				dead++
   792  				continue
   793  			}
   794  			// See if the model is empty, and if it is,
   795  			// get the ops required to destroy it.
   796  			ops, err := model.destroyOps(false, true)
   797  			switch err {
   798  			case errModelNotAlive:
   799  				dying++
   800  			case nil:
   801  				prereqOps = append(prereqOps, ops...)
   802  				aliveEmpty++
   803  			default:
   804  				aliveNonEmpty++
   805  			}
   806  		}
   807  		if dying > 0 || aliveNonEmpty > 0 {
   808  			// There are Dying, or Alive but non-empty models.
   809  			// We cannot destroy the controller without first
   810  			// destroying the models and waiting for them to
   811  			// become Dead.
   812  			return nil, errors.Trace(
   813  				hasHostedModelsError(dying + aliveNonEmpty + aliveEmpty),
   814  			)
   815  		}
   816  		// Ensure that the number of active models has not changed
   817  		// between the query and when the transaction is applied.
   818  		//
   819  		// Note that we assert that each empty model that we intend
   820  		// move to Dead is still Alive, so we're protected from an
   821  		// ABA style problem where an empty model is concurrently
   822  		// removed, and replaced with a non-empty model.
   823  		prereqOps = append(prereqOps, assertHostedModelsOp(aliveEmpty+dead))
   824  	}
   825  
   826  	timeOfDying := st.NowToTheSecond()
   827  	modelUpdateValues := bson.D{
   828  		{"life", nextLife},
   829  		{"time-of-dying", timeOfDying},
   830  	}
   831  	if nextLife == Dead {
   832  		modelUpdateValues = append(modelUpdateValues, bson.DocElem{
   833  			"time-of-death", timeOfDying,
   834  		})
   835  	}
   836  
   837  	ops := []txn.Op{{
   838  		C:      modelsC,
   839  		Id:     modelUUID,
   840  		Assert: isAliveDoc,
   841  		Update: bson.D{{"$set", modelUpdateValues}},
   842  	}}
   843  
   844  	// Because txn operations execute in order, and may encounter
   845  	// arbitrarily long delays, we need to make sure every op
   846  	// causes a state change that's still consistent; so we make
   847  	// sure the cleanup ops are the last thing that will execute.
   848  	if m.isControllerModel() {
   849  		cleanupOp := newCleanupOp(cleanupModelsForDyingController, modelUUID)
   850  		ops = append(ops, cleanupOp)
   851  	}
   852  	if !isEmpty {
   853  		// We only need to destroy machines and models if the model is
   854  		// non-empty. It wouldn't normally be harmful to enqueue the
   855  		// cleanups otherwise, except for when we're destroying an empty
   856  		// hosted model in the course of destroying the controller. In
   857  		// that case we'll get errors if we try to enqueue hosted-model
   858  		// cleanups, because the cleanups collection is non-global.
   859  		cleanupMachinesOp := newCleanupOp(cleanupMachinesForDyingModel, modelUUID)
   860  		cleanupServicesOp := newCleanupOp(cleanupServicesForDyingModel, modelUUID)
   861  		ops = append(ops, cleanupMachinesOp, cleanupServicesOp)
   862  	}
   863  	return append(prereqOps, ops...), nil
   864  }
   865  
   866  // checkEmpty checks that the machine is empty of any entities that may
   867  // require external resource cleanup. If the model is not empty, then
   868  // an error will be returned.
   869  func (m *Model) checkEmpty() error {
   870  	st, closeState, err := m.getState()
   871  	if err != nil {
   872  		return errors.Trace(err)
   873  	}
   874  	defer closeState()
   875  
   876  	modelEntityRefs, closer := st.getCollection(modelEntityRefsC)
   877  	defer closer()
   878  
   879  	var doc modelEntityRefsDoc
   880  	if err := modelEntityRefs.FindId(m.UUID()).One(&doc); err != nil {
   881  		if err == mgo.ErrNotFound {
   882  			return errors.NotFoundf("entity references doc for model %s", m.UUID())
   883  		}
   884  		return errors.Annotatef(err, "getting entity references for model %s", m.UUID())
   885  	}
   886  	if n := len(doc.Machines); n > 0 {
   887  		return errors.Errorf("model not empty, found %d machine(s)", n)
   888  	}
   889  	if n := len(doc.Applications); n > 0 {
   890  		return errors.Errorf("model not empty, found %d applications(s)", n)
   891  	}
   892  	return nil
   893  }
   894  
   895  func addModelMachineRefOp(st *State, machineId string) txn.Op {
   896  	return txn.Op{
   897  		C:      modelEntityRefsC,
   898  		Id:     st.ModelUUID(),
   899  		Assert: txn.DocExists,
   900  		Update: bson.D{{"$addToSet", bson.D{{"machines", machineId}}}},
   901  	}
   902  }
   903  
   904  func removeModelMachineRefOp(st *State, machineId string) txn.Op {
   905  	return txn.Op{
   906  		C:      modelEntityRefsC,
   907  		Id:     st.ModelUUID(),
   908  		Update: bson.D{{"$pull", bson.D{{"machines", machineId}}}},
   909  	}
   910  }
   911  
   912  func addModelServiceRefOp(st *State, applicationname string) txn.Op {
   913  	return txn.Op{
   914  		C:      modelEntityRefsC,
   915  		Id:     st.ModelUUID(),
   916  		Assert: txn.DocExists,
   917  		Update: bson.D{{"$addToSet", bson.D{{"applications", applicationname}}}},
   918  	}
   919  }
   920  
   921  func removeModelServiceRefOp(st *State, applicationname string) txn.Op {
   922  	return txn.Op{
   923  		C:      modelEntityRefsC,
   924  		Id:     st.ModelUUID(),
   925  		Update: bson.D{{"$pull", bson.D{{"applications", applicationname}}}},
   926  	}
   927  }
   928  
   929  // createModelOp returns the operation needed to create
   930  // an model document with the given name and UUID.
   931  func createModelOp(
   932  	owner names.UserTag,
   933  	name, uuid, controllerUUID, cloudName, cloudRegion string,
   934  	cloudCredential names.CloudCredentialTag,
   935  	migrationMode MigrationMode,
   936  ) txn.Op {
   937  	doc := &modelDoc{
   938  		UUID:            uuid,
   939  		Name:            name,
   940  		Life:            Alive,
   941  		Owner:           owner.Canonical(),
   942  		ControllerUUID:  controllerUUID,
   943  		MigrationMode:   migrationMode,
   944  		Cloud:           cloudName,
   945  		CloudRegion:     cloudRegion,
   946  		CloudCredential: cloudCredential.Canonical(),
   947  	}
   948  	return txn.Op{
   949  		C:      modelsC,
   950  		Id:     uuid,
   951  		Assert: txn.DocMissing,
   952  		Insert: doc,
   953  	}
   954  }
   955  
   956  func createModelEntityRefsOp(uuid string) txn.Op {
   957  	return txn.Op{
   958  		C:      modelEntityRefsC,
   959  		Id:     uuid,
   960  		Assert: txn.DocMissing,
   961  		Insert: &modelEntityRefsDoc{UUID: uuid},
   962  	}
   963  }
   964  
   965  const hostedModelCountKey = "hostedModelCount"
   966  
   967  type hostedModelCountDoc struct {
   968  	// RefCount is the number of models in the Juju system.
   969  	// We do not count the system model.
   970  	RefCount int `bson:"refcount"`
   971  }
   972  
   973  func assertHostedModelsOp(n int) txn.Op {
   974  	return txn.Op{
   975  		C:      controllersC,
   976  		Id:     hostedModelCountKey,
   977  		Assert: bson.D{{"refcount", n}},
   978  	}
   979  }
   980  
   981  func incHostedModelCountOp() txn.Op {
   982  	return HostedModelCountOp(1)
   983  }
   984  
   985  func decHostedModelCountOp() txn.Op {
   986  	return HostedModelCountOp(-1)
   987  }
   988  
   989  func HostedModelCountOp(amount int) txn.Op {
   990  	return txn.Op{
   991  		C:      controllersC,
   992  		Id:     hostedModelCountKey,
   993  		Assert: txn.DocExists,
   994  		Update: bson.M{
   995  			"$inc": bson.M{"refcount": amount},
   996  		},
   997  	}
   998  }
   999  
  1000  func hostedModelCount(st *State) (int, error) {
  1001  	var doc hostedModelCountDoc
  1002  	controllers, closer := st.getCollection(controllersC)
  1003  	defer closer()
  1004  
  1005  	if err := controllers.Find(bson.D{{"_id", hostedModelCountKey}}).One(&doc); err != nil {
  1006  		return 0, errors.Trace(err)
  1007  	}
  1008  	return doc.RefCount, nil
  1009  }
  1010  
  1011  // createUniqueOwnerModelNameOp returns the operation needed to create
  1012  // an usermodelnameC document with the given owner and model name.
  1013  func createUniqueOwnerModelNameOp(owner names.UserTag, envName string) txn.Op {
  1014  	return txn.Op{
  1015  		C:      usermodelnameC,
  1016  		Id:     userModelNameIndex(owner.Canonical(), envName),
  1017  		Assert: txn.DocMissing,
  1018  		Insert: bson.M{},
  1019  	}
  1020  }
  1021  
  1022  // assertAliveOp returns a txn.Op that asserts the model is alive.
  1023  func (m *Model) assertActiveOp() txn.Op {
  1024  	return assertModelActiveOp(m.UUID())
  1025  }
  1026  
  1027  // getState returns the State for the model, and a function to close
  1028  // it when done. If m.st is the model-specific state, then it will
  1029  // be returned and the close function will be a no-op.
  1030  //
  1031  // TODO(axw) 2016-04-14 #1570269
  1032  // find a way to guarantee that every Model is associated with the
  1033  // appropriate State. The current work-around is too easy to get wrong.
  1034  func (m *Model) getState() (*State, func(), error) {
  1035  	if m.st.modelTag == m.ModelTag() {
  1036  		return m.st, func() {}, nil
  1037  	}
  1038  	st, err := m.st.ForModel(m.ModelTag())
  1039  	if err != nil {
  1040  		return nil, nil, errors.Trace(err)
  1041  	}
  1042  	uuid := st.ModelUUID()
  1043  	return st, func() {
  1044  		if err := st.Close(); err != nil {
  1045  			logger.Errorf("closing temporary state for model %s", uuid)
  1046  		}
  1047  	}, nil
  1048  }
  1049  
  1050  // assertModelActiveOp returns a txn.Op that asserts the given
  1051  // model UUID refers to an Alive model.
  1052  func assertModelActiveOp(modelUUID string) txn.Op {
  1053  	return txn.Op{
  1054  		C:      modelsC,
  1055  		Id:     modelUUID,
  1056  		Assert: append(isAliveDoc, bson.DocElem{"migration-mode", MigrationModeNone}),
  1057  	}
  1058  }
  1059  
  1060  func checkModelActive(st *State) error {
  1061  	model, err := st.Model()
  1062  	if (err == nil && model.Life() != Alive) || errors.IsNotFound(err) {
  1063  		return errors.Errorf("model %q is no longer alive", model.Name())
  1064  	} else if err != nil {
  1065  		return errors.Annotate(err, "unable to read model")
  1066  	} else if mode := model.MigrationMode(); mode != MigrationModeNone {
  1067  		return errors.Errorf("model %q is being migrated", model.Name())
  1068  	}
  1069  	return nil
  1070  }