github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/state/state.go (about)

     1  // Copyright 2012-2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Package state enables reading, observing, and changing
     5  // the state stored in MongoDB of a whole model
     6  // managed by juju.
     7  package state
     8  
     9  import (
    10  	"fmt"
    11  	"regexp"
    12  	"sort"
    13  	"strconv"
    14  	"strings"
    15  	"sync"
    16  	"time"
    17  
    18  	"github.com/juju/errors"
    19  	"github.com/juju/loggo"
    20  	"github.com/juju/names"
    21  	jujutxn "github.com/juju/txn"
    22  	"github.com/juju/utils"
    23  	"github.com/juju/utils/os"
    24  	"github.com/juju/utils/series"
    25  	"github.com/juju/utils/set"
    26  	"github.com/juju/version"
    27  	"gopkg.in/juju/charm.v6-unstable"
    28  	csparams "gopkg.in/juju/charmrepo.v2-unstable/csclient/params"
    29  	"gopkg.in/mgo.v2"
    30  	"gopkg.in/mgo.v2/bson"
    31  	"gopkg.in/mgo.v2/txn"
    32  
    33  	"github.com/juju/juju/apiserver/params"
    34  	"github.com/juju/juju/constraints"
    35  	corelease "github.com/juju/juju/core/lease"
    36  	"github.com/juju/juju/environs/config"
    37  	"github.com/juju/juju/instance"
    38  	"github.com/juju/juju/mongo"
    39  	"github.com/juju/juju/network"
    40  	"github.com/juju/juju/state/cloudimagemetadata"
    41  	statelease "github.com/juju/juju/state/lease"
    42  	"github.com/juju/juju/state/presence"
    43  	"github.com/juju/juju/state/watcher"
    44  	"github.com/juju/juju/status"
    45  	jujuversion "github.com/juju/juju/version"
    46  	"github.com/juju/juju/worker/lease"
    47  )
    48  
    49  var logger = loggo.GetLogger("juju.state")
    50  
    51  const (
    52  	// jujuDB is the name of the main juju database.
    53  	jujuDB = "juju"
    54  
    55  	// presenceDB is the name of the database used to hold presence pinger data.
    56  	presenceDB = "presence"
    57  	presenceC  = "presence"
    58  
    59  	// blobstoreDB is the name of the blobstore GridFS database.
    60  	blobstoreDB = "blobstore"
    61  
    62  	// serviceLeadershipNamespace is the name of the lease.Client namespace
    63  	// used by the leadership manager.
    64  	serviceLeadershipNamespace = "service-leadership"
    65  
    66  	// singularControllerNamespace is the name of the lease.Client namespace
    67  	// used by the singular manager
    68  	singularControllerNamespace = "singular-controller"
    69  )
    70  
    71  // State represents the state of an model
    72  // managed by juju.
    73  type State struct {
    74  	modelTag      names.ModelTag
    75  	controllerTag names.ModelTag
    76  	mongoInfo     *mongo.MongoInfo
    77  	mongoDialOpts mongo.DialOpts
    78  	session       *mgo.Session
    79  	database      Database
    80  	policy        Policy
    81  
    82  	// TODO(fwereade): move these out of state and make them independent
    83  	// workers on which state depends.
    84  	watcher  *watcher.Watcher
    85  	pwatcher *presence.Watcher
    86  	// leadershipManager keeps track of units' service leadership leases
    87  	// within this environment.
    88  	leadershipClient  corelease.Client
    89  	leadershipManager *lease.Manager
    90  	// singularManager keeps track of which controller machine is responsible
    91  	// for managing this state's environment.
    92  	singularManager *lease.Manager
    93  
    94  	// mu guards allManager, allModelManager & allModelWatcherBacking
    95  	mu                     sync.Mutex
    96  	allManager             *storeManager
    97  	allModelManager        *storeManager
    98  	allModelWatcherBacking Backing
    99  
   100  	// TODO(anastasiamac 2015-07-16) As state gets broken up, remove this.
   101  	CloudImageMetadataStorage cloudimagemetadata.Storage
   102  }
   103  
   104  // StateServingInfo holds information needed by a controller.
   105  // This type is a copy of the type of the same name from the api/params package.
   106  // It is replicated here to avoid the state pacakge depending on api/params.
   107  type StateServingInfo struct {
   108  	APIPort      int
   109  	StatePort    int
   110  	Cert         string
   111  	PrivateKey   string
   112  	CAPrivateKey string
   113  	// this will be passed as the KeyFile argument to MongoDB
   114  	SharedSecret   string
   115  	SystemIdentity string
   116  }
   117  
   118  // IsController returns true if this state instance has the bootstrap
   119  // model UUID.
   120  func (st *State) IsController() bool {
   121  	return st.modelTag == st.controllerTag
   122  }
   123  
   124  // RemoveAllModelDocs removes all documents from multi-model
   125  // collections. The model should be put into a dying state before call
   126  // this method. Otherwise, there is a race condition in which collections
   127  // could be added to during or after the running of this method.
   128  func (st *State) RemoveAllModelDocs() error {
   129  	return st.removeAllModelDocs(bson.D{{"life", Dead}})
   130  }
   131  
   132  // RemoveImportingModelDocs removes all documents from multi-model collections
   133  // for the current model. This method asserts that the model's migration mode
   134  // is "importing".
   135  func (st *State) RemoveImportingModelDocs() error {
   136  	return st.removeAllModelDocs(bson.D{{"migration-mode", MigrationModeImporting}})
   137  }
   138  
   139  func (st *State) removeAllModelDocs(modelAssertion bson.D) error {
   140  	env, err := st.Model()
   141  	if err != nil {
   142  		return errors.Trace(err)
   143  	}
   144  	id := userModelNameIndex(env.Owner().Canonical(), env.Name())
   145  	ops := []txn.Op{{
   146  		// Cleanup the owner:envName unique key.
   147  		C:      usermodelnameC,
   148  		Id:     id,
   149  		Remove: true,
   150  	}, {
   151  		C:      modelEntityRefsC,
   152  		Id:     st.ModelUUID(),
   153  		Remove: true,
   154  	}, {
   155  		C:      modelsC,
   156  		Id:     st.ModelUUID(),
   157  		Assert: modelAssertion,
   158  		Remove: true,
   159  	}}
   160  	if !st.IsController() {
   161  		ops = append(ops, decHostedModelCountOp())
   162  	}
   163  
   164  	// Add all per-model docs to the txn.
   165  	for name, info := range st.database.Schema() {
   166  		if info.global {
   167  			continue
   168  		}
   169  		coll, closer := st.getCollection(name)
   170  		defer closer()
   171  
   172  		var ids []bson.M
   173  		err := coll.Find(nil).Select(bson.D{{"_id", 1}}).All(&ids)
   174  		if err != nil {
   175  			return errors.Trace(err)
   176  		}
   177  		for _, id := range ids {
   178  			if info.rawAccess {
   179  				if err := coll.Writeable().RemoveId(id["_id"]); err != nil {
   180  					return errors.Trace(err)
   181  				}
   182  			} else {
   183  				ops = append(ops, txn.Op{
   184  					C:      name,
   185  					Id:     id["_id"],
   186  					Remove: true,
   187  				})
   188  			}
   189  		}
   190  	}
   191  
   192  	return st.runTransaction(ops)
   193  }
   194  
   195  // ForModel returns a connection to mongo for the specified model. The
   196  // connection uses the same credentials and policy as the existing connection.
   197  func (st *State) ForModel(env names.ModelTag) (*State, error) {
   198  	newState, err := open(env, st.mongoInfo, st.mongoDialOpts, st.policy)
   199  	if err != nil {
   200  		return nil, errors.Trace(err)
   201  	}
   202  	if err := newState.start(st.controllerTag); err != nil {
   203  		return nil, errors.Trace(err)
   204  	}
   205  	return newState, nil
   206  }
   207  
   208  // start starts the presence watcher, leadership manager and images metadata storage,
   209  // and fills in the controllerTag field with the supplied value.
   210  func (st *State) start(controllerTag names.ModelTag) error {
   211  	st.controllerTag = controllerTag
   212  
   213  	var clientId string
   214  	if identity := st.mongoInfo.Tag; identity != nil {
   215  		// TODO(fwereade): it feels a bit wrong to take this from MongoInfo -- I
   216  		// think it's just coincidental that the mongodb user happens to map to
   217  		// the machine that's executing the code -- but there doesn't seem to be
   218  		// an accessible alternative.
   219  		clientId = identity.String()
   220  	} else {
   221  		// If we're running state anonymously, we can still use the lease
   222  		// manager; but we need to make sure we use a unique client ID, and
   223  		// will thus not be very performant.
   224  		logger.Infof("running state anonymously; using unique client id")
   225  		uuid, err := utils.NewUUID()
   226  		if err != nil {
   227  			return errors.Trace(err)
   228  		}
   229  		clientId = fmt.Sprintf("anon-%s", uuid.String())
   230  	}
   231  
   232  	logger.Infof("creating lease clients as %s", clientId)
   233  	clock := GetClock()
   234  	datastore := &environMongo{st}
   235  	leadershipClient, err := statelease.NewClient(statelease.ClientConfig{
   236  		Id:         clientId,
   237  		Namespace:  serviceLeadershipNamespace,
   238  		Collection: leasesC,
   239  		Mongo:      datastore,
   240  		Clock:      clock,
   241  	})
   242  	if err != nil {
   243  		return errors.Annotatef(err, "cannot create leadership lease client")
   244  	}
   245  	st.leadershipClient = leadershipClient
   246  	logger.Infof("starting leadership lease manager")
   247  	leadershipManager, err := lease.NewManager(lease.ManagerConfig{
   248  		Secretary: leadershipSecretary{},
   249  		Client:    leadershipClient,
   250  		Clock:     clock,
   251  		MaxSleep:  time.Minute,
   252  	})
   253  	if err != nil {
   254  		return errors.Annotatef(err, "cannot create leadership lease manager")
   255  	}
   256  	st.leadershipManager = leadershipManager
   257  
   258  	singularClient, err := statelease.NewClient(statelease.ClientConfig{
   259  		Id:         clientId,
   260  		Namespace:  singularControllerNamespace,
   261  		Collection: leasesC,
   262  		Mongo:      datastore,
   263  		Clock:      clock,
   264  	})
   265  	if err != nil {
   266  		return errors.Annotatef(err, "cannot create singular lease client")
   267  	}
   268  	logger.Infof("starting singular lease manager")
   269  	singularManager, err := lease.NewManager(lease.ManagerConfig{
   270  		Secretary: singularSecretary{st.modelTag.Id()},
   271  		Client:    singularClient,
   272  		Clock:     clock,
   273  		MaxSleep:  time.Minute,
   274  	})
   275  	if err != nil {
   276  		return errors.Annotatef(err, "cannot create singular lease manager")
   277  	}
   278  	st.singularManager = singularManager
   279  
   280  	logger.Infof("creating cloud image metadata storage")
   281  	st.CloudImageMetadataStorage = cloudimagemetadata.NewStorage(st.ModelUUID(), cloudimagemetadataC, datastore)
   282  
   283  	logger.Infof("starting presence watcher")
   284  	st.pwatcher = presence.NewWatcher(st.getPresence(), st.modelTag)
   285  	return nil
   286  }
   287  
   288  // ModelTag() returns the model tag for the model controlled by
   289  // this state instance.
   290  func (st *State) ModelTag() names.ModelTag {
   291  	return st.modelTag
   292  }
   293  
   294  // ModelUUID returns the model UUID for the model
   295  // controlled by this state instance.
   296  func (st *State) ModelUUID() string {
   297  	return st.modelTag.Id()
   298  }
   299  
   300  // userModelNameIndex returns a string to be used as a usermodelnameC unique index.
   301  func userModelNameIndex(username, envName string) string {
   302  	return strings.ToLower(username) + ":" + envName
   303  }
   304  
   305  // EnsureModelRemoved returns an error if any multi-model
   306  // documents for this model are found. It is intended only to be used in
   307  // tests and exported so it can be used in the tests of other packages.
   308  func (st *State) EnsureModelRemoved() error {
   309  	found := map[string]int{}
   310  	var foundOrdered []string
   311  	for name, info := range st.database.Schema() {
   312  		if info.global {
   313  			continue
   314  		}
   315  		coll, closer := st.getCollection(name)
   316  		defer closer()
   317  		n, err := coll.Find(nil).Count()
   318  		if err != nil {
   319  			return errors.Trace(err)
   320  		}
   321  		if n != 0 {
   322  			found[name] = n
   323  			foundOrdered = append(foundOrdered, name)
   324  		}
   325  	}
   326  
   327  	if len(found) != 0 {
   328  		errMessage := fmt.Sprintf("found documents for model with uuid %s:", st.ModelUUID())
   329  		sort.Strings(foundOrdered)
   330  		for _, name := range foundOrdered {
   331  			number := found[name]
   332  			errMessage += fmt.Sprintf(" %d %s doc,", number, name)
   333  		}
   334  		// Remove trailing comma.
   335  		errMessage = errMessage[:len(errMessage)-1]
   336  		return errors.New(errMessage)
   337  	}
   338  	return nil
   339  }
   340  
   341  // getPresence returns the presence m.
   342  func (st *State) getPresence() *mgo.Collection {
   343  	return st.session.DB(presenceDB).C(presenceC)
   344  }
   345  
   346  // newDB returns a database connection using a new session, along with
   347  // a closer function for the session. This is useful where you need to work
   348  // with various collections in a single session, so don't want to call
   349  // getCollection multiple times.
   350  func (st *State) newDB() (Database, func()) {
   351  	return st.database.CopySession()
   352  }
   353  
   354  // Ping probes the state's database connection to ensure
   355  // that it is still alive.
   356  func (st *State) Ping() error {
   357  	return st.session.Ping()
   358  }
   359  
   360  // MongoVersion return the string repre
   361  func (st *State) MongoVersion() (string, error) {
   362  	binfo, err := st.session.BuildInfo()
   363  	if err != nil {
   364  		return "", errors.Annotate(err, "cannot obtain mongo build info")
   365  	}
   366  	return binfo.Version, nil
   367  }
   368  
   369  // MongoSession returns the underlying mongodb session
   370  // used by the state. It is exposed so that external code
   371  // can maintain the mongo replica set and should not
   372  // otherwise be used.
   373  func (st *State) MongoSession() *mgo.Session {
   374  	return st.session
   375  }
   376  
   377  func (st *State) Watch() *Multiwatcher {
   378  	st.mu.Lock()
   379  	if st.allManager == nil {
   380  		st.allManager = newStoreManager(newAllWatcherStateBacking(st))
   381  	}
   382  	st.mu.Unlock()
   383  	return NewMultiwatcher(st.allManager)
   384  }
   385  
   386  func (st *State) WatchAllModels() *Multiwatcher {
   387  	st.mu.Lock()
   388  	if st.allModelManager == nil {
   389  		st.allModelWatcherBacking = NewAllModelWatcherStateBacking(st)
   390  		st.allModelManager = newStoreManager(st.allModelWatcherBacking)
   391  	}
   392  	st.mu.Unlock()
   393  	return NewMultiwatcher(st.allModelManager)
   394  }
   395  
   396  func (st *State) ModelConfig() (*config.Config, error) {
   397  	settings, err := readSettings(st, modelGlobalKey)
   398  	if err != nil {
   399  		return nil, errors.Trace(err)
   400  	}
   401  	attrs := settings.Map()
   402  	return config.New(config.NoDefaults, attrs)
   403  }
   404  
   405  // checkModelConfig returns an error if the config is definitely invalid.
   406  func checkModelConfig(cfg *config.Config) error {
   407  	if cfg.AdminSecret() != "" {
   408  		return errors.Errorf("admin-secret should never be written to the state")
   409  	}
   410  	if _, ok := cfg.AgentVersion(); !ok {
   411  		return errors.Errorf("agent-version must always be set in state")
   412  	}
   413  	return nil
   414  }
   415  
   416  // versionInconsistentError indicates one or more agents have a
   417  // different version from the current one (even empty, when not yet
   418  // set).
   419  type versionInconsistentError struct {
   420  	currentVersion version.Number
   421  	agents         []string
   422  }
   423  
   424  func (e *versionInconsistentError) Error() string {
   425  	sort.Strings(e.agents)
   426  	return fmt.Sprintf("some agents have not upgraded to the current model version %s: %s", e.currentVersion, strings.Join(e.agents, ", "))
   427  }
   428  
   429  // newVersionInconsistentError returns a new instance of
   430  // versionInconsistentError.
   431  func newVersionInconsistentError(currentVersion version.Number, agents []string) *versionInconsistentError {
   432  	return &versionInconsistentError{currentVersion, agents}
   433  }
   434  
   435  // IsVersionInconsistentError returns if the given error is
   436  // versionInconsistentError.
   437  func IsVersionInconsistentError(e interface{}) bool {
   438  	value := e
   439  	// In case of a wrapped error, check the cause first.
   440  	cause := errors.Cause(e.(error))
   441  	if cause != nil {
   442  		value = cause
   443  	}
   444  	_, ok := value.(*versionInconsistentError)
   445  	return ok
   446  }
   447  
   448  func (st *State) checkCanUpgrade(currentVersion, newVersion string) error {
   449  	matchCurrent := "^" + regexp.QuoteMeta(currentVersion) + "-"
   450  	matchNew := "^" + regexp.QuoteMeta(newVersion) + "-"
   451  	// Get all machines and units with a different or empty version.
   452  	sel := bson.D{{"$or", []bson.D{
   453  		{{"tools", bson.D{{"$exists", false}}}},
   454  		{{"$and", []bson.D{
   455  			{{"tools.version", bson.D{{"$not", bson.RegEx{matchCurrent, ""}}}}},
   456  			{{"tools.version", bson.D{{"$not", bson.RegEx{matchNew, ""}}}}},
   457  		}}},
   458  	}}}
   459  	var agentTags []string
   460  	for _, name := range []string{machinesC, unitsC} {
   461  		collection, closer := st.getCollection(name)
   462  		defer closer()
   463  		var doc struct {
   464  			DocID string `bson:"_id"`
   465  		}
   466  		iter := collection.Find(sel).Select(bson.D{{"_id", 1}}).Iter()
   467  		for iter.Next(&doc) {
   468  			localID, err := st.strictLocalID(doc.DocID)
   469  			if err != nil {
   470  				return errors.Trace(err)
   471  			}
   472  			switch name {
   473  			case machinesC:
   474  				agentTags = append(agentTags, names.NewMachineTag(localID).String())
   475  			case unitsC:
   476  				agentTags = append(agentTags, names.NewUnitTag(localID).String())
   477  			}
   478  		}
   479  		if err := iter.Close(); err != nil {
   480  			return errors.Trace(err)
   481  		}
   482  	}
   483  	if len(agentTags) > 0 {
   484  		err := newVersionInconsistentError(version.MustParse(currentVersion), agentTags)
   485  		return errors.Trace(err)
   486  	}
   487  	return nil
   488  }
   489  
   490  var errUpgradeInProgress = errors.New(params.CodeUpgradeInProgress)
   491  
   492  // IsUpgradeInProgressError returns true if the error is cause by an
   493  // upgrade in progress
   494  func IsUpgradeInProgressError(err error) bool {
   495  	return errors.Cause(err) == errUpgradeInProgress
   496  }
   497  
   498  // SetModelAgentVersion changes the agent version for the model to the
   499  // given version, only if the model is in a stable state (all agents are
   500  // running the current version). If this is a hosted model, newVersion
   501  // cannot be higher than the controller version.
   502  func (st *State) SetModelAgentVersion(newVersion version.Number) (err error) {
   503  	if newVersion.Compare(jujuversion.Current) > 0 && !st.IsController() {
   504  		return errors.Errorf("a hosted model cannot have a higher version than the server model: %s > %s",
   505  			newVersion.String(),
   506  			jujuversion.Current,
   507  		)
   508  	}
   509  
   510  	buildTxn := func(attempt int) ([]txn.Op, error) {
   511  		settings, err := readSettings(st, modelGlobalKey)
   512  		if err != nil {
   513  			return nil, errors.Trace(err)
   514  		}
   515  		agentVersion, ok := settings.Get("agent-version")
   516  		if !ok {
   517  			return nil, errors.Errorf("no agent version set in the model")
   518  		}
   519  		currentVersion, ok := agentVersion.(string)
   520  		if !ok {
   521  			return nil, errors.Errorf("invalid agent version format: expected string, got %v", agentVersion)
   522  		}
   523  		if newVersion.String() == currentVersion {
   524  			// Nothing to do.
   525  			return nil, jujutxn.ErrNoOperations
   526  		}
   527  
   528  		if err := st.checkCanUpgrade(currentVersion, newVersion.String()); err != nil {
   529  			return nil, errors.Trace(err)
   530  		}
   531  
   532  		ops := []txn.Op{
   533  			// Can't set agent-version if there's an active upgradeInfo doc.
   534  			{
   535  				C:      upgradeInfoC,
   536  				Id:     currentUpgradeId,
   537  				Assert: txn.DocMissing,
   538  			}, {
   539  				C:      settingsC,
   540  				Id:     st.docID(modelGlobalKey),
   541  				Assert: bson.D{{"version", settings.version}},
   542  				Update: bson.D{
   543  					{"$set", bson.D{{"settings.agent-version", newVersion.String()}}},
   544  				},
   545  			},
   546  		}
   547  		return ops, nil
   548  	}
   549  	if err = st.run(buildTxn); err == jujutxn.ErrExcessiveContention {
   550  		// Although there is a small chance of a race here, try to
   551  		// return a more helpful error message in the case of an
   552  		// active upgradeInfo document being in place.
   553  		if upgrading, _ := st.IsUpgrading(); upgrading {
   554  			err = errUpgradeInProgress
   555  		} else {
   556  			err = errors.Annotate(err, "cannot set agent version")
   557  		}
   558  	}
   559  	return errors.Trace(err)
   560  }
   561  
   562  func (st *State) buildAndValidateModelConfig(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) (validCfg *config.Config, err error) {
   563  	newConfig, err := oldConfig.Apply(updateAttrs)
   564  	if err != nil {
   565  		return nil, errors.Trace(err)
   566  	}
   567  	if len(removeAttrs) != 0 {
   568  		newConfig, err = newConfig.Remove(removeAttrs)
   569  		if err != nil {
   570  			return nil, errors.Trace(err)
   571  		}
   572  	}
   573  	if err := checkModelConfig(newConfig); err != nil {
   574  		return nil, errors.Trace(err)
   575  	}
   576  	return st.validate(newConfig, oldConfig)
   577  }
   578  
   579  type ValidateConfigFunc func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error
   580  
   581  // UpdateModelConfig adds, updates or removes attributes in the current
   582  // configuration of the model with the provided updateAttrs and
   583  // removeAttrs.
   584  func (st *State) UpdateModelConfig(updateAttrs map[string]interface{}, removeAttrs []string, additionalValidation ValidateConfigFunc) error {
   585  	if len(updateAttrs)+len(removeAttrs) == 0 {
   586  		return nil
   587  	}
   588  
   589  	// TODO(axw) 2013-12-6 #1167616
   590  	// Ensure that the settings on disk have not changed
   591  	// underneath us. The settings changes are actually
   592  	// applied as a delta to what's on disk; if there has
   593  	// been a concurrent update, the change may not be what
   594  	// the user asked for.
   595  	settings, err := readSettings(st, modelGlobalKey)
   596  	if err != nil {
   597  		return errors.Trace(err)
   598  	}
   599  
   600  	// Get the existing model config from state.
   601  	oldConfig, err := config.New(config.NoDefaults, settings.Map())
   602  	if err != nil {
   603  		return errors.Trace(err)
   604  	}
   605  	if additionalValidation != nil {
   606  		err = additionalValidation(updateAttrs, removeAttrs, oldConfig)
   607  		if err != nil {
   608  			return errors.Trace(err)
   609  		}
   610  	}
   611  	validCfg, err := st.buildAndValidateModelConfig(updateAttrs, removeAttrs, oldConfig)
   612  	if err != nil {
   613  		return errors.Trace(err)
   614  	}
   615  
   616  	validAttrs := validCfg.AllAttrs()
   617  	for k := range oldConfig.AllAttrs() {
   618  		if _, ok := validAttrs[k]; !ok {
   619  			settings.Delete(k)
   620  		}
   621  	}
   622  	settings.Update(validAttrs)
   623  	_, err = settings.Write()
   624  	return errors.Trace(err)
   625  }
   626  
   627  // ModelConstraints returns the current model constraints.
   628  func (st *State) ModelConstraints() (constraints.Value, error) {
   629  	cons, err := readConstraints(st, modelGlobalKey)
   630  	return cons, errors.Trace(err)
   631  }
   632  
   633  // SetModelConstraints replaces the current model constraints.
   634  func (st *State) SetModelConstraints(cons constraints.Value) error {
   635  	unsupported, err := st.validateConstraints(cons)
   636  	if len(unsupported) > 0 {
   637  		logger.Warningf(
   638  			"setting model constraints: unsupported constraints: %v", strings.Join(unsupported, ","))
   639  	} else if err != nil {
   640  		return errors.Trace(err)
   641  	}
   642  	return writeConstraints(st, modelGlobalKey, cons)
   643  }
   644  
   645  // AllMachines returns all machines in the model
   646  // ordered by id.
   647  func (st *State) AllMachines() (machines []*Machine, err error) {
   648  	machinesCollection, closer := st.getCollection(machinesC)
   649  	defer closer()
   650  
   651  	mdocs := machineDocSlice{}
   652  	err = machinesCollection.Find(nil).All(&mdocs)
   653  	if err != nil {
   654  		return nil, errors.Annotatef(err, "cannot get all machines")
   655  	}
   656  	sort.Sort(mdocs)
   657  	for _, doc := range mdocs {
   658  		machines = append(machines, newMachine(st, &doc))
   659  	}
   660  	return
   661  }
   662  
   663  type machineDocSlice []machineDoc
   664  
   665  func (ms machineDocSlice) Len() int      { return len(ms) }
   666  func (ms machineDocSlice) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }
   667  func (ms machineDocSlice) Less(i, j int) bool {
   668  	return machineIdLessThan(ms[i].Id, ms[j].Id)
   669  }
   670  
   671  // machineIdLessThan returns true if id1 < id2, false otherwise.
   672  // Machine ids may include "/" separators if they are for a container so
   673  // the comparison is done by comparing the id component values from
   674  // left to right (most significant part to least significant). Ids for
   675  // host machines are always less than ids for their containers.
   676  func machineIdLessThan(id1, id2 string) bool {
   677  	// Most times, we are dealing with host machines and not containers, so we will
   678  	// try interpreting the ids as ints - this will be faster than dealing with the
   679  	// container ids below.
   680  	mint1, err1 := strconv.Atoi(id1)
   681  	mint2, err2 := strconv.Atoi(id2)
   682  	if err1 == nil && err2 == nil {
   683  		return mint1 < mint2
   684  	}
   685  	// We have at least one container id so it gets complicated.
   686  	idParts1 := strings.Split(id1, "/")
   687  	idParts2 := strings.Split(id2, "/")
   688  	nrParts1 := len(idParts1)
   689  	nrParts2 := len(idParts2)
   690  	minLen := nrParts1
   691  	if nrParts2 < minLen {
   692  		minLen = nrParts2
   693  	}
   694  	for x := 0; x < minLen; x++ {
   695  		m1 := idParts1[x]
   696  		m2 := idParts2[x]
   697  		if m1 == m2 {
   698  			continue
   699  		}
   700  		// See if the id part is a container type, and if so compare directly.
   701  		if x%2 == 1 {
   702  			return m1 < m2
   703  		}
   704  		// Compare the integer ids.
   705  		// There's nothing we can do with errors at this point.
   706  		mint1, _ := strconv.Atoi(m1)
   707  		mint2, _ := strconv.Atoi(m2)
   708  		return mint1 < mint2
   709  	}
   710  	return nrParts1 < nrParts2
   711  }
   712  
   713  // Machine returns the machine with the given id.
   714  func (st *State) Machine(id string) (*Machine, error) {
   715  	mdoc, err := st.getMachineDoc(id)
   716  	if err != nil {
   717  		return nil, err
   718  	}
   719  	return newMachine(st, mdoc), nil
   720  }
   721  
   722  func (st *State) getMachineDoc(id string) (*machineDoc, error) {
   723  	machinesCollection, closer := st.getRawCollection(machinesC)
   724  	defer closer()
   725  
   726  	var err error
   727  	mdoc := &machineDoc{}
   728  	for _, tryId := range []string{st.docID(id), id} {
   729  		err = machinesCollection.FindId(tryId).One(mdoc)
   730  		if err != mgo.ErrNotFound {
   731  			break
   732  		}
   733  	}
   734  	switch err {
   735  	case nil:
   736  		// This is required to allow loading of machines before the
   737  		// model UUID migration has been applied to the machines
   738  		// collection. Without this, a machine agent can't come up to
   739  		// run the database migration.
   740  		if mdoc.Id == "" {
   741  			mdoc.Id = mdoc.DocID
   742  		}
   743  		return mdoc, nil
   744  	case mgo.ErrNotFound:
   745  		return nil, errors.NotFoundf("machine %s", id)
   746  	default:
   747  		return nil, errors.Annotatef(err, "cannot get machine %s", id)
   748  	}
   749  }
   750  
   751  // FindEntity returns the entity with the given tag.
   752  //
   753  // The returned value can be of type *Machine, *Unit,
   754  // *User, *Service, *Model, or *Action, depending
   755  // on the tag.
   756  func (st *State) FindEntity(tag names.Tag) (Entity, error) {
   757  	id := tag.Id()
   758  	switch tag := tag.(type) {
   759  	case names.MachineTag:
   760  		return st.Machine(id)
   761  	case names.UnitTag:
   762  		return st.Unit(id)
   763  	case names.UserTag:
   764  		return st.User(tag)
   765  	case names.ServiceTag:
   766  		return st.Service(id)
   767  	case names.ModelTag:
   768  		env, err := st.Model()
   769  		if err != nil {
   770  			return nil, errors.Trace(err)
   771  		}
   772  		// Return an invalid entity error if the requested model is not
   773  		// the current one.
   774  		if id != env.UUID() {
   775  			if utils.IsValidUUIDString(id) {
   776  				return nil, errors.NotFoundf("model %q", id)
   777  			}
   778  			// TODO(axw) 2013-12-04 #1257587
   779  			// We should not accept model tags that do not match the
   780  			// model's UUID. We accept anything for now, to cater
   781  			// both for past usage, and for potentially supporting aliases.
   782  			logger.Warningf("model-tag does not match current model UUID: %q != %q", id, env.UUID())
   783  			conf, err := st.ModelConfig()
   784  			if err != nil {
   785  				logger.Warningf("ModelConfig failed: %v", err)
   786  			} else if id != conf.Name() {
   787  				logger.Warningf("model-tag does not match current model name: %q != %q", id, conf.Name())
   788  			}
   789  		}
   790  		return env, nil
   791  	case names.RelationTag:
   792  		return st.KeyRelation(id)
   793  	case names.IPAddressTag:
   794  		return st.IPAddressByTag(tag)
   795  	case names.ActionTag:
   796  		return st.ActionByTag(tag)
   797  	case names.CharmTag:
   798  		if url, err := charm.ParseURL(id); err != nil {
   799  			logger.Warningf("Parsing charm URL %q failed: %v", id, err)
   800  			return nil, errors.NotFoundf("could not find charm %q in state", id)
   801  		} else {
   802  			return st.Charm(url)
   803  		}
   804  	case names.VolumeTag:
   805  		return st.Volume(tag)
   806  	case names.FilesystemTag:
   807  		return st.Filesystem(tag)
   808  	default:
   809  		return nil, errors.Errorf("unsupported tag %T", tag)
   810  	}
   811  }
   812  
   813  // tagToCollectionAndId, given an entity tag, returns the collection name and id
   814  // of the entity document.
   815  func (st *State) tagToCollectionAndId(tag names.Tag) (string, interface{}, error) {
   816  	if tag == nil {
   817  		return "", nil, errors.Errorf("tag is nil")
   818  	}
   819  	coll := ""
   820  	id := tag.Id()
   821  	switch tag := tag.(type) {
   822  	case names.MachineTag:
   823  		coll = machinesC
   824  		id = st.docID(id)
   825  	case names.ServiceTag:
   826  		coll = servicesC
   827  		id = st.docID(id)
   828  	case names.UnitTag:
   829  		coll = unitsC
   830  		id = st.docID(id)
   831  	case names.UserTag:
   832  		coll = usersC
   833  		if !tag.IsLocal() {
   834  			return "", nil, fmt.Errorf("%q is not a local user", tag.Canonical())
   835  		}
   836  		id = tag.Name()
   837  	case names.RelationTag:
   838  		coll = relationsC
   839  		id = st.docID(id)
   840  	case names.ModelTag:
   841  		coll = modelsC
   842  	case names.ActionTag:
   843  		coll = actionsC
   844  		id = tag.Id()
   845  	case names.CharmTag:
   846  		coll = charmsC
   847  		id = tag.Id()
   848  	default:
   849  		return "", nil, errors.Errorf("%q is not a valid collection tag", tag)
   850  	}
   851  	return coll, id, nil
   852  }
   853  
   854  // AddCharm adds the ch charm with curl to the state.
   855  // On success the newly added charm state is returned.
   856  func (st *State) AddCharm(info CharmInfo) (stch *Charm, err error) {
   857  	charms, closer := st.getCollection(charmsC)
   858  	defer closer()
   859  
   860  	if err := validateCharmVersion(info.Charm); err != nil {
   861  		return nil, errors.Trace(err)
   862  	}
   863  
   864  	query := charms.FindId(info.ID.String()).Select(bson.D{{"placeholder", 1}})
   865  
   866  	buildTxn := func(attempt int) ([]txn.Op, error) {
   867  		var placeholderDoc struct {
   868  			Placeholder bool `bson:"placeholder"`
   869  		}
   870  		if err := query.One(&placeholderDoc); err == mgo.ErrNotFound {
   871  
   872  			return insertCharmOps(st, info)
   873  		} else if err != nil {
   874  			return nil, errors.Trace(err)
   875  		} else if placeholderDoc.Placeholder {
   876  			return updateCharmOps(st, info, stillPlaceholder)
   877  		}
   878  		return nil, errors.AlreadyExistsf("charm %q", info.ID)
   879  	}
   880  	if err = st.run(buildTxn); err == nil {
   881  		return st.Charm(info.ID)
   882  	}
   883  	return nil, errors.Trace(err)
   884  }
   885  
   886  type hasMeta interface {
   887  	Meta() *charm.Meta
   888  }
   889  
   890  func validateCharmVersion(ch hasMeta) error {
   891  	minver := ch.Meta().MinJujuVersion
   892  	if minver != version.Zero {
   893  		if minver.Compare(jujuversion.Current) > 0 {
   894  			return errors.Errorf("Charm's min version (%s) is higher than this juju environment's version (%s)", minver, jujuversion.Current)
   895  		}
   896  	}
   897  	return nil
   898  }
   899  
   900  // AllCharms returns all charms in state.
   901  func (st *State) AllCharms() ([]*Charm, error) {
   902  	charmsCollection, closer := st.getCollection(charmsC)
   903  	defer closer()
   904  	var cdoc charmDoc
   905  	var charms []*Charm
   906  	iter := charmsCollection.Find(nil).Iter()
   907  	for iter.Next(&cdoc) {
   908  		ch := newCharm(st, &cdoc)
   909  		charms = append(charms, ch)
   910  	}
   911  	return charms, errors.Trace(iter.Close())
   912  }
   913  
   914  // Charm returns the charm with the given URL. Charms pending upload
   915  // to storage and placeholders are never returned.
   916  func (st *State) Charm(curl *charm.URL) (*Charm, error) {
   917  	charms, closer := st.getCollection(charmsC)
   918  	defer closer()
   919  
   920  	cdoc := &charmDoc{}
   921  	what := bson.D{
   922  		{"_id", curl.String()},
   923  		{"placeholder", bson.D{{"$ne", true}}},
   924  		{"pendingupload", bson.D{{"$ne", true}}},
   925  	}
   926  	err := charms.Find(what).One(&cdoc)
   927  	if err == mgo.ErrNotFound {
   928  		return nil, errors.NotFoundf("charm %q", curl)
   929  	}
   930  	if err != nil {
   931  		return nil, errors.Annotatef(err, "cannot get charm %q", curl)
   932  	}
   933  	if err := cdoc.Meta.Check(); err != nil {
   934  		return nil, errors.Annotatef(err, "malformed charm metadata found in state")
   935  	}
   936  	return newCharm(st, cdoc), nil
   937  }
   938  
   939  // LatestPlaceholderCharm returns the latest charm described by the
   940  // given URL but which is not yet deployed.
   941  func (st *State) LatestPlaceholderCharm(curl *charm.URL) (*Charm, error) {
   942  	charms, closer := st.getCollection(charmsC)
   943  	defer closer()
   944  
   945  	noRevURL := curl.WithRevision(-1)
   946  	curlRegex := "^" + regexp.QuoteMeta(st.docID(noRevURL.String()))
   947  	var docs []charmDoc
   948  	err := charms.Find(bson.D{{"_id", bson.D{{"$regex", curlRegex}}}, {"placeholder", true}}).All(&docs)
   949  	if err != nil {
   950  		return nil, errors.Annotatef(err, "cannot get charm %q", curl)
   951  	}
   952  	// Find the highest revision.
   953  	var latest charmDoc
   954  	for _, doc := range docs {
   955  		if latest.URL == nil || doc.URL.Revision > latest.URL.Revision {
   956  			latest = doc
   957  		}
   958  	}
   959  	if latest.URL == nil {
   960  		return nil, errors.NotFoundf("placeholder charm %q", noRevURL)
   961  	}
   962  	return newCharm(st, &latest), nil
   963  }
   964  
   965  // PrepareLocalCharmUpload must be called before a local charm is
   966  // uploaded to the provider storage in order to create a charm
   967  // document in state. It returns the chosen unique charm URL reserved
   968  // in state for the charm.
   969  //
   970  // The url's schema must be "local" and it must include a revision.
   971  func (st *State) PrepareLocalCharmUpload(curl *charm.URL) (chosenUrl *charm.URL, err error) {
   972  	// Perform a few sanity checks first.
   973  	if curl.Schema != "local" {
   974  		return nil, errors.Errorf("expected charm URL with local schema, got %q", curl)
   975  	}
   976  	if curl.Revision < 0 {
   977  		return nil, errors.Errorf("expected charm URL with revision, got %q", curl)
   978  	}
   979  	// Get a regex with the charm URL and no revision.
   980  	noRevURL := curl.WithRevision(-1)
   981  	curlRegex := "^" + regexp.QuoteMeta(st.docID(noRevURL.String()))
   982  
   983  	charms, closer := st.getCollection(charmsC)
   984  	defer closer()
   985  
   986  	buildTxn := func(attempt int) ([]txn.Op, error) {
   987  		// Find the highest revision of that charm in state.
   988  		var docs []charmDoc
   989  		query := bson.D{{"_id", bson.D{{"$regex", curlRegex}}}}
   990  		err = charms.Find(query).Select(bson.D{{"_id", 1}, {"url", 1}}).All(&docs)
   991  		if err != nil {
   992  			return nil, errors.Trace(err)
   993  		}
   994  		// Find the highest revision.
   995  		maxRevision := -1
   996  		for _, doc := range docs {
   997  			if doc.URL.Revision > maxRevision {
   998  				maxRevision = doc.URL.Revision
   999  			}
  1000  		}
  1001  
  1002  		// Respect the local charm's revision first.
  1003  		chosenRevision := curl.Revision
  1004  		if maxRevision >= chosenRevision {
  1005  			// More recent revision exists in state, pick the next.
  1006  			chosenRevision = maxRevision + 1
  1007  		}
  1008  		chosenUrl = curl.WithRevision(chosenRevision)
  1009  		return insertPendingCharmOps(st, chosenUrl)
  1010  	}
  1011  	if err = st.run(buildTxn); err == nil {
  1012  		return chosenUrl, nil
  1013  	}
  1014  	return nil, errors.Trace(err)
  1015  }
  1016  
  1017  // PrepareStoreCharmUpload must be called before a charm store charm
  1018  // is uploaded to the provider storage in order to create a charm
  1019  // document in state. If a charm with the same URL is already in
  1020  // state, it will be returned as a *state.Charm (it can be still
  1021  // pending or already uploaded). Otherwise, a new charm document is
  1022  // added in state with just the given charm URL and
  1023  // PendingUpload=true, which is then returned as a *state.Charm.
  1024  //
  1025  // The url's schema must be "cs" and it must include a revision.
  1026  func (st *State) PrepareStoreCharmUpload(curl *charm.URL) (*Charm, error) {
  1027  	// Perform a few sanity checks first.
  1028  	if curl.Schema != "cs" {
  1029  		return nil, errors.Errorf("expected charm URL with cs schema, got %q", curl)
  1030  	}
  1031  	if curl.Revision < 0 {
  1032  		return nil, errors.Errorf("expected charm URL with revision, got %q", curl)
  1033  	}
  1034  
  1035  	charms, closer := st.getCollection(charmsC)
  1036  	defer closer()
  1037  
  1038  	var (
  1039  		uploadedCharm charmDoc
  1040  		err           error
  1041  	)
  1042  	buildTxn := func(attempt int) ([]txn.Op, error) {
  1043  		// Find an uploaded or pending charm with the given exact curl.
  1044  		err := charms.FindId(curl.String()).One(&uploadedCharm)
  1045  		switch {
  1046  		case err == mgo.ErrNotFound:
  1047  			uploadedCharm = charmDoc{
  1048  				DocID:         st.docID(curl.String()),
  1049  				ModelUUID:     st.ModelTag().Id(),
  1050  				URL:           curl,
  1051  				PendingUpload: true,
  1052  			}
  1053  			return insertAnyCharmOps(&uploadedCharm)
  1054  		case err != nil:
  1055  			return nil, errors.Trace(err)
  1056  		case uploadedCharm.Placeholder:
  1057  			// Update the fields of the document we're returning.
  1058  			uploadedCharm.PendingUpload = true
  1059  			uploadedCharm.Placeholder = false
  1060  			return convertPlaceholderCharmOps(uploadedCharm.DocID)
  1061  		default:
  1062  			// The charm exists and it's either uploaded or still
  1063  			// pending, but it's not a placeholder. In any case,
  1064  			// there's nothing to do.
  1065  			return nil, jujutxn.ErrNoOperations
  1066  		}
  1067  	}
  1068  	if err = st.run(buildTxn); err == nil {
  1069  		return newCharm(st, &uploadedCharm), nil
  1070  	}
  1071  	return nil, errors.Trace(err)
  1072  }
  1073  
  1074  var (
  1075  	stillPending     = bson.D{{"pendingupload", true}}
  1076  	stillPlaceholder = bson.D{{"placeholder", true}}
  1077  )
  1078  
  1079  // AddStoreCharmPlaceholder creates a charm document in state for the given charm URL which
  1080  // must reference a charm from the store. The charm document is marked as a placeholder which
  1081  // means that if the charm is to be deployed, it will need to first be uploaded to env storage.
  1082  func (st *State) AddStoreCharmPlaceholder(curl *charm.URL) (err error) {
  1083  	// Perform sanity checks first.
  1084  	if curl.Schema != "cs" {
  1085  		return errors.Errorf("expected charm URL with cs schema, got %q", curl)
  1086  	}
  1087  	if curl.Revision < 0 {
  1088  		return errors.Errorf("expected charm URL with revision, got %q", curl)
  1089  	}
  1090  	charms, closer := st.getCollection(charmsC)
  1091  	defer closer()
  1092  
  1093  	buildTxn := func(attempt int) ([]txn.Op, error) {
  1094  		// See if the charm already exists in state and exit early if that's the case.
  1095  		var doc charmDoc
  1096  		err := charms.Find(bson.D{{"_id", curl.String()}}).Select(bson.D{{"_id", 1}}).One(&doc)
  1097  		if err != nil && err != mgo.ErrNotFound {
  1098  			return nil, errors.Trace(err)
  1099  		}
  1100  		if err == nil {
  1101  			return nil, jujutxn.ErrNoOperations
  1102  		}
  1103  
  1104  		// Delete all previous placeholders so we don't fill up the database with unused data.
  1105  		deleteOps, err := deleteOldPlaceholderCharmsOps(st, charms, curl)
  1106  		if err != nil {
  1107  			return nil, errors.Trace(err)
  1108  		}
  1109  		insertOps, err := insertPlaceholderCharmOps(st, curl)
  1110  		if err != nil {
  1111  			return nil, errors.Trace(err)
  1112  		}
  1113  		ops := append(deleteOps, insertOps...)
  1114  		return ops, nil
  1115  	}
  1116  	return errors.Trace(st.run(buildTxn))
  1117  }
  1118  
  1119  // UpdateUploadedCharm marks the given charm URL as uploaded and
  1120  // updates the rest of its data, returning it as *state.Charm.
  1121  func (st *State) UpdateUploadedCharm(info CharmInfo) (*Charm, error) {
  1122  	charms, closer := st.getCollection(charmsC)
  1123  	defer closer()
  1124  
  1125  	doc := &charmDoc{}
  1126  	err := charms.FindId(info.ID.String()).One(&doc)
  1127  	if err == mgo.ErrNotFound {
  1128  		return nil, errors.NotFoundf("charm %q", info.ID)
  1129  	}
  1130  	if err != nil {
  1131  		return nil, errors.Trace(err)
  1132  	}
  1133  	if !doc.PendingUpload {
  1134  		return nil, errors.Trace(&ErrCharmAlreadyUploaded{info.ID})
  1135  	}
  1136  
  1137  	ops, err := updateCharmOps(st, info, stillPending)
  1138  	if err != nil {
  1139  		return nil, errors.Trace(err)
  1140  	}
  1141  	if err := st.runTransaction(ops); err != nil {
  1142  		return nil, onAbort(err, ErrCharmRevisionAlreadyModified)
  1143  	}
  1144  	return st.Charm(info.ID)
  1145  }
  1146  
  1147  // addPeerRelationsOps returns the operations necessary to add the
  1148  // specified service peer relations to the state.
  1149  func (st *State) addPeerRelationsOps(serviceName string, peers map[string]charm.Relation) ([]txn.Op, error) {
  1150  	var ops []txn.Op
  1151  	for _, rel := range peers {
  1152  		relId, err := st.sequence("relation")
  1153  		if err != nil {
  1154  			return nil, errors.Trace(err)
  1155  		}
  1156  		eps := []Endpoint{{
  1157  			ServiceName: serviceName,
  1158  			Relation:    rel,
  1159  		}}
  1160  		relKey := relationKey(eps)
  1161  		relDoc := &relationDoc{
  1162  			DocID:     st.docID(relKey),
  1163  			Key:       relKey,
  1164  			ModelUUID: st.ModelUUID(),
  1165  			Id:        relId,
  1166  			Endpoints: eps,
  1167  			Life:      Alive,
  1168  		}
  1169  		ops = append(ops, txn.Op{
  1170  			C:      relationsC,
  1171  			Id:     relDoc.DocID,
  1172  			Assert: txn.DocMissing,
  1173  			Insert: relDoc,
  1174  		})
  1175  	}
  1176  	return ops, nil
  1177  }
  1178  
  1179  type AddServiceArgs struct {
  1180  	Name             string
  1181  	Series           string
  1182  	Owner            string
  1183  	Charm            *Charm
  1184  	Channel          csparams.Channel
  1185  	Storage          map[string]StorageConstraints
  1186  	EndpointBindings map[string]string
  1187  	Settings         charm.Settings
  1188  	NumUnits         int
  1189  	Placement        []*instance.Placement
  1190  	Constraints      constraints.Value
  1191  	Resources        map[string]string
  1192  }
  1193  
  1194  // AddService creates a new service, running the supplied charm, with the
  1195  // supplied name (which must be unique). If the charm defines peer relations,
  1196  // they will be created automatically.
  1197  func (st *State) AddService(args AddServiceArgs) (service *Service, err error) {
  1198  	defer errors.DeferredAnnotatef(&err, "cannot add service %q", args.Name)
  1199  	ownerTag, err := names.ParseUserTag(args.Owner)
  1200  	if err != nil {
  1201  		return nil, errors.Annotatef(err, "Invalid ownertag %s", args.Owner)
  1202  	}
  1203  	// Sanity checks.
  1204  	if !names.IsValidService(args.Name) {
  1205  		return nil, errors.Errorf("invalid name")
  1206  	}
  1207  	if args.Charm == nil {
  1208  		return nil, errors.Errorf("charm is nil")
  1209  	}
  1210  
  1211  	if err := validateCharmVersion(args.Charm); err != nil {
  1212  		return nil, errors.Trace(err)
  1213  	}
  1214  
  1215  	if exists, err := isNotDead(st, servicesC, args.Name); err != nil {
  1216  		return nil, errors.Trace(err)
  1217  	} else if exists {
  1218  		return nil, errors.Errorf("service already exists")
  1219  	}
  1220  	if err := checkModelActive(st); err != nil {
  1221  		return nil, errors.Trace(err)
  1222  	}
  1223  	if _, err := st.ModelUser(ownerTag); err != nil {
  1224  		return nil, errors.Trace(err)
  1225  	}
  1226  	if args.Storage == nil {
  1227  		args.Storage = make(map[string]StorageConstraints)
  1228  	}
  1229  	if err := addDefaultStorageConstraints(st, args.Storage, args.Charm.Meta()); err != nil {
  1230  		return nil, errors.Trace(err)
  1231  	}
  1232  	if err := validateStorageConstraints(st, args.Storage, args.Charm.Meta()); err != nil {
  1233  		return nil, errors.Trace(err)
  1234  	}
  1235  	storagePools := make(set.Strings)
  1236  	for _, storageParams := range args.Storage {
  1237  		storagePools.Add(storageParams.Pool)
  1238  	}
  1239  
  1240  	if args.Series == "" {
  1241  		// args.Series is not set, so use the series in the URL.
  1242  		args.Series = args.Charm.URL().Series
  1243  		if args.Series == "" {
  1244  			// Should not happen, but just in case.
  1245  			return nil, errors.New("series is empty")
  1246  		}
  1247  	} else {
  1248  		// User has specified series. Overriding supported series is
  1249  		// handled by the client, so args.Series is not necessarily
  1250  		// one of the charm's supported series. We require that the
  1251  		// specified series is of the same operating system as one of
  1252  		// the supported series. For old-style charms with the series
  1253  		// in the URL, that series is the one and only supported
  1254  		// series.
  1255  		var supportedSeries []string
  1256  		if series := args.Charm.URL().Series; series != "" {
  1257  			supportedSeries = []string{series}
  1258  		} else {
  1259  			supportedSeries = args.Charm.Meta().Series
  1260  		}
  1261  		if len(supportedSeries) > 0 {
  1262  			seriesOS, err := series.GetOSFromSeries(args.Series)
  1263  			if err != nil {
  1264  				return nil, errors.Trace(err)
  1265  			}
  1266  			supportedOperatingSystems := make(map[os.OSType]bool)
  1267  			for _, supportedSeries := range supportedSeries {
  1268  				os, err := series.GetOSFromSeries(supportedSeries)
  1269  				if err != nil {
  1270  					return nil, errors.Trace(err)
  1271  				}
  1272  				supportedOperatingSystems[os] = true
  1273  			}
  1274  			if !supportedOperatingSystems[seriesOS] {
  1275  				return nil, errors.NewNotSupported(errors.Errorf(
  1276  					"series %q (OS %q) not supported by charm, supported series are %q",
  1277  					args.Series, seriesOS, strings.Join(supportedSeries, ", "),
  1278  				), "")
  1279  			}
  1280  		}
  1281  	}
  1282  
  1283  	for _, placement := range args.Placement {
  1284  		data, err := st.parsePlacement(placement)
  1285  		if err != nil {
  1286  			return nil, errors.Trace(err)
  1287  		}
  1288  		switch data.placementType() {
  1289  		case machinePlacement:
  1290  			// Ensure that the machine and charm series match.
  1291  			m, err := st.Machine(data.machineId)
  1292  			if err != nil {
  1293  				return nil, errors.Trace(err)
  1294  			}
  1295  			subordinate := args.Charm.Meta().Subordinate
  1296  			if err := validateUnitMachineAssignment(
  1297  				m, args.Series, subordinate, storagePools,
  1298  			); err != nil {
  1299  				return nil, errors.Annotatef(
  1300  					err, "cannot deploy to machine %s", m,
  1301  				)
  1302  			}
  1303  
  1304  		case directivePlacement:
  1305  			if err := st.precheckInstance(args.Series, args.Constraints, data.directive); err != nil {
  1306  				return nil, errors.Trace(err)
  1307  			}
  1308  		}
  1309  	}
  1310  
  1311  	serviceID := st.docID(args.Name)
  1312  
  1313  	// Create the service addition operations.
  1314  	peers := args.Charm.Meta().Peers
  1315  
  1316  	// The doc defaults to CharmModifiedVersion = 0, which is correct, since it
  1317  	// has, by definition, at its initial state.
  1318  	svcDoc := &serviceDoc{
  1319  		DocID:         serviceID,
  1320  		Name:          args.Name,
  1321  		ModelUUID:     st.ModelUUID(),
  1322  		Series:        args.Series,
  1323  		Subordinate:   args.Charm.Meta().Subordinate,
  1324  		CharmURL:      args.Charm.URL(),
  1325  		Channel:       string(args.Channel),
  1326  		RelationCount: len(peers),
  1327  		Life:          Alive,
  1328  		OwnerTag:      args.Owner,
  1329  	}
  1330  
  1331  	svc := newService(st, svcDoc)
  1332  
  1333  	endpointBindingsOp, err := createEndpointBindingsOp(
  1334  		st, svc.globalKey(),
  1335  		args.EndpointBindings, args.Charm.Meta(),
  1336  	)
  1337  	if err != nil {
  1338  		return nil, errors.Trace(err)
  1339  	}
  1340  
  1341  	statusDoc := statusDoc{
  1342  		ModelUUID: st.ModelUUID(),
  1343  		// TODO(fwereade): this violates the spec. Should be "waiting".
  1344  		// Implemented like this to be consistent with incorrect add-unit
  1345  		// behaviour.
  1346  		Status:     status.StatusUnknown,
  1347  		StatusInfo: MessageWaitForAgentInit,
  1348  		// TODO(fwereade): 2016-03-17 lp:1558657
  1349  		Updated: time.Now().UnixNano(),
  1350  		// This exists to preserve questionable unit-aggregation behaviour
  1351  		// while we work out how to switch to an implementation that makes
  1352  		// sense. It is also set in AddMissingServiceStatuses.
  1353  		NeverSet: true,
  1354  	}
  1355  
  1356  	// The addServiceOps does not include the environment alive assertion,
  1357  	// so we add it here.
  1358  	ops := append(
  1359  		[]txn.Op{
  1360  			assertModelActiveOp(st.ModelUUID()),
  1361  			endpointBindingsOp,
  1362  		},
  1363  		addServiceOps(st, addServiceOpsArgs{
  1364  			serviceDoc:       svcDoc,
  1365  			statusDoc:        statusDoc,
  1366  			constraints:      args.Constraints,
  1367  			storage:          args.Storage,
  1368  			settings:         map[string]interface{}(args.Settings),
  1369  			settingsRefCount: 1,
  1370  		})...)
  1371  
  1372  	// Collect peer relation addition operations.
  1373  	//
  1374  	// TODO(dimitern): Ensure each st.Endpoint has a space name associated in a
  1375  	// follow-up.
  1376  	peerOps, err := st.addPeerRelationsOps(args.Name, peers)
  1377  	if err != nil {
  1378  		return nil, errors.Trace(err)
  1379  	}
  1380  	ops = append(ops, peerOps...)
  1381  
  1382  	if len(args.Resources) > 0 {
  1383  		// Collect pending resource resolution operations.
  1384  		resources, err := st.Resources()
  1385  		if err != nil {
  1386  			return nil, errors.Trace(err)
  1387  		}
  1388  		resOps, err := resources.NewResolvePendingResourcesOps(args.Name, args.Resources)
  1389  		if err != nil {
  1390  			return nil, errors.Trace(err)
  1391  		}
  1392  		ops = append(ops, resOps...)
  1393  	}
  1394  
  1395  	// Collect unit-adding operations.
  1396  	for x := 0; x < args.NumUnits; x++ {
  1397  		unitName, unitOps, err := svc.addServiceUnitOps(serviceAddUnitOpsArgs{cons: args.Constraints, storageCons: args.Storage})
  1398  		if err != nil {
  1399  			return nil, errors.Trace(err)
  1400  		}
  1401  		ops = append(ops, unitOps...)
  1402  		placement := instance.Placement{}
  1403  		if x < len(args.Placement) {
  1404  			placement = *args.Placement[x]
  1405  		}
  1406  		ops = append(ops, assignUnitOps(unitName, placement)...)
  1407  	}
  1408  	// At the last moment before inserting the service, prime status history.
  1409  	probablyUpdateStatusHistory(st, svc.globalKey(), statusDoc)
  1410  
  1411  	if err := st.runTransaction(ops); err == txn.ErrAborted {
  1412  		if err := checkModelActive(st); err != nil {
  1413  			return nil, errors.Trace(err)
  1414  		}
  1415  		return nil, errors.Errorf("service already exists")
  1416  	} else if err != nil {
  1417  		return nil, errors.Trace(err)
  1418  	}
  1419  	// Refresh to pick the txn-revno.
  1420  	if err = svc.Refresh(); err != nil {
  1421  		return nil, errors.Trace(err)
  1422  	}
  1423  
  1424  	return svc, nil
  1425  }
  1426  
  1427  // TODO(natefinch) DEMO code, revisit after demo!
  1428  var AddServicePostFuncs = map[string]func(*State, AddServiceArgs) error{}
  1429  
  1430  // assignUnitOps returns the db ops to save unit assignment for use by the
  1431  // UnitAssigner worker.
  1432  func assignUnitOps(unitName string, placement instance.Placement) []txn.Op {
  1433  	udoc := assignUnitDoc{
  1434  		DocId:     unitName,
  1435  		Scope:     placement.Scope,
  1436  		Directive: placement.Directive,
  1437  	}
  1438  	return []txn.Op{{
  1439  		C:      assignUnitC,
  1440  		Id:     udoc.DocId,
  1441  		Assert: txn.DocMissing,
  1442  		Insert: udoc,
  1443  	}}
  1444  }
  1445  
  1446  // AssignStagedUnits gets called by the UnitAssigner worker, and runs the given
  1447  // assignments.
  1448  func (st *State) AssignStagedUnits(ids []string) ([]UnitAssignmentResult, error) {
  1449  	query := bson.D{{"_id", bson.D{{"$in", ids}}}}
  1450  	unitAssignments, err := st.unitAssignments(query)
  1451  	if err != nil {
  1452  		return nil, errors.Annotate(err, "getting staged unit assignments")
  1453  	}
  1454  	results := make([]UnitAssignmentResult, len(unitAssignments))
  1455  	for i, a := range unitAssignments {
  1456  		err := st.assignStagedUnit(a)
  1457  		results[i].Unit = a.Unit
  1458  		results[i].Error = err
  1459  	}
  1460  	return results, nil
  1461  }
  1462  
  1463  // UnitAssignments returns all staged unit assignments in the model.
  1464  func (st *State) AllUnitAssignments() ([]UnitAssignment, error) {
  1465  	return st.unitAssignments(nil)
  1466  }
  1467  
  1468  func (st *State) unitAssignments(query bson.D) ([]UnitAssignment, error) {
  1469  	col, close := st.getCollection(assignUnitC)
  1470  	defer close()
  1471  
  1472  	var docs []assignUnitDoc
  1473  	if err := col.Find(query).All(&docs); err != nil {
  1474  		return nil, errors.Annotatef(err, "cannot get unit assignment docs")
  1475  	}
  1476  	results := make([]UnitAssignment, len(docs))
  1477  	for i, doc := range docs {
  1478  		results[i] = UnitAssignment{
  1479  			st.localID(doc.DocId),
  1480  			doc.Scope,
  1481  			doc.Directive,
  1482  		}
  1483  	}
  1484  	return results, nil
  1485  }
  1486  
  1487  func removeStagedAssignmentOp(id string) txn.Op {
  1488  	return txn.Op{
  1489  		C:      assignUnitC,
  1490  		Id:     id,
  1491  		Remove: true,
  1492  	}
  1493  }
  1494  
  1495  func (st *State) assignStagedUnit(a UnitAssignment) error {
  1496  	u, err := st.Unit(a.Unit)
  1497  	if err != nil {
  1498  		return errors.Trace(err)
  1499  	}
  1500  	if a.Scope == "" && a.Directive == "" {
  1501  		return errors.Trace(st.AssignUnit(u, AssignCleanEmpty))
  1502  	}
  1503  
  1504  	placement := &instance.Placement{Scope: a.Scope, Directive: a.Directive}
  1505  
  1506  	return errors.Trace(st.AssignUnitWithPlacement(u, placement))
  1507  }
  1508  
  1509  // AssignUnitWithPlacement chooses a machine using the given placement directive
  1510  // and then assigns the unit to it.
  1511  func (st *State) AssignUnitWithPlacement(unit *Unit, placement *instance.Placement) error {
  1512  	// TODO(natefinch) this should be done as a single transaction, not two.
  1513  	// Mark https://launchpad.net/bugs/1506994 fixed when done.
  1514  
  1515  	m, err := st.addMachineWithPlacement(unit, placement)
  1516  	if err != nil {
  1517  		return errors.Trace(err)
  1518  	}
  1519  	return unit.AssignToMachine(m)
  1520  }
  1521  
  1522  // placementData is a helper type that encodes some of the logic behind how an
  1523  // instance.Placement gets translated into a placement directive the providers
  1524  // understand.
  1525  type placementData struct {
  1526  	machineId     string
  1527  	directive     string
  1528  	containerType instance.ContainerType
  1529  }
  1530  
  1531  type placementType int
  1532  
  1533  const (
  1534  	containerPlacement placementType = iota
  1535  	directivePlacement
  1536  	machinePlacement
  1537  )
  1538  
  1539  // placementType returns the type of placement that this data represents.
  1540  func (p placementData) placementType() placementType {
  1541  	if p.containerType != "" {
  1542  		return containerPlacement
  1543  	}
  1544  	if p.directive != "" {
  1545  		return directivePlacement
  1546  	}
  1547  	return machinePlacement
  1548  }
  1549  
  1550  func (st *State) parsePlacement(placement *instance.Placement) (*placementData, error) {
  1551  	// Extract container type and parent from container placement directives.
  1552  	if container, err := instance.ParseContainerType(placement.Scope); err == nil {
  1553  		return &placementData{
  1554  			containerType: container,
  1555  			machineId:     placement.Directive,
  1556  		}, nil
  1557  	}
  1558  	switch placement.Scope {
  1559  	case st.ModelUUID():
  1560  		return &placementData{directive: placement.Directive}, nil
  1561  	case instance.MachineScope:
  1562  		return &placementData{machineId: placement.Directive}, nil
  1563  	default:
  1564  		return nil, errors.Errorf("placement scope: invalid model UUID %q", placement.Scope)
  1565  	}
  1566  }
  1567  
  1568  // addMachineWithPlacement finds a machine that matches the given placement directive for the given unit.
  1569  func (st *State) addMachineWithPlacement(unit *Unit, placement *instance.Placement) (*Machine, error) {
  1570  	unitCons, err := unit.Constraints()
  1571  	if err != nil {
  1572  		return nil, err
  1573  	}
  1574  
  1575  	data, err := st.parsePlacement(placement)
  1576  	if err != nil {
  1577  		return nil, errors.Trace(err)
  1578  	}
  1579  
  1580  	// Create any new machine marked as dirty so that
  1581  	// nothing else will grab it before we assign the unit to it.
  1582  	// TODO(natefinch) fix this when we put assignment in the same
  1583  	// transaction as adding a machine.  See bug
  1584  	// https://launchpad.net/bugs/1506994
  1585  
  1586  	switch data.placementType() {
  1587  	case containerPlacement:
  1588  		// If a container is to be used, create it.
  1589  		template := MachineTemplate{
  1590  			Series:      unit.Series(),
  1591  			Jobs:        []MachineJob{JobHostUnits},
  1592  			Dirty:       true,
  1593  			Constraints: *unitCons,
  1594  		}
  1595  		return st.AddMachineInsideMachine(template, data.machineId, data.containerType)
  1596  	case directivePlacement:
  1597  		// If a placement directive is to be used, do that here.
  1598  		template := MachineTemplate{
  1599  			Series:      unit.Series(),
  1600  			Jobs:        []MachineJob{JobHostUnits},
  1601  			Dirty:       true,
  1602  			Constraints: *unitCons,
  1603  			Placement:   data.directive,
  1604  		}
  1605  		return st.AddOneMachine(template)
  1606  	default:
  1607  		// Otherwise use an existing machine.
  1608  		return st.Machine(data.machineId)
  1609  	}
  1610  }
  1611  
  1612  // AddIPAddress creates and returns a new IP address. It can return an
  1613  // error satisfying IsNotValid() or IsAlreadyExists() when the addr
  1614  // does not contain a valid IP, or when addr is already added.
  1615  func (st *State) AddIPAddress(addr network.Address, subnetID string) (*IPAddress, error) {
  1616  	return addIPAddress(st, addr, subnetID)
  1617  }
  1618  
  1619  // IPAddress returns an existing IP address from the state.
  1620  func (st *State) IPAddress(value string) (*IPAddress, error) {
  1621  	return ipAddress(st, value)
  1622  }
  1623  
  1624  // IPAddressByTag returns an existing IP address from the state
  1625  // identified by its tag.
  1626  func (st *State) IPAddressByTag(tag names.IPAddressTag) (*IPAddress, error) {
  1627  	return ipAddressByTag(st, tag)
  1628  }
  1629  
  1630  // AllocatedIPAddresses returns all the allocated addresses for a machine
  1631  func (st *State) AllocatedIPAddresses(machineId string) ([]*IPAddress, error) {
  1632  	return fetchIPAddresses(st, bson.D{{"machineid", machineId}})
  1633  }
  1634  
  1635  // DeadIPAddresses returns all IP addresses with a Life of Dead
  1636  func (st *State) DeadIPAddresses() ([]*IPAddress, error) {
  1637  	return fetchIPAddresses(st, isDeadDoc)
  1638  }
  1639  
  1640  // AddSubnet creates and returns a new subnet
  1641  func (st *State) AddSubnet(args SubnetInfo) (subnet *Subnet, err error) {
  1642  	defer errors.DeferredAnnotatef(&err, "adding subnet %q", args.CIDR)
  1643  
  1644  	subnetID := st.docID(args.CIDR)
  1645  	var modelLocalProviderID string
  1646  	if args.ProviderId != "" {
  1647  		modelLocalProviderID = st.docID(string(args.ProviderId))
  1648  	}
  1649  
  1650  	subDoc := subnetDoc{
  1651  		DocID:             subnetID,
  1652  		ModelUUID:         st.ModelUUID(),
  1653  		Life:              Alive,
  1654  		CIDR:              args.CIDR,
  1655  		VLANTag:           args.VLANTag,
  1656  		ProviderId:        modelLocalProviderID,
  1657  		AllocatableIPHigh: args.AllocatableIPHigh,
  1658  		AllocatableIPLow:  args.AllocatableIPLow,
  1659  		AvailabilityZone:  args.AvailabilityZone,
  1660  		SpaceName:         args.SpaceName,
  1661  	}
  1662  	subnet = &Subnet{doc: subDoc, st: st}
  1663  	err = subnet.Validate()
  1664  	if err != nil {
  1665  		return nil, err
  1666  	}
  1667  	ops := []txn.Op{
  1668  		assertModelActiveOp(st.ModelUUID()),
  1669  		{
  1670  			C:      subnetsC,
  1671  			Id:     subnetID,
  1672  			Assert: txn.DocMissing,
  1673  			Insert: subDoc,
  1674  		},
  1675  	}
  1676  
  1677  	err = st.runTransaction(ops)
  1678  	switch err {
  1679  	case txn.ErrAborted:
  1680  		if err := checkModelActive(st); err != nil {
  1681  			return nil, errors.Trace(err)
  1682  		}
  1683  		if _, err = st.Subnet(args.CIDR); err == nil {
  1684  			return nil, errors.AlreadyExistsf("subnet %q", args.CIDR)
  1685  		} else if err != nil {
  1686  			return nil, errors.Trace(err)
  1687  		}
  1688  	case nil:
  1689  		// If the ProviderId was not unique adding the subnet can fail without
  1690  		// an error. Refreshing catches this by returning NotFoundError.
  1691  		err = subnet.Refresh()
  1692  		if err != nil {
  1693  			if errors.IsNotFound(err) {
  1694  				return nil, errors.Errorf("ProviderId %q not unique", args.ProviderId)
  1695  			}
  1696  			return nil, errors.Trace(err)
  1697  		}
  1698  		return subnet, nil
  1699  	}
  1700  	return nil, errors.Trace(err)
  1701  }
  1702  
  1703  func (st *State) Subnet(cidr string) (*Subnet, error) {
  1704  	subnets, closer := st.getCollection(subnetsC)
  1705  	defer closer()
  1706  
  1707  	doc := &subnetDoc{}
  1708  	err := subnets.FindId(cidr).One(doc)
  1709  	if err == mgo.ErrNotFound {
  1710  		return nil, errors.NotFoundf("subnet %q", cidr)
  1711  	}
  1712  	if err != nil {
  1713  		return nil, errors.Annotatef(err, "cannot get subnet %q", cidr)
  1714  	}
  1715  	return &Subnet{st, *doc}, nil
  1716  }
  1717  
  1718  // AllSubnets returns all known subnets in the model.
  1719  func (st *State) AllSubnets() (subnets []*Subnet, err error) {
  1720  	subnetsCollection, closer := st.getCollection(subnetsC)
  1721  	defer closer()
  1722  
  1723  	docs := []subnetDoc{}
  1724  	err = subnetsCollection.Find(nil).All(&docs)
  1725  	if err != nil {
  1726  		return nil, errors.Annotatef(err, "cannot get all subnets")
  1727  	}
  1728  	for _, doc := range docs {
  1729  		subnets = append(subnets, &Subnet{st, doc})
  1730  	}
  1731  	return subnets, nil
  1732  }
  1733  
  1734  // Service returns a service state by name.
  1735  func (st *State) Service(name string) (service *Service, err error) {
  1736  	services, closer := st.getCollection(servicesC)
  1737  	defer closer()
  1738  
  1739  	if !names.IsValidService(name) {
  1740  		return nil, errors.Errorf("%q is not a valid service name", name)
  1741  	}
  1742  	sdoc := &serviceDoc{}
  1743  	err = services.FindId(name).One(sdoc)
  1744  	if err == mgo.ErrNotFound {
  1745  		return nil, errors.NotFoundf("service %q", name)
  1746  	}
  1747  	if err != nil {
  1748  		return nil, errors.Annotatef(err, "cannot get service %q", name)
  1749  	}
  1750  	return newService(st, sdoc), nil
  1751  }
  1752  
  1753  // AllServices returns all deployed services in the model.
  1754  func (st *State) AllServices() (services []*Service, err error) {
  1755  	servicesCollection, closer := st.getCollection(servicesC)
  1756  	defer closer()
  1757  
  1758  	sdocs := []serviceDoc{}
  1759  	err = servicesCollection.Find(bson.D{}).All(&sdocs)
  1760  	if err != nil {
  1761  		return nil, errors.Errorf("cannot get all services")
  1762  	}
  1763  	for _, v := range sdocs {
  1764  		services = append(services, newService(st, &v))
  1765  	}
  1766  	return services, nil
  1767  }
  1768  
  1769  // docID generates a globally unique id value
  1770  // where the model uuid is prefixed to the
  1771  // localID.
  1772  func (st *State) docID(localID string) string {
  1773  	return ensureModelUUID(st.ModelUUID(), localID)
  1774  }
  1775  
  1776  // localID returns the local id value by stripping
  1777  // off the model uuid prefix if it is there.
  1778  func (st *State) localID(ID string) string {
  1779  	modelUUID, localID, ok := splitDocID(ID)
  1780  	if !ok || modelUUID != st.ModelUUID() {
  1781  		return ID
  1782  	}
  1783  	return localID
  1784  }
  1785  
  1786  // strictLocalID returns the local id value by removing the
  1787  // model UUID prefix.
  1788  //
  1789  // If there is no prefix matching the State's model, an error is
  1790  // returned.
  1791  func (st *State) strictLocalID(ID string) (string, error) {
  1792  	modelUUID, localID, ok := splitDocID(ID)
  1793  	if !ok || modelUUID != st.ModelUUID() {
  1794  		return "", errors.Errorf("unexpected id: %#v", ID)
  1795  	}
  1796  	return localID, nil
  1797  }
  1798  
  1799  // InferEndpoints returns the endpoints corresponding to the supplied names.
  1800  // There must be 1 or 2 supplied names, of the form <service>[:<relation>].
  1801  // If the supplied names uniquely specify a possible relation, or if they
  1802  // uniquely specify a possible relation once all implicit relations have been
  1803  // filtered, the endpoints corresponding to that relation will be returned.
  1804  func (st *State) InferEndpoints(names ...string) ([]Endpoint, error) {
  1805  	// Collect all possible sane endpoint lists.
  1806  	var candidates [][]Endpoint
  1807  	switch len(names) {
  1808  	case 1:
  1809  		eps, err := st.endpoints(names[0], isPeer)
  1810  		if err != nil {
  1811  			return nil, errors.Trace(err)
  1812  		}
  1813  		for _, ep := range eps {
  1814  			candidates = append(candidates, []Endpoint{ep})
  1815  		}
  1816  	case 2:
  1817  		eps1, err := st.endpoints(names[0], notPeer)
  1818  		if err != nil {
  1819  			return nil, errors.Trace(err)
  1820  		}
  1821  		eps2, err := st.endpoints(names[1], notPeer)
  1822  		if err != nil {
  1823  			return nil, errors.Trace(err)
  1824  		}
  1825  		for _, ep1 := range eps1 {
  1826  			for _, ep2 := range eps2 {
  1827  				if ep1.CanRelateTo(ep2) && containerScopeOk(st, ep1, ep2) {
  1828  					candidates = append(candidates, []Endpoint{ep1, ep2})
  1829  				}
  1830  			}
  1831  		}
  1832  	default:
  1833  		return nil, errors.Errorf("cannot relate %d endpoints", len(names))
  1834  	}
  1835  	// If there's ambiguity, try discarding implicit relations.
  1836  	switch len(candidates) {
  1837  	case 0:
  1838  		return nil, errors.Errorf("no relations found")
  1839  	case 1:
  1840  		return candidates[0], nil
  1841  	}
  1842  	var filtered [][]Endpoint
  1843  outer:
  1844  	for _, cand := range candidates {
  1845  		for _, ep := range cand {
  1846  			if ep.IsImplicit() {
  1847  				continue outer
  1848  			}
  1849  		}
  1850  		filtered = append(filtered, cand)
  1851  	}
  1852  	if len(filtered) == 1 {
  1853  		return filtered[0], nil
  1854  	}
  1855  	keys := []string{}
  1856  	for _, cand := range candidates {
  1857  		keys = append(keys, fmt.Sprintf("%q", relationKey(cand)))
  1858  	}
  1859  	sort.Strings(keys)
  1860  	return nil, errors.Errorf("ambiguous relation: %q could refer to %s",
  1861  		strings.Join(names, " "), strings.Join(keys, "; "))
  1862  }
  1863  
  1864  func isPeer(ep Endpoint) bool {
  1865  	return ep.Role == charm.RolePeer
  1866  }
  1867  
  1868  func notPeer(ep Endpoint) bool {
  1869  	return ep.Role != charm.RolePeer
  1870  }
  1871  
  1872  func containerScopeOk(st *State, ep1, ep2 Endpoint) bool {
  1873  	if ep1.Scope != charm.ScopeContainer && ep2.Scope != charm.ScopeContainer {
  1874  		return true
  1875  	}
  1876  	var subordinateCount int
  1877  	for _, ep := range []Endpoint{ep1, ep2} {
  1878  		svc, err := st.Service(ep.ServiceName)
  1879  		if err != nil {
  1880  			return false
  1881  		}
  1882  		if svc.doc.Subordinate {
  1883  			subordinateCount++
  1884  		}
  1885  	}
  1886  	return subordinateCount >= 1
  1887  }
  1888  
  1889  // endpoints returns all endpoints that could be intended by the
  1890  // supplied endpoint name, and which cause the filter param to
  1891  // return true.
  1892  func (st *State) endpoints(name string, filter func(ep Endpoint) bool) ([]Endpoint, error) {
  1893  	var svcName, relName string
  1894  	if i := strings.Index(name, ":"); i == -1 {
  1895  		svcName = name
  1896  	} else if i != 0 && i != len(name)-1 {
  1897  		svcName = name[:i]
  1898  		relName = name[i+1:]
  1899  	} else {
  1900  		return nil, errors.Errorf("invalid endpoint %q", name)
  1901  	}
  1902  	svc, err := st.Service(svcName)
  1903  	if err != nil {
  1904  		return nil, errors.Trace(err)
  1905  	}
  1906  	eps := []Endpoint{}
  1907  	if relName != "" {
  1908  		ep, err := svc.Endpoint(relName)
  1909  		if err != nil {
  1910  			return nil, errors.Trace(err)
  1911  		}
  1912  		eps = append(eps, ep)
  1913  	} else {
  1914  		eps, err = svc.Endpoints()
  1915  		if err != nil {
  1916  			return nil, errors.Trace(err)
  1917  		}
  1918  	}
  1919  	final := []Endpoint{}
  1920  	for _, ep := range eps {
  1921  		if filter(ep) {
  1922  			final = append(final, ep)
  1923  		}
  1924  	}
  1925  	return final, nil
  1926  }
  1927  
  1928  // AddRelation creates a new relation with the given endpoints.
  1929  func (st *State) AddRelation(eps ...Endpoint) (r *Relation, err error) {
  1930  	key := relationKey(eps)
  1931  	defer errors.DeferredAnnotatef(&err, "cannot add relation %q", key)
  1932  	// Enforce basic endpoint sanity. The epCount restrictions may be relaxed
  1933  	// in the future; if so, this method is likely to need significant rework.
  1934  	if len(eps) != 2 {
  1935  		return nil, errors.Errorf("relation must have two endpoints")
  1936  	}
  1937  	if !eps[0].CanRelateTo(eps[1]) {
  1938  		return nil, errors.Errorf("endpoints do not relate")
  1939  	}
  1940  	// If either endpoint has container scope, so must the other; and the
  1941  	// services's series must also match, because they'll be deployed to
  1942  	// the same machines.
  1943  	matchSeries := true
  1944  	if eps[0].Scope == charm.ScopeContainer {
  1945  		eps[1].Scope = charm.ScopeContainer
  1946  	} else if eps[1].Scope == charm.ScopeContainer {
  1947  		eps[0].Scope = charm.ScopeContainer
  1948  	} else {
  1949  		matchSeries = false
  1950  	}
  1951  	// We only get a unique relation id once, to save on roundtrips. If it's
  1952  	// -1, we haven't got it yet (we don't get it at this stage, because we
  1953  	// still don't know whether it's sane to even attempt creation).
  1954  	id := -1
  1955  	// If a service's charm is upgraded while we're trying to add a relation,
  1956  	// we'll need to re-validate service sanity.
  1957  	var doc *relationDoc
  1958  	buildTxn := func(attempt int) ([]txn.Op, error) {
  1959  		// Perform initial relation sanity check.
  1960  		if exists, err := isNotDead(st, relationsC, key); err != nil {
  1961  			return nil, errors.Trace(err)
  1962  		} else if exists {
  1963  			return nil, errors.Errorf("relation already exists")
  1964  		}
  1965  		// Collect per-service operations, checking sanity as we go.
  1966  		var ops []txn.Op
  1967  		var subordinateCount int
  1968  		series := map[string]bool{}
  1969  		for _, ep := range eps {
  1970  			svc, err := st.Service(ep.ServiceName)
  1971  			if errors.IsNotFound(err) {
  1972  				return nil, errors.Errorf("service %q does not exist", ep.ServiceName)
  1973  			} else if err != nil {
  1974  				return nil, errors.Trace(err)
  1975  			} else if svc.doc.Life != Alive {
  1976  				return nil, errors.Errorf("service %q is not alive", ep.ServiceName)
  1977  			}
  1978  			if svc.doc.Subordinate {
  1979  				subordinateCount++
  1980  			}
  1981  			series[svc.doc.Series] = true
  1982  			ch, _, err := svc.Charm()
  1983  			if err != nil {
  1984  				return nil, errors.Trace(err)
  1985  			}
  1986  			if !ep.ImplementedBy(ch) {
  1987  				return nil, errors.Errorf("%q does not implement %q", ep.ServiceName, ep)
  1988  			}
  1989  			ops = append(ops, txn.Op{
  1990  				C:      servicesC,
  1991  				Id:     st.docID(ep.ServiceName),
  1992  				Assert: bson.D{{"life", Alive}, {"charmurl", ch.URL()}},
  1993  				Update: bson.D{{"$inc", bson.D{{"relationcount", 1}}}},
  1994  			})
  1995  		}
  1996  		if matchSeries && len(series) != 1 {
  1997  			return nil, errors.Errorf("principal and subordinate services' series must match")
  1998  		}
  1999  		if eps[0].Scope == charm.ScopeContainer && subordinateCount < 1 {
  2000  			return nil, errors.Errorf("container scoped relation requires at least one subordinate service")
  2001  		}
  2002  
  2003  		// Create a new unique id if that has not already been done, and add
  2004  		// an operation to create the relation document.
  2005  		if id == -1 {
  2006  			var err error
  2007  			if id, err = st.sequence("relation"); err != nil {
  2008  				return nil, errors.Trace(err)
  2009  			}
  2010  		}
  2011  		docID := st.docID(key)
  2012  		doc = &relationDoc{
  2013  			DocID:     docID,
  2014  			Key:       key,
  2015  			ModelUUID: st.ModelUUID(),
  2016  			Id:        id,
  2017  			Endpoints: eps,
  2018  			Life:      Alive,
  2019  		}
  2020  		ops = append(ops, txn.Op{
  2021  			C:      relationsC,
  2022  			Id:     docID,
  2023  			Assert: txn.DocMissing,
  2024  			Insert: doc,
  2025  		})
  2026  		return ops, nil
  2027  	}
  2028  	if err = st.run(buildTxn); err == nil {
  2029  		return &Relation{st, *doc}, nil
  2030  	}
  2031  	return nil, errors.Trace(err)
  2032  }
  2033  
  2034  // EndpointsRelation returns the existing relation with the given endpoints.
  2035  func (st *State) EndpointsRelation(endpoints ...Endpoint) (*Relation, error) {
  2036  	return st.KeyRelation(relationKey(endpoints))
  2037  }
  2038  
  2039  // KeyRelation returns the existing relation with the given key (which can
  2040  // be derived unambiguously from the relation's endpoints).
  2041  func (st *State) KeyRelation(key string) (*Relation, error) {
  2042  	relations, closer := st.getCollection(relationsC)
  2043  	defer closer()
  2044  
  2045  	doc := relationDoc{}
  2046  	err := relations.FindId(key).One(&doc)
  2047  	if err == mgo.ErrNotFound {
  2048  		return nil, errors.NotFoundf("relation %q", key)
  2049  	}
  2050  	if err != nil {
  2051  		return nil, errors.Annotatef(err, "cannot get relation %q", key)
  2052  	}
  2053  	return newRelation(st, &doc), nil
  2054  }
  2055  
  2056  // Relation returns the existing relation with the given id.
  2057  func (st *State) Relation(id int) (*Relation, error) {
  2058  	relations, closer := st.getCollection(relationsC)
  2059  	defer closer()
  2060  
  2061  	doc := relationDoc{}
  2062  	err := relations.Find(bson.D{{"id", id}}).One(&doc)
  2063  	if err == mgo.ErrNotFound {
  2064  		return nil, errors.NotFoundf("relation %d", id)
  2065  	}
  2066  	if err != nil {
  2067  		return nil, errors.Annotatef(err, "cannot get relation %d", id)
  2068  	}
  2069  	return newRelation(st, &doc), nil
  2070  }
  2071  
  2072  // AllRelations returns all relations in the model ordered by id.
  2073  func (st *State) AllRelations() (relations []*Relation, err error) {
  2074  	relationsCollection, closer := st.getCollection(relationsC)
  2075  	defer closer()
  2076  
  2077  	docs := relationDocSlice{}
  2078  	err = relationsCollection.Find(nil).All(&docs)
  2079  	if err != nil {
  2080  		return nil, errors.Annotate(err, "cannot get all relations")
  2081  	}
  2082  	sort.Sort(docs)
  2083  	for _, v := range docs {
  2084  		relations = append(relations, newRelation(st, &v))
  2085  	}
  2086  	return
  2087  }
  2088  
  2089  type relationDocSlice []relationDoc
  2090  
  2091  func (rdc relationDocSlice) Len() int      { return len(rdc) }
  2092  func (rdc relationDocSlice) Swap(i, j int) { rdc[i], rdc[j] = rdc[j], rdc[i] }
  2093  func (rdc relationDocSlice) Less(i, j int) bool {
  2094  	return rdc[i].Id < rdc[j].Id
  2095  }
  2096  
  2097  // Unit returns a unit by name.
  2098  func (st *State) Unit(name string) (*Unit, error) {
  2099  	if !names.IsValidUnit(name) {
  2100  		return nil, errors.Errorf("%q is not a valid unit name", name)
  2101  	}
  2102  	units, closer := st.getCollection(unitsC)
  2103  	defer closer()
  2104  
  2105  	doc := unitDoc{}
  2106  	err := units.FindId(name).One(&doc)
  2107  	if err == mgo.ErrNotFound {
  2108  		return nil, errors.NotFoundf("unit %q", name)
  2109  	}
  2110  	if err != nil {
  2111  		return nil, errors.Annotatef(err, "cannot get unit %q", name)
  2112  	}
  2113  	return newUnit(st, &doc), nil
  2114  }
  2115  
  2116  // UnitsFor returns the units placed in the given machine id.
  2117  func (st *State) UnitsFor(machineId string) ([]*Unit, error) {
  2118  	if !names.IsValidMachine(machineId) {
  2119  		return nil, errors.Errorf("%q is not a valid machine id", machineId)
  2120  	}
  2121  	m := &Machine{
  2122  		st: st,
  2123  		doc: machineDoc{
  2124  			Id: machineId,
  2125  		},
  2126  	}
  2127  	return m.Units()
  2128  }
  2129  
  2130  // AssignUnit places the unit on a machine. Depending on the policy, and the
  2131  // state of the model, this may lead to new instances being launched
  2132  // within the model.
  2133  func (st *State) AssignUnit(u *Unit, policy AssignmentPolicy) (err error) {
  2134  	if !u.IsPrincipal() {
  2135  		return errors.Errorf("subordinate unit %q cannot be assigned directly to a machine", u)
  2136  	}
  2137  	defer errors.DeferredAnnotatef(&err, "cannot assign unit %q to machine", u)
  2138  	var m *Machine
  2139  	switch policy {
  2140  	case AssignLocal:
  2141  		m, err = st.Machine("0")
  2142  		if err != nil {
  2143  			return errors.Trace(err)
  2144  		}
  2145  		return u.AssignToMachine(m)
  2146  	case AssignClean:
  2147  		if _, err = u.AssignToCleanMachine(); err != noCleanMachines {
  2148  			return errors.Trace(err)
  2149  		}
  2150  		return u.AssignToNewMachineOrContainer()
  2151  	case AssignCleanEmpty:
  2152  		if _, err = u.AssignToCleanEmptyMachine(); err != noCleanMachines {
  2153  			return errors.Trace(err)
  2154  		}
  2155  		return u.AssignToNewMachineOrContainer()
  2156  	case AssignNew:
  2157  		return errors.Trace(u.AssignToNewMachine())
  2158  	}
  2159  	return errors.Errorf("unknown unit assignment policy: %q", policy)
  2160  }
  2161  
  2162  // StartSync forces watchers to resynchronize their state with the
  2163  // database immediately. This will happen periodically automatically.
  2164  func (st *State) StartSync() {
  2165  	st.watcher.StartSync()
  2166  	st.pwatcher.Sync()
  2167  }
  2168  
  2169  // SetAdminMongoPassword sets the administrative password
  2170  // to access the state. If the password is non-empty,
  2171  // all subsequent attempts to access the state must
  2172  // be authorized; otherwise no authorization is required.
  2173  func (st *State) SetAdminMongoPassword(password string) error {
  2174  	err := mongo.SetAdminMongoPassword(st.session, mongo.AdminUser, password)
  2175  	return errors.Trace(err)
  2176  }
  2177  
  2178  type controllersDoc struct {
  2179  	Id               string `bson:"_id"`
  2180  	ModelUUID        string `bson:"model-uuid"`
  2181  	MachineIds       []string
  2182  	VotingMachineIds []string
  2183  	MongoSpaceName   string `bson:"mongo-space-name"`
  2184  	MongoSpaceState  string `bson:"mongo-space-state"`
  2185  }
  2186  
  2187  // ControllerInfo holds information about currently
  2188  // configured controller machines.
  2189  type ControllerInfo struct {
  2190  	// ModelTag identifies the initial model. Only the initial
  2191  	// model is able to have machines that manage state. The initial
  2192  	// model is the model that is created when bootstrapping.
  2193  	ModelTag names.ModelTag
  2194  
  2195  	// MachineIds holds the ids of all machines configured
  2196  	// to run a controller. It includes all the machine
  2197  	// ids in VotingMachineIds.
  2198  	MachineIds []string
  2199  
  2200  	// VotingMachineIds holds the ids of all machines
  2201  	// configured to run a controller and to have a vote
  2202  	// in peer election.
  2203  	VotingMachineIds []string
  2204  
  2205  	// MongoSpaceName is the space that contains all Mongo servers.
  2206  	MongoSpaceName string
  2207  
  2208  	// MongoSpaceState records the state of the mongo space selection state machine. Valid states are:
  2209  	// * We haven't looked for a Mongo space yet (MongoSpaceUnknown)
  2210  	// * We have looked for a Mongo space, but we didn't find one (MongoSpaceInvalid)
  2211  	// * We have looked for and found a Mongo space (MongoSpaceValid)
  2212  	// * We didn't try to find a Mongo space because the provider doesn't support spaces (MongoSpaceUnsupported)
  2213  	MongoSpaceState MongoSpaceStates
  2214  }
  2215  
  2216  type MongoSpaceStates string
  2217  
  2218  const (
  2219  	MongoSpaceUnknown     MongoSpaceStates = ""
  2220  	MongoSpaceValid       MongoSpaceStates = "valid"
  2221  	MongoSpaceInvalid     MongoSpaceStates = "invalid"
  2222  	MongoSpaceUnsupported MongoSpaceStates = "unsupported"
  2223  )
  2224  
  2225  // ControllerInfo returns information about
  2226  // the currently configured controller machines.
  2227  func (st *State) ControllerInfo() (*ControllerInfo, error) {
  2228  	session := st.session.Copy()
  2229  	defer session.Close()
  2230  	return readRawControllerInfo(st.session)
  2231  }
  2232  
  2233  // readRawControllerInfo reads ControllerInfo direct from the supplied session,
  2234  // falling back to the bootstrap model document to extract the UUID when
  2235  // required.
  2236  func readRawControllerInfo(session *mgo.Session) (*ControllerInfo, error) {
  2237  	db := session.DB(jujuDB)
  2238  	controllers := db.C(controllersC)
  2239  
  2240  	var doc controllersDoc
  2241  	err := controllers.Find(bson.D{{"_id", modelGlobalKey}}).One(&doc)
  2242  	if err != nil {
  2243  		return nil, errors.Annotatef(err, "cannot get controllers document")
  2244  	}
  2245  
  2246  	if doc.ModelUUID == "" {
  2247  		logger.Warningf("controllers info has no model UUID so retrieving it from model")
  2248  
  2249  		// This only happens when migrating from 1.20 to 1.21 before
  2250  		// upgrade steps have been run. Without this hack modelTag
  2251  		// on State ends up empty, breaking basic functionality needed
  2252  		// to run upgrade steps (a chicken-and-egg scenario).
  2253  		environments := db.C(modelsC)
  2254  
  2255  		var envDoc modelDoc
  2256  		query := environments.Find(nil)
  2257  		count, err := query.Count()
  2258  		if err != nil {
  2259  			return nil, errors.Annotate(err, "cannot get model document count")
  2260  		}
  2261  		if count != 1 {
  2262  			return nil, errors.New("expected just one model to get UUID from")
  2263  		}
  2264  		if err := query.One(&envDoc); err != nil {
  2265  			return nil, errors.Annotate(err, "cannot load model document")
  2266  		}
  2267  		doc.ModelUUID = envDoc.UUID
  2268  	}
  2269  
  2270  	return &ControllerInfo{
  2271  		ModelTag:         names.NewModelTag(doc.ModelUUID),
  2272  		MachineIds:       doc.MachineIds,
  2273  		VotingMachineIds: doc.VotingMachineIds,
  2274  		MongoSpaceName:   doc.MongoSpaceName,
  2275  		MongoSpaceState:  MongoSpaceStates(doc.MongoSpaceState),
  2276  	}, nil
  2277  }
  2278  
  2279  const stateServingInfoKey = "stateServingInfo"
  2280  
  2281  // StateServingInfo returns information for running a controller machine
  2282  func (st *State) StateServingInfo() (StateServingInfo, error) {
  2283  	controllers, closer := st.getCollection(controllersC)
  2284  	defer closer()
  2285  
  2286  	var info StateServingInfo
  2287  	err := controllers.Find(bson.D{{"_id", stateServingInfoKey}}).One(&info)
  2288  	if err != nil {
  2289  		return info, errors.Trace(err)
  2290  	}
  2291  	if info.StatePort == 0 {
  2292  		return StateServingInfo{}, errors.NotFoundf("state serving info")
  2293  	}
  2294  	return info, nil
  2295  }
  2296  
  2297  // SetStateServingInfo stores information needed for running a controller
  2298  func (st *State) SetStateServingInfo(info StateServingInfo) error {
  2299  	if info.StatePort == 0 || info.APIPort == 0 ||
  2300  		info.Cert == "" || info.PrivateKey == "" {
  2301  		return errors.Errorf("incomplete state serving info set in state")
  2302  	}
  2303  	if info.CAPrivateKey == "" {
  2304  		// No CA certificate key means we can't generate new controller
  2305  		// certificates when needed to add to the certificate SANs.
  2306  		// Older Juju deployments discard the key because no one realised
  2307  		// the certificate was flawed, so at best we can log a warning
  2308  		// until an upgrade process is written.
  2309  		logger.Warningf("state serving info has no CA certificate key")
  2310  	}
  2311  	ops := []txn.Op{{
  2312  		C:      controllersC,
  2313  		Id:     stateServingInfoKey,
  2314  		Update: bson.D{{"$set", info}},
  2315  	}}
  2316  	if err := st.runTransaction(ops); err != nil {
  2317  		return errors.Annotatef(err, "cannot set state serving info")
  2318  	}
  2319  	return nil
  2320  }
  2321  
  2322  // SetSystemIdentity sets the system identity value in the database
  2323  // if and only iff it is empty.
  2324  func SetSystemIdentity(st *State, identity string) error {
  2325  	ops := []txn.Op{{
  2326  		C:      controllersC,
  2327  		Id:     stateServingInfoKey,
  2328  		Assert: bson.D{{"systemidentity", ""}},
  2329  		Update: bson.D{{"$set", bson.D{{"systemidentity", identity}}}},
  2330  	}}
  2331  
  2332  	if err := st.runTransaction(ops); err != nil {
  2333  		return errors.Trace(err)
  2334  	}
  2335  	return nil
  2336  }
  2337  
  2338  // SetOrGetMongoSpaceName attempts to set the Mongo space or, if that fails, look
  2339  // up the current Mongo space. Either way, it always returns what is in the
  2340  // database by the end of the call.
  2341  func (st *State) SetOrGetMongoSpaceName(mongoSpaceName network.SpaceName) (network.SpaceName, error) {
  2342  	err := st.setMongoSpaceName(mongoSpaceName)
  2343  	if err == txn.ErrAborted {
  2344  		// Failed to set the new space name. Return what is already stored in state.
  2345  		controllerInfo, err := st.ControllerInfo()
  2346  		if err != nil {
  2347  			return network.SpaceName(""), errors.Trace(err)
  2348  		}
  2349  		return network.SpaceName(controllerInfo.MongoSpaceName), nil
  2350  	} else if err != nil {
  2351  		return network.SpaceName(""), errors.Trace(err)
  2352  	}
  2353  	return mongoSpaceName, nil
  2354  }
  2355  
  2356  // SetMongoSpaceState attempts to set the Mongo space state or, if that fails, look
  2357  // up the current Mongo state. Either way, it always returns what is in the
  2358  // database by the end of the call.
  2359  func (st *State) SetMongoSpaceState(mongoSpaceState MongoSpaceStates) error {
  2360  
  2361  	if mongoSpaceState != MongoSpaceUnknown &&
  2362  		mongoSpaceState != MongoSpaceValid &&
  2363  		mongoSpaceState != MongoSpaceInvalid &&
  2364  		mongoSpaceState != MongoSpaceUnsupported {
  2365  		return errors.NotValidf("mongoSpaceState: %s", mongoSpaceState)
  2366  	}
  2367  
  2368  	err := st.setMongoSpaceState(mongoSpaceState)
  2369  	if err != nil {
  2370  		return errors.Trace(err)
  2371  	}
  2372  	return nil
  2373  }
  2374  
  2375  func (st *State) setMongoSpaceName(mongoSpaceName network.SpaceName) error {
  2376  	ops := []txn.Op{{
  2377  		C:      controllersC,
  2378  		Id:     modelGlobalKey,
  2379  		Assert: bson.D{{"mongo-space-state", string(MongoSpaceUnknown)}},
  2380  		Update: bson.D{{
  2381  			"$set",
  2382  			bson.D{
  2383  				{"mongo-space-name", string(mongoSpaceName)},
  2384  				{"mongo-space-state", MongoSpaceValid},
  2385  			},
  2386  		}},
  2387  	}}
  2388  
  2389  	return st.runTransaction(ops)
  2390  }
  2391  
  2392  func (st *State) setMongoSpaceState(mongoSpaceState MongoSpaceStates) error {
  2393  	ops := []txn.Op{{
  2394  		C:      controllersC,
  2395  		Id:     modelGlobalKey,
  2396  		Update: bson.D{{"$set", bson.D{{"mongo-space-state", mongoSpaceState}}}},
  2397  	}}
  2398  
  2399  	return st.runTransaction(ops)
  2400  }
  2401  
  2402  var tagPrefix = map[byte]string{
  2403  	'm': names.MachineTagKind + "-",
  2404  	's': names.ServiceTagKind + "-",
  2405  	'u': names.UnitTagKind + "-",
  2406  	'e': names.ModelTagKind + "-",
  2407  	'r': names.RelationTagKind + "-",
  2408  }
  2409  
  2410  func tagForGlobalKey(key string) (string, bool) {
  2411  	if len(key) < 3 || key[1] != '#' {
  2412  		return "", false
  2413  	}
  2414  	p, ok := tagPrefix[key[0]]
  2415  	if !ok {
  2416  		return "", false
  2417  	}
  2418  	return p + key[2:], true
  2419  }