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