github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"strings"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names"
    12  	jujutxn "github.com/juju/txn"
    13  	"gopkg.in/mgo.v2"
    14  	"gopkg.in/mgo.v2/bson"
    15  	"gopkg.in/mgo.v2/txn"
    16  
    17  	"github.com/juju/juju/environs/config"
    18  	"github.com/juju/juju/mongo"
    19  	"github.com/juju/juju/status"
    20  	"github.com/juju/version"
    21  )
    22  
    23  // modelGlobalKey is the key for the model, its
    24  // settings and constraints.
    25  const modelGlobalKey = "e"
    26  
    27  // MigrationMode specifies where the Model is with respect to migration.
    28  type MigrationMode string
    29  
    30  const (
    31  	// MigrationModeActive is the default mode for a model and reflects
    32  	// a model that is active within its controller.
    33  	MigrationModeActive MigrationMode = ""
    34  
    35  	// MigrationModeExporting reflects a model that is in the process of being
    36  	// exported from one controller to another.
    37  	MigrationModeExporting MigrationMode = "exporting"
    38  
    39  	// MigrationModeImporting reflects a model that is being imported into a
    40  	// controller, but is not yet fully active.
    41  	MigrationModeImporting MigrationMode = "importing"
    42  )
    43  
    44  // Model represents the state of a model.
    45  type Model struct {
    46  	// st is not necessarily the state of this model. Though it is
    47  	// usually safe to assume that it is. The only times it isn't is when we
    48  	// get models other than the current one - which is mostly in
    49  	// controller api endpoints.
    50  	st  *State
    51  	doc modelDoc
    52  }
    53  
    54  // modelDoc represents the internal state of the model in MongoDB.
    55  type modelDoc struct {
    56  	UUID          string `bson:"_id"`
    57  	Name          string
    58  	Life          Life
    59  	Owner         string        `bson:"owner"`
    60  	ServerUUID    string        `bson:"server-uuid"`
    61  	MigrationMode MigrationMode `bson:"migration-mode"`
    62  
    63  	// LatestAvailableTools is a string representing the newest version
    64  	// found while checking streams for new versions.
    65  	LatestAvailableTools string `bson:"available-tools,omitempty"`
    66  }
    67  
    68  // modelEntityRefsDoc records references to the top-level entities
    69  // in the model.
    70  type modelEntityRefsDoc struct {
    71  	UUID string `bson:"_id"`
    72  
    73  	// Machines contains the names of the top-level machines in the model.
    74  	Machines []string `bson:"machines"`
    75  
    76  	// Services contains the names of the services in the model.
    77  	Services []string `bson:"services"`
    78  }
    79  
    80  // ControllerModel returns the model that was bootstrapped.
    81  // This is the only model that can have controller machines.
    82  // The owner of this model is also considered "special", in that
    83  // they are the only user that is able to create other users (until we
    84  // have more fine grained permissions), and they cannot be disabled.
    85  func (st *State) ControllerModel() (*Model, error) {
    86  	ssinfo, err := st.ControllerInfo()
    87  	if err != nil {
    88  		return nil, errors.Annotate(err, "could not get controller info")
    89  	}
    90  
    91  	models, closer := st.getCollection(modelsC)
    92  	defer closer()
    93  
    94  	env := &Model{st: st}
    95  	uuid := ssinfo.ModelTag.Id()
    96  	if err := env.refresh(models.FindId(uuid)); err != nil {
    97  		return nil, errors.Trace(err)
    98  	}
    99  	return env, nil
   100  }
   101  
   102  // Model returns the model entity.
   103  func (st *State) Model() (*Model, error) {
   104  	models, closer := st.getCollection(modelsC)
   105  	defer closer()
   106  
   107  	env := &Model{st: st}
   108  	uuid := st.modelTag.Id()
   109  	if err := env.refresh(models.FindId(uuid)); err != nil {
   110  		return nil, errors.Trace(err)
   111  	}
   112  	return env, nil
   113  }
   114  
   115  // GetModel looks for the model identified by the uuid passed in.
   116  func (st *State) GetModel(tag names.ModelTag) (*Model, error) {
   117  	models, closer := st.getCollection(modelsC)
   118  	defer closer()
   119  
   120  	env := &Model{st: st}
   121  	if err := env.refresh(models.FindId(tag.Id())); err != nil {
   122  		return nil, errors.Trace(err)
   123  	}
   124  	return env, nil
   125  }
   126  
   127  // AllModels returns all the models in the system.
   128  func (st *State) AllModels() ([]*Model, error) {
   129  	models, closer := st.getCollection(modelsC)
   130  	defer closer()
   131  
   132  	var envDocs []modelDoc
   133  	err := models.Find(nil).All(&envDocs)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	result := make([]*Model, len(envDocs))
   139  	for i, doc := range envDocs {
   140  		result[i] = &Model{st: st, doc: doc}
   141  	}
   142  
   143  	return result, nil
   144  }
   145  
   146  // ModelArgs is a params struct for creating a new model.
   147  type ModelArgs struct {
   148  	Config        *config.Config
   149  	Owner         names.UserTag
   150  	MigrationMode MigrationMode
   151  }
   152  
   153  // NewModel creates a new model with its own UUID and
   154  // prepares it for use. Model and State instances for the new
   155  // model are returned.
   156  //
   157  // The controller model's UUID is attached to the new
   158  // model's document. Having the server UUIDs stored with each
   159  // model document means that we have a way to represent external
   160  // models, perhaps for future use around cross model
   161  // relations.
   162  func (st *State) NewModel(args ModelArgs) (_ *Model, _ *State, err error) {
   163  	owner := args.Owner
   164  	if owner.IsLocal() {
   165  		if _, err := st.User(owner); err != nil {
   166  			return nil, nil, errors.Annotate(err, "cannot create model")
   167  		}
   168  	}
   169  
   170  	ssEnv, err := st.ControllerModel()
   171  	if err != nil {
   172  		return nil, nil, errors.Annotate(err, "could not load controller model")
   173  	}
   174  
   175  	uuid := args.Config.UUID()
   176  	newState, err := st.ForModel(names.NewModelTag(uuid))
   177  	if err != nil {
   178  		return nil, nil, errors.Annotate(err, "could not create state for new model")
   179  	}
   180  	defer func() {
   181  		if err != nil {
   182  			newState.Close()
   183  		}
   184  	}()
   185  
   186  	ops, err := newState.modelSetupOps(args.Config, uuid, ssEnv.UUID(), owner, args.MigrationMode)
   187  	if err != nil {
   188  		return nil, nil, errors.Annotate(err, "failed to create new model")
   189  	}
   190  	err = newState.runTransaction(ops)
   191  	if err == txn.ErrAborted {
   192  
   193  		// We have a  unique key restriction on the "owner" and "name" fields,
   194  		// which will cause the insert to fail if there is another record with
   195  		// the same "owner" and "name" in the collection. If the txn is
   196  		// aborted, check if it is due to the unique key restriction.
   197  		name := args.Config.Name()
   198  		models, closer := st.getCollection(modelsC)
   199  		defer closer()
   200  		envCount, countErr := models.Find(bson.D{
   201  			{"owner", owner.Canonical()},
   202  			{"name", name}},
   203  		).Count()
   204  		if countErr != nil {
   205  			err = errors.Trace(countErr)
   206  		} else if envCount > 0 {
   207  			err = errors.AlreadyExistsf("model %q for %s", name, owner.Canonical())
   208  		} else {
   209  			err = errors.New("model already exists")
   210  		}
   211  	}
   212  	if err != nil {
   213  		return nil, nil, errors.Trace(err)
   214  	}
   215  
   216  	newEnv, err := newState.Model()
   217  	if err != nil {
   218  		return nil, nil, errors.Trace(err)
   219  	}
   220  
   221  	return newEnv, newState, nil
   222  }
   223  
   224  // Tag returns a name identifying the model.
   225  // The returned name will be different from other Tag values returned
   226  // by any other entities from the same state.
   227  func (m *Model) Tag() names.Tag {
   228  	return m.ModelTag()
   229  }
   230  
   231  // ModelTag is the concrete model tag for this model.
   232  func (m *Model) ModelTag() names.ModelTag {
   233  	return names.NewModelTag(m.doc.UUID)
   234  }
   235  
   236  // ControllerTag is the model tag for the controller that the model is
   237  // running within.
   238  func (m *Model) ControllerTag() names.ModelTag {
   239  	return names.NewModelTag(m.doc.ServerUUID)
   240  }
   241  
   242  // UUID returns the universally unique identifier of the model.
   243  func (m *Model) UUID() string {
   244  	return m.doc.UUID
   245  }
   246  
   247  // ControllerUUID returns the universally unique identifier of the controller
   248  // in which the model is running.
   249  func (m *Model) ControllerUUID() string {
   250  	return m.doc.ServerUUID
   251  }
   252  
   253  // Name returns the human friendly name of the model.
   254  func (m *Model) Name() string {
   255  	return m.doc.Name
   256  }
   257  
   258  // MigrationMode returns whether the model is active or being migrated.
   259  func (m *Model) MigrationMode() MigrationMode {
   260  	return m.doc.MigrationMode
   261  }
   262  
   263  // SetMigrationMode updates the migration mode of the model.
   264  func (m *Model) SetMigrationMode(mode MigrationMode) error {
   265  	st, closeState, err := m.getState()
   266  	if err != nil {
   267  		return errors.Trace(err)
   268  	}
   269  	defer closeState()
   270  
   271  	ops := []txn.Op{{
   272  		C:      modelsC,
   273  		Id:     m.doc.UUID,
   274  		Assert: txn.DocExists,
   275  		Update: bson.D{{"$set", bson.D{{"migration-mode", mode}}}},
   276  	}}
   277  	if err := st.runTransaction(ops); err != nil {
   278  		return errors.Trace(err)
   279  	}
   280  	return m.Refresh()
   281  }
   282  
   283  // Life returns whether the model is Alive, Dying or Dead.
   284  func (m *Model) Life() Life {
   285  	return m.doc.Life
   286  }
   287  
   288  // Owner returns tag representing the owner of the model.
   289  // The owner is the user that created the model.
   290  func (m *Model) Owner() names.UserTag {
   291  	return names.NewUserTag(m.doc.Owner)
   292  }
   293  
   294  // Status returns the status of the model.
   295  func (m *Model) Status() (status.StatusInfo, error) {
   296  	st, closeState, err := m.getState()
   297  	if err != nil {
   298  		return status.StatusInfo{}, errors.Trace(err)
   299  	}
   300  	defer closeState()
   301  	status, err := getStatus(st, m.globalKey(), "model")
   302  	if err != nil {
   303  		return status, err
   304  	}
   305  	return status, nil
   306  }
   307  
   308  // SetStatus sets the status of the model.
   309  func (m *Model) SetStatus(modelStatus status.Status, info string, data map[string]interface{}) error {
   310  	st, closeState, err := m.getState()
   311  	if err != nil {
   312  		return errors.Trace(err)
   313  	}
   314  	defer closeState()
   315  	if !status.ValidModelStatus(modelStatus) {
   316  		return errors.Errorf("cannot set invalid status %q", modelStatus)
   317  	}
   318  	return setStatus(st, setStatusParams{
   319  		badge:     "model",
   320  		globalKey: m.globalKey(),
   321  		status:    modelStatus,
   322  		message:   info,
   323  		rawData:   data,
   324  	})
   325  }
   326  
   327  // Config returns the config for the model.
   328  func (m *Model) Config() (*config.Config, error) {
   329  	st, closeState, err := m.getState()
   330  	if err != nil {
   331  		return nil, errors.Trace(err)
   332  	}
   333  	defer closeState()
   334  	return st.ModelConfig()
   335  }
   336  
   337  // UpdateLatestToolsVersion looks up for the latest available version of
   338  // juju tools and updates environementDoc with it.
   339  func (m *Model) UpdateLatestToolsVersion(ver version.Number) error {
   340  	v := ver.String()
   341  	// TODO(perrito666): I need to assert here that there isn't a newer
   342  	// version in place.
   343  	ops := []txn.Op{{
   344  		C:      modelsC,
   345  		Id:     m.doc.UUID,
   346  		Update: bson.D{{"$set", bson.D{{"available-tools", v}}}},
   347  	}}
   348  	err := m.st.runTransaction(ops)
   349  	if err != nil {
   350  		return errors.Trace(err)
   351  	}
   352  	return m.Refresh()
   353  }
   354  
   355  // LatestToolsVersion returns the newest version found in the last
   356  // check in the streams.
   357  // Bear in mind that the check was performed filtering only
   358  // new patches for the current major.minor. (major.minor.patch)
   359  func (m *Model) LatestToolsVersion() version.Number {
   360  	ver := m.doc.LatestAvailableTools
   361  	if ver == "" {
   362  		return version.Zero
   363  	}
   364  	v, err := version.Parse(ver)
   365  	if err != nil {
   366  		// This is being stored from a valid version but
   367  		// in case this data would beacame corrupt It is not
   368  		// worth to fail because of it.
   369  		return version.Zero
   370  	}
   371  	return v
   372  }
   373  
   374  // globalKey returns the global database key for the model.
   375  func (m *Model) globalKey() string {
   376  	return modelGlobalKey
   377  }
   378  
   379  func (m *Model) Refresh() error {
   380  	models, closer := m.st.getCollection(modelsC)
   381  	defer closer()
   382  	return m.refresh(models.FindId(m.UUID()))
   383  }
   384  
   385  func (m *Model) refresh(query mongo.Query) error {
   386  	err := query.One(&m.doc)
   387  	if err == mgo.ErrNotFound {
   388  		return errors.NotFoundf("model")
   389  	}
   390  	return err
   391  }
   392  
   393  // Users returns a slice of all users for this model.
   394  func (m *Model) Users() ([]*ModelUser, error) {
   395  	if m.st.ModelUUID() != m.UUID() {
   396  		return nil, errors.New("cannot lookup model users outside the current model")
   397  	}
   398  	coll, closer := m.st.getCollection(modelUsersC)
   399  	defer closer()
   400  
   401  	var userDocs []modelUserDoc
   402  	err := coll.Find(nil).All(&userDocs)
   403  	if err != nil {
   404  		return nil, errors.Trace(err)
   405  	}
   406  
   407  	var modelUsers []*ModelUser
   408  	for _, doc := range userDocs {
   409  		modelUsers = append(modelUsers, &ModelUser{
   410  			st:  m.st,
   411  			doc: doc,
   412  		})
   413  	}
   414  
   415  	return modelUsers, nil
   416  }
   417  
   418  // Destroy sets the models's lifecycle to Dying, preventing
   419  // addition of services or machines to state. If called on
   420  // an empty hosted model, the lifecycle will be advanced
   421  // straight to Dead.
   422  //
   423  // If called on a controller model, and that controller is
   424  // hosting any non-Dead models, this method will return an
   425  // error satisfying IsHasHostedsError.
   426  func (m *Model) Destroy() (err error) {
   427  	ensureNoHostedModels := false
   428  	if m.doc.UUID == m.doc.ServerUUID {
   429  		ensureNoHostedModels = true
   430  	}
   431  	return m.destroy(ensureNoHostedModels)
   432  }
   433  
   434  // DestroyIncludingHosted sets the model's lifecycle to Dying, preventing
   435  // addition of services or machines to state. If this model is a controller
   436  // hosting other models, they will also be destroyed.
   437  func (m *Model) DestroyIncludingHosted() error {
   438  	ensureNoHostedModels := false
   439  	return m.destroy(ensureNoHostedModels)
   440  }
   441  
   442  func (m *Model) destroy(ensureNoHostedModels bool) (err error) {
   443  	defer errors.DeferredAnnotatef(&err, "failed to destroy model")
   444  
   445  	st, closeState, err := m.getState()
   446  	if err != nil {
   447  		return errors.Trace(err)
   448  	}
   449  	defer closeState()
   450  
   451  	buildTxn := func(attempt int) ([]txn.Op, error) {
   452  		// On the first attempt, we assume memory state is recent
   453  		// enough to try using...
   454  		if attempt != 0 {
   455  			// ...but on subsequent attempts, we read fresh environ
   456  			// state from the DB. Note that we do *not* refresh `e`
   457  			// itself, as detailed in doc/hacking-state.txt
   458  			if m, err = st.Model(); err != nil {
   459  				return nil, errors.Trace(err)
   460  			}
   461  		}
   462  
   463  		ops, err := m.destroyOps(ensureNoHostedModels, false)
   464  		if err == errModelNotAlive {
   465  			return nil, jujutxn.ErrNoOperations
   466  		} else if err != nil {
   467  			return nil, errors.Trace(err)
   468  		}
   469  
   470  		return ops, nil
   471  	}
   472  
   473  	return st.run(buildTxn)
   474  }
   475  
   476  // errModelNotAlive is a signal emitted from destroyOps to indicate
   477  // that model destruction is already underway.
   478  var errModelNotAlive = errors.New("model is no longer alive")
   479  
   480  type hasHostedModelsError int
   481  
   482  func (e hasHostedModelsError) Error() string {
   483  	return fmt.Sprintf("hosting %d other models", e)
   484  }
   485  
   486  func IsHasHostedModelsError(err error) bool {
   487  	_, ok := errors.Cause(err).(hasHostedModelsError)
   488  	return ok
   489  }
   490  
   491  // destroyOps returns the txn operations necessary to begin model
   492  // destruction, or an error indicating why it can't.
   493  //
   494  // If ensureNoHostedModels is true, then destroyOps will
   495  // fail if there are any non-Dead hosted models
   496  func (m *Model) destroyOps(ensureNoHostedModels, ensureEmpty bool) ([]txn.Op, error) {
   497  	if m.Life() != Alive {
   498  		return nil, errModelNotAlive
   499  	}
   500  
   501  	// Ensure we're using the model's state.
   502  	st, closeState, err := m.getState()
   503  	if err != nil {
   504  		return nil, errors.Trace(err)
   505  	}
   506  	defer closeState()
   507  
   508  	if err := ensureDestroyable(st); err != nil {
   509  		return nil, errors.Trace(err)
   510  	}
   511  
   512  	// Check if the model is empty. If it is, we can advance the model's
   513  	// lifecycle state directly to Dead.
   514  	var prereqOps []txn.Op
   515  	checkEmptyErr := m.checkEmpty()
   516  	isEmpty := checkEmptyErr == nil
   517  	uuid := m.UUID()
   518  	if ensureEmpty && !isEmpty {
   519  		return nil, errors.Trace(checkEmptyErr)
   520  	}
   521  	if isEmpty {
   522  		prereqOps = append(prereqOps, txn.Op{
   523  			C:  modelEntityRefsC,
   524  			Id: uuid,
   525  			Assert: bson.D{
   526  				{"machines", bson.D{{"$size", 0}}},
   527  				{"services", bson.D{{"$size", 0}}},
   528  			},
   529  		})
   530  	}
   531  
   532  	if ensureNoHostedModels {
   533  		// Check for any Dying or alive but non-empty models. If there
   534  		// are any, we return an error indicating that there are hosted
   535  		// models.
   536  		models, err := st.AllModels()
   537  		if err != nil {
   538  			return nil, errors.Trace(err)
   539  		}
   540  		var aliveEmpty, aliveNonEmpty, dying, dead int
   541  		for _, model := range models {
   542  			if model.UUID() == m.UUID() {
   543  				// Ignore the controller model.
   544  				continue
   545  			}
   546  			if model.Life() == Dead {
   547  				// Dead hosted models don't affect
   548  				// whether the controller can be
   549  				// destroyed or not, but they are
   550  				// still counted in the hosted models.
   551  				dead++
   552  				continue
   553  			}
   554  			// See if the model is empty, and if it is,
   555  			// get the ops required to destroy it.
   556  			ops, err := model.destroyOps(false, true)
   557  			switch err {
   558  			case errModelNotAlive:
   559  				dying++
   560  			case nil:
   561  				prereqOps = append(prereqOps, ops...)
   562  				aliveEmpty++
   563  			default:
   564  				aliveNonEmpty++
   565  			}
   566  		}
   567  		if dying > 0 || aliveNonEmpty > 0 {
   568  			// There are Dying, or Alive but non-empty models.
   569  			// We cannot destroy the controller without first
   570  			// destroying the models and waiting for them to
   571  			// become Dead.
   572  			return nil, errors.Trace(
   573  				hasHostedModelsError(dying + aliveNonEmpty + aliveEmpty),
   574  			)
   575  		}
   576  		// Ensure that the number of active models has not changed
   577  		// between the query and when the transaction is applied.
   578  		//
   579  		// Note that we assert that each empty model that we intend
   580  		// move to Dead is still Alive, so we're protected from an
   581  		// ABA style problem where an empty model is concurrently
   582  		// removed, and replaced with a non-empty model.
   583  		prereqOps = append(prereqOps, assertHostedModelsOp(aliveEmpty+dead))
   584  	}
   585  
   586  	life := Dying
   587  	if isEmpty && uuid != m.doc.ServerUUID {
   588  		// The model is empty, and is not the admin
   589  		// model, so we can move it straight to Dead.
   590  		life = Dead
   591  	}
   592  	timeOfDying := nowToTheSecond()
   593  	modelUpdateValues := bson.D{
   594  		{"life", life},
   595  		{"time-of-dying", timeOfDying},
   596  	}
   597  	if life == Dead {
   598  		modelUpdateValues = append(modelUpdateValues, bson.DocElem{
   599  			"time-of-death", timeOfDying,
   600  		})
   601  	}
   602  
   603  	ops := []txn.Op{{
   604  		C:      modelsC,
   605  		Id:     uuid,
   606  		Assert: isAliveDoc,
   607  		Update: bson.D{{"$set", modelUpdateValues}},
   608  	}}
   609  
   610  	// Because txn operations execute in order, and may encounter
   611  	// arbitrarily long delays, we need to make sure every op
   612  	// causes a state change that's still consistent; so we make
   613  	// sure the cleanup ops are the last thing that will execute.
   614  	if uuid == m.doc.ServerUUID {
   615  		cleanupOp := st.newCleanupOp(cleanupModelsForDyingController, uuid)
   616  		ops = append(ops, cleanupOp)
   617  	}
   618  	if !isEmpty {
   619  		// We only need to destroy machines and models if the model is
   620  		// non-empty. It wouldn't normally be harmful to enqueue the
   621  		// cleanups otherwise, except for when we're destroying an empty
   622  		// hosted model in the course of destroying the controller. In
   623  		// that case we'll get errors if we try to enqueue hosted-model
   624  		// cleanups, because the cleanups collection is non-global.
   625  		cleanupMachinesOp := st.newCleanupOp(cleanupMachinesForDyingModel, uuid)
   626  		ops = append(ops, cleanupMachinesOp)
   627  		cleanupServicesOp := st.newCleanupOp(cleanupServicesForDyingModel, uuid)
   628  		ops = append(ops, cleanupServicesOp)
   629  	}
   630  	return append(prereqOps, ops...), nil
   631  }
   632  
   633  // checkEmpty checks that the machine is empty of any entities that may
   634  // require external resource cleanup. If the machine is not empty, then
   635  // an error will be returned.
   636  func (m *Model) checkEmpty() error {
   637  	st, closeState, err := m.getState()
   638  	if err != nil {
   639  		return errors.Trace(err)
   640  	}
   641  	defer closeState()
   642  
   643  	modelEntityRefs, closer := st.getCollection(modelEntityRefsC)
   644  	defer closer()
   645  
   646  	var doc modelEntityRefsDoc
   647  	if err := modelEntityRefs.FindId(m.UUID()).One(&doc); err != nil {
   648  		if err == mgo.ErrNotFound {
   649  			return errors.NotFoundf("entity references doc for model %s", m.UUID())
   650  		}
   651  		return errors.Annotatef(err, "getting entity references for model %s", m.UUID())
   652  	}
   653  	if n := len(doc.Machines); n > 0 {
   654  		return errors.Errorf("model not empty, found %d machine(s)", n)
   655  	}
   656  	if n := len(doc.Services); n > 0 {
   657  		return errors.Errorf("model not empty, found %d services(s)", n)
   658  	}
   659  	return nil
   660  }
   661  
   662  func addModelMachineRefOp(st *State, machineId string) txn.Op {
   663  	return txn.Op{
   664  		C:      modelEntityRefsC,
   665  		Id:     st.ModelUUID(),
   666  		Assert: txn.DocExists,
   667  		Update: bson.D{{"$addToSet", bson.D{{"machines", machineId}}}},
   668  	}
   669  }
   670  
   671  func removeModelMachineRefOp(st *State, machineId string) txn.Op {
   672  	return txn.Op{
   673  		C:      modelEntityRefsC,
   674  		Id:     st.ModelUUID(),
   675  		Update: bson.D{{"$pull", bson.D{{"machines", machineId}}}},
   676  	}
   677  }
   678  
   679  func addModelServiceRefOp(st *State, serviceName string) txn.Op {
   680  	return txn.Op{
   681  		C:      modelEntityRefsC,
   682  		Id:     st.ModelUUID(),
   683  		Assert: txn.DocExists,
   684  		Update: bson.D{{"$addToSet", bson.D{{"services", serviceName}}}},
   685  	}
   686  }
   687  
   688  func removeModelServiceRefOp(st *State, serviceName string) txn.Op {
   689  	return txn.Op{
   690  		C:      modelEntityRefsC,
   691  		Id:     st.ModelUUID(),
   692  		Update: bson.D{{"$pull", bson.D{{"services", serviceName}}}},
   693  	}
   694  }
   695  
   696  // checkManualMachines checks if any of the machines in the slice were
   697  // manually provisioned, and are non-manager machines. These machines
   698  // must (currently) be manually destroyed via destroy-machine before
   699  // destroy-model can successfully complete.
   700  func checkManualMachines(machines []*Machine) error {
   701  	var ids []string
   702  	for _, m := range machines {
   703  		if m.IsManager() {
   704  			continue
   705  		}
   706  		manual, err := m.IsManual()
   707  		if err != nil {
   708  			return errors.Trace(err)
   709  		}
   710  		if manual {
   711  			ids = append(ids, m.Id())
   712  		}
   713  	}
   714  	if len(ids) > 0 {
   715  		return errors.Errorf("manually provisioned machines must first be destroyed with `juju destroy-machine %s`", strings.Join(ids, " "))
   716  	}
   717  	return nil
   718  }
   719  
   720  // ensureDestroyable an error if any manual machines or persistent volumes are
   721  // found.
   722  func ensureDestroyable(st *State) error {
   723  
   724  	// TODO(waigani) bug #1475212: Model destroy can miss manual
   725  	// machines. We need to be able to assert the absence of these as
   726  	// part of the destroy txn, but in order to do this  manual machines
   727  	// need to add refcounts to their models.
   728  
   729  	// Check for manual machines. We bail out if there are any,
   730  	// to stop the user from prematurely hobbling the model.
   731  	machines, err := st.AllMachines()
   732  	if err != nil {
   733  		return errors.Trace(err)
   734  	}
   735  
   736  	if err := checkManualMachines(machines); err != nil {
   737  		return errors.Trace(err)
   738  	}
   739  
   740  	return nil
   741  }
   742  
   743  // createModelOp returns the operation needed to create
   744  // an model document with the given name and UUID.
   745  func createModelOp(st *State, owner names.UserTag, name, uuid, server string, mode MigrationMode) txn.Op {
   746  	doc := &modelDoc{
   747  		UUID:          uuid,
   748  		Name:          name,
   749  		Life:          Alive,
   750  		Owner:         owner.Canonical(),
   751  		ServerUUID:    server,
   752  		MigrationMode: mode,
   753  	}
   754  	return txn.Op{
   755  		C:      modelsC,
   756  		Id:     uuid,
   757  		Assert: txn.DocMissing,
   758  		Insert: doc,
   759  	}
   760  }
   761  
   762  func createModelEntityRefsOp(st *State, uuid string) txn.Op {
   763  	return txn.Op{
   764  		C:      modelEntityRefsC,
   765  		Id:     uuid,
   766  		Assert: txn.DocMissing,
   767  		Insert: &modelEntityRefsDoc{UUID: uuid},
   768  	}
   769  }
   770  
   771  const hostedModelCountKey = "hostedModelCount"
   772  
   773  type hostedModelCountDoc struct {
   774  	// RefCount is the number of models in the Juju system.
   775  	// We do not count the system model.
   776  	RefCount int `bson:"refcount"`
   777  }
   778  
   779  func assertNoHostedModelsOp() txn.Op {
   780  	return assertHostedModelsOp(0)
   781  }
   782  
   783  func assertHostedModelsOp(n int) txn.Op {
   784  	return txn.Op{
   785  		C:      controllersC,
   786  		Id:     hostedModelCountKey,
   787  		Assert: bson.D{{"refcount", n}},
   788  	}
   789  }
   790  
   791  func incHostedModelCountOp() txn.Op {
   792  	return HostedModelCountOp(1)
   793  }
   794  
   795  func decHostedModelCountOp() txn.Op {
   796  	return HostedModelCountOp(-1)
   797  }
   798  
   799  func HostedModelCountOp(amount int) txn.Op {
   800  	return txn.Op{
   801  		C:      controllersC,
   802  		Id:     hostedModelCountKey,
   803  		Assert: txn.DocExists,
   804  		Update: bson.M{
   805  			"$inc": bson.M{"refcount": amount},
   806  		},
   807  	}
   808  }
   809  
   810  func hostedModelCount(st *State) (int, error) {
   811  	var doc hostedModelCountDoc
   812  	controllers, closer := st.getCollection(controllersC)
   813  	defer closer()
   814  
   815  	if err := controllers.Find(bson.D{{"_id", hostedModelCountKey}}).One(&doc); err != nil {
   816  		return 0, errors.Trace(err)
   817  	}
   818  	return doc.RefCount, nil
   819  }
   820  
   821  // createUniqueOwnerModelNameOp returns the operation needed to create
   822  // an usermodelnameC document with the given owner and model name.
   823  func createUniqueOwnerModelNameOp(owner names.UserTag, envName string) txn.Op {
   824  	return txn.Op{
   825  		C:      usermodelnameC,
   826  		Id:     userModelNameIndex(owner.Canonical(), envName),
   827  		Assert: txn.DocMissing,
   828  		Insert: bson.M{},
   829  	}
   830  }
   831  
   832  // assertAliveOp returns a txn.Op that asserts the model is alive.
   833  func (m *Model) assertActiveOp() txn.Op {
   834  	return assertModelActiveOp(m.UUID())
   835  }
   836  
   837  // getState returns the State for the model, and a function to close
   838  // it when done. If m.st is the model-specific state, then it will
   839  // be returned and the close function will be a no-op.
   840  //
   841  // TODO(axw) 2016-04-14 #1570269
   842  // find a way to guarantee that every Model is associated with the
   843  // appropriate State. The current work-around is too easy to get wrong.
   844  func (m *Model) getState() (*State, func(), error) {
   845  	if m.st.modelTag == m.ModelTag() {
   846  		return m.st, func() {}, nil
   847  	}
   848  	st, err := m.st.ForModel(m.ModelTag())
   849  	if err != nil {
   850  		return nil, nil, errors.Trace(err)
   851  	}
   852  	uuid := st.ModelUUID()
   853  	return st, func() {
   854  		if err := st.Close(); err != nil {
   855  			logger.Errorf("closing temporary state for model %s", uuid)
   856  		}
   857  	}, nil
   858  }
   859  
   860  // assertModelActiveOp returns a txn.Op that asserts the given
   861  // model UUID refers to an Alive model.
   862  func assertModelActiveOp(modelUUID string) txn.Op {
   863  	return txn.Op{
   864  		C:      modelsC,
   865  		Id:     modelUUID,
   866  		Assert: append(isAliveDoc, bson.DocElem{"migration-mode", MigrationModeActive}),
   867  	}
   868  }
   869  
   870  func checkModelActive(st *State) error {
   871  	model, err := st.Model()
   872  	if (err == nil && model.Life() != Alive) || errors.IsNotFound(err) {
   873  		return errors.Errorf("model %q is no longer alive", model.Name())
   874  	} else if err != nil {
   875  		return errors.Annotate(err, "unable to read model")
   876  	} else if mode := model.MigrationMode(); mode != MigrationModeActive {
   877  		return errors.Errorf("model %q is being migrated", model.Name())
   878  	}
   879  	return nil
   880  }