github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/state/service.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	stderrors "errors"
     8  	"fmt"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/names"
    15  	jujutxn "github.com/juju/txn"
    16  	"gopkg.in/juju/charm.v4"
    17  	"gopkg.in/mgo.v2"
    18  	"gopkg.in/mgo.v2/bson"
    19  	"gopkg.in/mgo.v2/txn"
    20  
    21  	"github.com/juju/juju/constraints"
    22  )
    23  
    24  // Service represents the state of a service.
    25  type Service struct {
    26  	st  *State
    27  	doc serviceDoc
    28  }
    29  
    30  // serviceDoc represents the internal state of a service in MongoDB.
    31  // Note the correspondence with ServiceInfo in apiserver/params.
    32  type serviceDoc struct {
    33  	DocID             string     `bson:"_id"`
    34  	Name              string     `bson:"name"`
    35  	EnvUUID           string     `bson:"env-uuid"`
    36  	Series            string     `bson:"series"`
    37  	Subordinate       bool       `bson:"subordinate"`
    38  	CharmURL          *charm.URL `bson:"charmurl"`
    39  	ForceCharm        bool       `bson:forcecharm"`
    40  	Life              Life       `bson:"life"`
    41  	UnitSeq           int        `bson:"unitseq"`
    42  	UnitCount         int        `bson:"unitcount"`
    43  	RelationCount     int        `bson:"relationcount"`
    44  	Exposed           bool       `bson:"exposed"`
    45  	MinUnits          int        `bson:"minunits"`
    46  	OwnerTag          string     `bson:"ownertag"`
    47  	TxnRevno          int64      `bson:"txn-revno"`
    48  	MetricCredentials []byte     `bson:"metric-credentials"`
    49  }
    50  
    51  func newService(st *State, doc *serviceDoc) *Service {
    52  	svc := &Service{
    53  		st:  st,
    54  		doc: *doc,
    55  	}
    56  	return svc
    57  }
    58  
    59  // Name returns the service name.
    60  func (s *Service) Name() string {
    61  	return s.doc.Name
    62  }
    63  
    64  // Tag returns a name identifying the service.
    65  // The returned name will be different from other Tag values returned by any
    66  // other entities from the same state.
    67  func (s *Service) Tag() names.Tag {
    68  	return names.NewServiceTag(s.Name())
    69  }
    70  
    71  // serviceGlobalKey returns the global database key for the service
    72  // with the given name.
    73  func serviceGlobalKey(svcName string) string {
    74  	return "s#" + svcName
    75  }
    76  
    77  // globalKey returns the global database key for the service.
    78  func (s *Service) globalKey() string {
    79  	return serviceGlobalKey(s.doc.Name)
    80  }
    81  
    82  func serviceSettingsKey(serviceName string, curl *charm.URL) string {
    83  	return fmt.Sprintf("s#%s#%s", serviceName, curl)
    84  }
    85  
    86  // settingsKey returns the charm-version-specific settings collection
    87  // key for the service.
    88  func (s *Service) settingsKey() string {
    89  	return serviceSettingsKey(s.doc.Name, s.doc.CharmURL)
    90  }
    91  
    92  // Life returns whether the service is Alive, Dying or Dead.
    93  func (s *Service) Life() Life {
    94  	return s.doc.Life
    95  }
    96  
    97  var errRefresh = stderrors.New("state seems inconsistent, refresh and try again")
    98  
    99  // Destroy ensures that the service and all its relations will be removed at
   100  // some point; if the service has no units, and no relation involving the
   101  // service has any units in scope, they are all removed immediately.
   102  func (s *Service) Destroy() (err error) {
   103  	defer errors.DeferredAnnotatef(&err, "cannot destroy service %q", s)
   104  	defer func() {
   105  		if err == nil {
   106  			// This is a white lie; the document might actually be removed.
   107  			s.doc.Life = Dying
   108  		}
   109  	}()
   110  	svc := &Service{st: s.st, doc: s.doc}
   111  	buildTxn := func(attempt int) ([]txn.Op, error) {
   112  		if attempt > 0 {
   113  			if err := svc.Refresh(); errors.IsNotFound(err) {
   114  				return nil, jujutxn.ErrNoOperations
   115  			} else if err != nil {
   116  				return nil, err
   117  			}
   118  		}
   119  		switch ops, err := svc.destroyOps(); err {
   120  		case errRefresh:
   121  		case errAlreadyDying:
   122  			return nil, jujutxn.ErrNoOperations
   123  		case nil:
   124  			return ops, nil
   125  		default:
   126  			return nil, err
   127  		}
   128  		return nil, jujutxn.ErrTransientFailure
   129  	}
   130  	return s.st.run(buildTxn)
   131  }
   132  
   133  // destroyOps returns the operations required to destroy the service. If it
   134  // returns errRefresh, the service should be refreshed and the destruction
   135  // operations recalculated.
   136  func (s *Service) destroyOps() ([]txn.Op, error) {
   137  	if s.doc.Life == Dying {
   138  		return nil, errAlreadyDying
   139  	}
   140  	rels, err := s.Relations()
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	if len(rels) != s.doc.RelationCount {
   145  		// This is just an early bail out. The relations obtained may still
   146  		// be wrong, but that situation will be caught by a combination of
   147  		// asserts on relationcount and on each known relation, below.
   148  		return nil, errRefresh
   149  	}
   150  	ops := []txn.Op{minUnitsRemoveOp(s.st, s.doc.Name)}
   151  	removeCount := 0
   152  	for _, rel := range rels {
   153  		relOps, isRemove, err := rel.destroyOps(s.doc.Name)
   154  		if err == errAlreadyDying {
   155  			relOps = []txn.Op{{
   156  				C:      relationsC,
   157  				Id:     rel.doc.DocID,
   158  				Assert: bson.D{{"life", Dying}},
   159  			}}
   160  		} else if err != nil {
   161  			return nil, err
   162  		}
   163  		if isRemove {
   164  			removeCount++
   165  		}
   166  		ops = append(ops, relOps...)
   167  	}
   168  	// If the service has no units, and all its known relations will be
   169  	// removed, the service can also be removed.
   170  	if s.doc.UnitCount == 0 && s.doc.RelationCount == removeCount {
   171  		hasLastRefs := bson.D{{"life", Alive}, {"unitcount", 0}, {"relationcount", removeCount}}
   172  		return append(ops, s.removeOps(hasLastRefs)...), nil
   173  	}
   174  	// In all other cases, service removal will be handled as a consequence
   175  	// of the removal of the last unit or relation referencing it. If any
   176  	// relations have been removed, they'll be caught by the operations
   177  	// collected above; but if any has been added, we need to abort and add
   178  	// a destroy op for that relation too. In combination, it's enough to
   179  	// check for count equality: an add/remove will not touch the count, but
   180  	// will be caught by virtue of being a remove.
   181  	notLastRefs := bson.D{
   182  		{"life", Alive},
   183  		{"relationcount", s.doc.RelationCount},
   184  	}
   185  	// With respect to unit count, a changing value doesn't matter, so long
   186  	// as the count's equality with zero does not change, because all we care
   187  	// about is that *some* unit is, or is not, keeping the service from
   188  	// being removed: the difference between 1 unit and 1000 is irrelevant.
   189  	if s.doc.UnitCount > 0 {
   190  		ops = append(ops, s.st.newCleanupOp(cleanupUnitsForDyingService, s.doc.Name))
   191  		notLastRefs = append(notLastRefs, bson.D{{"unitcount", bson.D{{"$gt", 0}}}}...)
   192  	} else {
   193  		notLastRefs = append(notLastRefs, bson.D{{"unitcount", 0}}...)
   194  	}
   195  	update := bson.D{{"$set", bson.D{{"life", Dying}}}}
   196  	if removeCount != 0 {
   197  		decref := bson.D{{"$inc", bson.D{{"relationcount", -removeCount}}}}
   198  		update = append(update, decref...)
   199  	}
   200  	return append(ops, txn.Op{
   201  		C:      servicesC,
   202  		Id:     s.doc.DocID,
   203  		Assert: notLastRefs,
   204  		Update: update,
   205  	}), nil
   206  }
   207  
   208  // removeOps returns the operations required to remove the service. Supplied
   209  // asserts will be included in the operation on the service document.
   210  func (s *Service) removeOps(asserts bson.D) []txn.Op {
   211  	settingsDocID := s.st.docID(s.settingsKey())
   212  	ops := []txn.Op{
   213  		{
   214  			C:      servicesC,
   215  			Id:     s.doc.DocID,
   216  			Assert: asserts,
   217  			Remove: true,
   218  		}, {
   219  			C:      settingsrefsC,
   220  			Id:     settingsDocID,
   221  			Remove: true,
   222  		}, {
   223  			C:      settingsC,
   224  			Id:     settingsDocID,
   225  			Remove: true,
   226  		},
   227  		removeRequestedNetworksOp(s.st, s.globalKey()),
   228  		removeStorageConstraintsOp(s.globalKey()),
   229  		removeConstraintsOp(s.st, s.globalKey()),
   230  		annotationRemoveOp(s.st, s.globalKey()),
   231  		removeLeadershipSettingsOp(s.Tag().Id()),
   232  	}
   233  	return ops
   234  }
   235  
   236  // IsExposed returns whether this service is exposed. The explicitly open
   237  // ports (with open-port) for exposed services may be accessed from machines
   238  // outside of the local deployment network. See SetExposed and ClearExposed.
   239  func (s *Service) IsExposed() bool {
   240  	return s.doc.Exposed
   241  }
   242  
   243  // SetExposed marks the service as exposed.
   244  // See ClearExposed and IsExposed.
   245  func (s *Service) SetExposed() error {
   246  	return s.setExposed(true)
   247  }
   248  
   249  // ClearExposed removes the exposed flag from the service.
   250  // See SetExposed and IsExposed.
   251  func (s *Service) ClearExposed() error {
   252  	return s.setExposed(false)
   253  }
   254  
   255  func (s *Service) setExposed(exposed bool) (err error) {
   256  	ops := []txn.Op{{
   257  		C:      servicesC,
   258  		Id:     s.doc.DocID,
   259  		Assert: isAliveDoc,
   260  		Update: bson.D{{"$set", bson.D{{"exposed", exposed}}}},
   261  	}}
   262  	if err := s.st.runTransaction(ops); err != nil {
   263  		return fmt.Errorf("cannot set exposed flag for service %q to %v: %v", s, exposed, onAbort(err, errNotAlive))
   264  	}
   265  	s.doc.Exposed = exposed
   266  	return nil
   267  }
   268  
   269  // Charm returns the service's charm and whether units should upgrade to that
   270  // charm even if they are in an error state.
   271  func (s *Service) Charm() (ch *Charm, force bool, err error) {
   272  	ch, err = s.st.Charm(s.doc.CharmURL)
   273  	if err != nil {
   274  		return nil, false, err
   275  	}
   276  	return ch, s.doc.ForceCharm, nil
   277  }
   278  
   279  // IsPrincipal returns whether units of the service can
   280  // have subordinate units.
   281  func (s *Service) IsPrincipal() bool {
   282  	return !s.doc.Subordinate
   283  }
   284  
   285  // CharmURL returns the service's charm URL, and whether units should upgrade
   286  // to the charm with that URL even if they are in an error state.
   287  func (s *Service) CharmURL() (curl *charm.URL, force bool) {
   288  	return s.doc.CharmURL, s.doc.ForceCharm
   289  }
   290  
   291  // Endpoints returns the service's currently available relation endpoints.
   292  func (s *Service) Endpoints() (eps []Endpoint, err error) {
   293  	ch, _, err := s.Charm()
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  	collect := func(role charm.RelationRole, rels map[string]charm.Relation) {
   298  		for _, rel := range rels {
   299  			eps = append(eps, Endpoint{
   300  				ServiceName: s.doc.Name,
   301  				Relation:    rel,
   302  			})
   303  		}
   304  	}
   305  	meta := ch.Meta()
   306  	collect(charm.RolePeer, meta.Peers)
   307  	collect(charm.RoleProvider, meta.Provides)
   308  	collect(charm.RoleRequirer, meta.Requires)
   309  	collect(charm.RoleProvider, map[string]charm.Relation{
   310  		"juju-info": {
   311  			Name:      "juju-info",
   312  			Role:      charm.RoleProvider,
   313  			Interface: "juju-info",
   314  			Scope:     charm.ScopeGlobal,
   315  		},
   316  	})
   317  	sort.Sort(epSlice(eps))
   318  	return eps, nil
   319  }
   320  
   321  // Endpoint returns the relation endpoint with the supplied name, if it exists.
   322  func (s *Service) Endpoint(relationName string) (Endpoint, error) {
   323  	eps, err := s.Endpoints()
   324  	if err != nil {
   325  		return Endpoint{}, err
   326  	}
   327  	for _, ep := range eps {
   328  		if ep.Name == relationName {
   329  			return ep, nil
   330  		}
   331  	}
   332  	return Endpoint{}, fmt.Errorf("service %q has no %q relation", s, relationName)
   333  }
   334  
   335  // extraPeerRelations returns only the peer relations in newMeta not
   336  // present in the service's current charm meta data.
   337  func (s *Service) extraPeerRelations(newMeta *charm.Meta) map[string]charm.Relation {
   338  	if newMeta == nil {
   339  		// This should never happen, since we're checking the charm in SetCharm already.
   340  		panic("newMeta is nil")
   341  	}
   342  	ch, _, err := s.Charm()
   343  	if err != nil {
   344  		return nil
   345  	}
   346  	newPeers := newMeta.Peers
   347  	oldPeers := ch.Meta().Peers
   348  	extraPeers := make(map[string]charm.Relation)
   349  	for relName, rel := range newPeers {
   350  		if _, ok := oldPeers[relName]; !ok {
   351  			extraPeers[relName] = rel
   352  		}
   353  	}
   354  	return extraPeers
   355  }
   356  
   357  func (s *Service) checkRelationsOps(ch *Charm, relations []*Relation) ([]txn.Op, error) {
   358  	asserts := make([]txn.Op, 0, len(relations))
   359  	// All relations must still exist and their endpoints are implemented by the charm.
   360  	for _, rel := range relations {
   361  		if ep, err := rel.Endpoint(s.doc.Name); err != nil {
   362  			return nil, err
   363  		} else if !ep.ImplementedBy(ch) {
   364  			return nil, fmt.Errorf("cannot upgrade service %q to charm %q: would break relation %q", s, ch, rel)
   365  		}
   366  		asserts = append(asserts, txn.Op{
   367  			C:      relationsC,
   368  			Id:     rel.doc.DocID,
   369  			Assert: txn.DocExists,
   370  		})
   371  	}
   372  	return asserts, nil
   373  }
   374  
   375  // changeCharmOps returns the operations necessary to set a service's
   376  // charm URL to a new value.
   377  func (s *Service) changeCharmOps(ch *Charm, force bool) ([]txn.Op, error) {
   378  	// Build the new service config from what can be used of the old one.
   379  	var newSettings charm.Settings
   380  	oldSettings, err := readSettings(s.st, s.settingsKey())
   381  	if err == nil {
   382  		// Filter the old settings through to get the new settings.
   383  		newSettings = ch.Config().FilterSettings(oldSettings.Map())
   384  	} else if errors.IsNotFound(err) {
   385  		// No old settings, start with empty new settings.
   386  		newSettings = make(charm.Settings)
   387  	} else {
   388  		return nil, errors.Trace(err)
   389  	}
   390  
   391  	// Create or replace service settings.
   392  	var settingsOp txn.Op
   393  	newKey := serviceSettingsKey(s.doc.Name, ch.URL())
   394  	if _, err := readSettings(s.st, newKey); errors.IsNotFound(err) {
   395  		// No settings for this key yet, create it.
   396  		settingsOp = createSettingsOp(s.st, newKey, newSettings)
   397  	} else if err != nil {
   398  		return nil, errors.Trace(err)
   399  	} else {
   400  		// Settings exist, just replace them with the new ones.
   401  		settingsOp, _, err = replaceSettingsOp(s.st, newKey, newSettings)
   402  		if err != nil {
   403  			return nil, errors.Trace(err)
   404  		}
   405  	}
   406  
   407  	// Add or create a reference to the new settings doc.
   408  	incOp, err := settingsIncRefOp(s.st, s.doc.Name, ch.URL(), true)
   409  	if err != nil {
   410  		return nil, errors.Trace(err)
   411  	}
   412  	var decOps []txn.Op
   413  	// Drop the reference to the old settings doc (if they exist).
   414  	if oldSettings != nil {
   415  		decOps, err = settingsDecRefOps(s.st, s.doc.Name, s.doc.CharmURL) // current charm
   416  		if err != nil {
   417  			return nil, errors.Trace(err)
   418  		}
   419  	}
   420  
   421  	// Build the transaction.
   422  	var ops []txn.Op
   423  	differentCharm := bson.D{{"charmurl", bson.D{{"$ne", ch.URL()}}}}
   424  	if oldSettings != nil {
   425  		// Old settings shouldn't change (when they exist).
   426  		ops = append(ops, oldSettings.assertUnchangedOp())
   427  	}
   428  	ops = append(ops, []txn.Op{
   429  		// Create or replace new settings.
   430  		settingsOp,
   431  		// Increment the ref count.
   432  		incOp,
   433  		// Update the charm URL and force flag (if relevant).
   434  		{
   435  			C:      servicesC,
   436  			Id:     s.doc.DocID,
   437  			Assert: append(notDeadDoc, differentCharm...),
   438  			Update: bson.D{{"$set", bson.D{{"charmurl", ch.URL()}, {"forcecharm", force}}}},
   439  		},
   440  	}...)
   441  	// Add any extra peer relations that need creation.
   442  	newPeers := s.extraPeerRelations(ch.Meta())
   443  	peerOps, err := s.st.addPeerRelationsOps(s.doc.Name, newPeers)
   444  	if err != nil {
   445  		return nil, errors.Trace(err)
   446  	}
   447  
   448  	// Get all relations - we need to check them later.
   449  	relations, err := s.Relations()
   450  	if err != nil {
   451  		return nil, errors.Trace(err)
   452  	}
   453  	// Make sure the relation count does not change.
   454  	sameRelCount := bson.D{{"relationcount", len(relations)}}
   455  
   456  	ops = append(ops, peerOps...)
   457  	// Update the relation count as well.
   458  	ops = append(ops, txn.Op{
   459  		C:      servicesC,
   460  		Id:     s.doc.DocID,
   461  		Assert: append(notDeadDoc, sameRelCount...),
   462  		Update: bson.D{{"$inc", bson.D{{"relationcount", len(newPeers)}}}},
   463  	})
   464  	// Check relations to ensure no active relations are removed.
   465  	relOps, err := s.checkRelationsOps(ch, relations)
   466  	if err != nil {
   467  		return nil, errors.Trace(err)
   468  	}
   469  	ops = append(ops, relOps...)
   470  
   471  	// And finally, decrement the old settings.
   472  	return append(ops, decOps...), nil
   473  }
   474  
   475  // SetCharm changes the charm for the service. New units will be started with
   476  // this charm, and existing units will be upgraded to use it. If force is true,
   477  // units will be upgraded even if they are in an error state.
   478  func (s *Service) SetCharm(ch *Charm, force bool) error {
   479  	if ch.Meta().Subordinate != s.doc.Subordinate {
   480  		return errors.Errorf("cannot change a service's subordinacy")
   481  	}
   482  	if ch.URL().Series != s.doc.Series {
   483  		return errors.Errorf("cannot change a service's series")
   484  	}
   485  
   486  	services, closer := s.st.getCollection(servicesC)
   487  	defer closer()
   488  
   489  	buildTxn := func(attempt int) ([]txn.Op, error) {
   490  		if attempt > 0 {
   491  			// NOTE: We're explicitly allowing SetCharm to succeed
   492  			// when the service is Dying, because service/charm
   493  			// upgrades should still be allowed to apply to dying
   494  			// services and units, so that bugs in departed/broken
   495  			// hooks can be addressed at runtime.
   496  			if notDead, err := isNotDeadWithSession(services, s.doc.DocID); err != nil {
   497  				return nil, errors.Trace(err)
   498  			} else if !notDead {
   499  				return nil, ErrDead
   500  			}
   501  		}
   502  		// Make sure the service doesn't have this charm already.
   503  		sel := bson.D{{"_id", s.doc.DocID}, {"charmurl", ch.URL()}}
   504  		var ops []txn.Op
   505  		if count, err := services.Find(sel).Count(); err != nil {
   506  			return nil, errors.Trace(err)
   507  		} else if count == 1 {
   508  			// Charm URL already set; just update the force flag.
   509  			sameCharm := bson.D{{"charmurl", ch.URL()}}
   510  			ops = []txn.Op{{
   511  				C:      servicesC,
   512  				Id:     s.doc.DocID,
   513  				Assert: append(notDeadDoc, sameCharm...),
   514  				Update: bson.D{{"$set", bson.D{{"forcecharm", force}}}},
   515  			}}
   516  		} else {
   517  			// Change the charm URL.
   518  			ops, err = s.changeCharmOps(ch, force)
   519  			if err != nil {
   520  				return nil, errors.Trace(err)
   521  			}
   522  		}
   523  		return ops, nil
   524  	}
   525  	err := s.st.run(buildTxn)
   526  	if err == nil {
   527  		s.doc.CharmURL = ch.URL()
   528  		s.doc.ForceCharm = force
   529  	}
   530  	return err
   531  }
   532  
   533  // String returns the service name.
   534  func (s *Service) String() string {
   535  	return s.doc.Name
   536  }
   537  
   538  // Refresh refreshes the contents of the Service from the underlying
   539  // state. It returns an error that satisfies errors.IsNotFound if the
   540  // service has been removed.
   541  func (s *Service) Refresh() error {
   542  	services, closer := s.st.getCollection(servicesC)
   543  	defer closer()
   544  
   545  	err := services.FindId(s.doc.DocID).One(&s.doc)
   546  	if err == mgo.ErrNotFound {
   547  		return errors.NotFoundf("service %q", s)
   548  	}
   549  	if err != nil {
   550  		return fmt.Errorf("cannot refresh service %q: %v", s, err)
   551  	}
   552  	return nil
   553  }
   554  
   555  // newUnitName returns the next unit name.
   556  func (s *Service) newUnitName() (string, error) {
   557  	services, closer := s.st.getCollection(servicesC)
   558  	defer closer()
   559  
   560  	change := mgo.Change{Update: bson.D{{"$inc", bson.D{{"unitseq", 1}}}}}
   561  	result := serviceDoc{}
   562  	if _, err := services.Find(bson.D{{"_id", s.doc.DocID}}).Apply(change, &result); err == mgo.ErrNotFound {
   563  		return "", errors.NotFoundf("service %q", s)
   564  	} else if err != nil {
   565  		return "", fmt.Errorf("cannot increment unit sequence: %v", err)
   566  	}
   567  	name := s.doc.Name + "/" + strconv.Itoa(result.UnitSeq)
   568  	return name, nil
   569  }
   570  
   571  // addUnitOps returns a unique name for a new unit, and a list of txn operations
   572  // necessary to create that unit. The principalName param must be non-empty if
   573  // and only if s is a subordinate service. Only one subordinate of a given
   574  // service will be assigned to a given principal. The asserts param can be used
   575  // to include additional assertions for the service document.
   576  func (s *Service) addUnitOps(principalName string, asserts bson.D) (string, []txn.Op, error) {
   577  	if s.doc.Subordinate && principalName == "" {
   578  		return "", nil, fmt.Errorf("service is a subordinate")
   579  	} else if !s.doc.Subordinate && principalName != "" {
   580  		return "", nil, fmt.Errorf("service is not a subordinate")
   581  	}
   582  	name, err := s.newUnitName()
   583  	if err != nil {
   584  		return "", nil, err
   585  	}
   586  
   587  	// Create instances of the charm's declared stores.
   588  	storageInstanceOps, storageInstanceIds, err := s.unitStorageInstanceOps(name)
   589  	if err != nil {
   590  		return "", nil, errors.Trace(err)
   591  	}
   592  
   593  	docID := s.st.docID(name)
   594  	globalKey := unitGlobalKey(name)
   595  	udoc := &unitDoc{
   596  		DocID:            docID,
   597  		Name:             name,
   598  		EnvUUID:          s.doc.EnvUUID,
   599  		Service:          s.doc.Name,
   600  		Series:           s.doc.Series,
   601  		Life:             Alive,
   602  		Principal:        principalName,
   603  		StorageInstances: storageInstanceIds,
   604  	}
   605  	sdoc := statusDoc{
   606  		Status:  StatusAllocating,
   607  		EnvUUID: s.st.EnvironUUID(),
   608  	}
   609  	ops := []txn.Op{
   610  		{
   611  			C:      unitsC,
   612  			Id:     docID,
   613  			Assert: txn.DocMissing,
   614  			Insert: udoc,
   615  		},
   616  		createStatusOp(s.st, globalKey, sdoc),
   617  		createMeterStatusOp(s.st, globalKey, &meterStatusDoc{Code: MeterNotSet}),
   618  		{
   619  			C:      servicesC,
   620  			Id:     s.doc.DocID,
   621  			Assert: append(isAliveDoc, asserts...),
   622  			Update: bson.D{{"$inc", bson.D{{"unitcount", 1}}}},
   623  		},
   624  	}
   625  	ops = append(ops, storageInstanceOps...)
   626  
   627  	if s.doc.Subordinate {
   628  		ops = append(ops, txn.Op{
   629  			C:  unitsC,
   630  			Id: s.st.docID(principalName),
   631  			Assert: append(isAliveDoc, bson.DocElem{
   632  				"subordinates", bson.D{{"$not", bson.RegEx{Pattern: "^" + s.doc.Name + "/"}}},
   633  			}),
   634  			Update: bson.D{{"$addToSet", bson.D{{"subordinates", name}}}},
   635  		})
   636  	} else {
   637  		scons, err := s.Constraints()
   638  		if err != nil {
   639  			return "", nil, err
   640  		}
   641  		cons, err := s.st.resolveConstraints(scons)
   642  		if err != nil {
   643  			return "", nil, err
   644  		}
   645  		ops = append(ops, createConstraintsOp(s.st, globalKey, cons))
   646  	}
   647  	return name, ops, nil
   648  }
   649  
   650  // createUnitStorageInstanceOps returns transactions operations for
   651  // creating storage instances for a new unit.
   652  func (s *Service) unitStorageInstanceOps(unitName string) (ops []txn.Op, storageInstanceIds []string, err error) {
   653  	cons, err := s.StorageConstraints()
   654  	if err != nil {
   655  		return nil, nil, err
   656  	}
   657  	charm, _, err := s.Charm()
   658  	if err != nil {
   659  		return nil, nil, err
   660  	}
   661  	meta := charm.Meta()
   662  	tag := names.NewUnitTag(unitName)
   663  	ops, storageInstanceIds, err = createStorageInstanceOps(s.st, tag, meta, cons)
   664  	if err != nil {
   665  		return nil, nil, errors.Trace(err)
   666  	}
   667  	return ops, storageInstanceIds, nil
   668  }
   669  
   670  // SCHEMACHANGE
   671  // TODO(mattyw) remove when schema upgrades are possible
   672  func (s *Service) GetOwnerTag() string {
   673  	owner := s.doc.OwnerTag
   674  	if owner == "" {
   675  		// We know that if there was no owner, it was created with an early
   676  		// version of juju, and that admin was the only user.
   677  		owner = names.NewUserTag("admin").String()
   678  	}
   679  	return owner
   680  }
   681  
   682  // AddUnit adds a new principal unit to the service.
   683  func (s *Service) AddUnit() (unit *Unit, err error) {
   684  	defer errors.DeferredAnnotatef(&err, "cannot add unit to service %q", s)
   685  	name, ops, err := s.addUnitOps("", nil)
   686  	if err != nil {
   687  		return nil, err
   688  	}
   689  	if err := s.st.runTransaction(ops); err == txn.ErrAborted {
   690  		if alive, err := isAlive(s.st, servicesC, s.doc.DocID); err != nil {
   691  			return nil, err
   692  		} else if !alive {
   693  			return nil, fmt.Errorf("service is not alive")
   694  		}
   695  		return nil, fmt.Errorf("inconsistent state")
   696  	} else if err != nil {
   697  		return nil, err
   698  	}
   699  	return s.st.Unit(name)
   700  }
   701  
   702  // removeUnitOps returns the operations necessary to remove the supplied unit,
   703  // assuming the supplied asserts apply to the unit document.
   704  func (s *Service) removeUnitOps(u *Unit, asserts bson.D) ([]txn.Op, error) {
   705  	ops, err := u.destroyHostOps(s)
   706  	if err != nil {
   707  		return nil, err
   708  	}
   709  	portsOps, err := removePortsForUnitOps(s.st, u)
   710  	if err != nil {
   711  		return nil, err
   712  	}
   713  	storageInstanceOps, err := removeStorageInstancesOps(s.st, u.Tag())
   714  	if err != nil {
   715  		return nil, err
   716  	}
   717  
   718  	observedFieldsMatch := bson.D{
   719  		{"charmurl", u.doc.CharmURL},
   720  		{"machineid", u.doc.MachineId},
   721  	}
   722  	ops = append(ops, txn.Op{
   723  		C:      unitsC,
   724  		Id:     u.doc.DocID,
   725  		Assert: append(observedFieldsMatch, asserts...),
   726  		Remove: true,
   727  	},
   728  		removeConstraintsOp(s.st, u.globalKey()),
   729  		removeStatusOp(s.st, u.globalKey()),
   730  		removeMeterStatusOp(s.st, u.globalKey()),
   731  		annotationRemoveOp(s.st, u.globalKey()),
   732  		s.st.newCleanupOp(cleanupRemovedUnit, u.doc.Name),
   733  	)
   734  	ops = append(ops, portsOps...)
   735  	ops = append(ops, storageInstanceOps...)
   736  	if u.doc.CharmURL != nil {
   737  		decOps, err := settingsDecRefOps(s.st, s.doc.Name, u.doc.CharmURL)
   738  		if errors.IsNotFound(err) {
   739  			return nil, errRefresh
   740  		} else if err != nil {
   741  			return nil, err
   742  		}
   743  		ops = append(ops, decOps...)
   744  	}
   745  	if s.doc.Life == Dying && s.doc.RelationCount == 0 && s.doc.UnitCount == 1 {
   746  		hasLastRef := bson.D{{"life", Dying}, {"relationcount", 0}, {"unitcount", 1}}
   747  		return append(ops, s.removeOps(hasLastRef)...), nil
   748  	}
   749  	svcOp := txn.Op{
   750  		C:      servicesC,
   751  		Id:     s.doc.DocID,
   752  		Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}},
   753  	}
   754  	if s.doc.Life == Alive {
   755  		svcOp.Assert = bson.D{{"life", Alive}, {"unitcount", bson.D{{"$gt", 0}}}}
   756  	} else {
   757  		svcOp.Assert = bson.D{
   758  			{"life", Dying},
   759  			{"$or", []bson.D{
   760  				{{"unitcount", bson.D{{"$gt", 1}}}},
   761  				{{"relationcount", bson.D{{"$gt", 0}}}},
   762  			}},
   763  		}
   764  	}
   765  	ops = append(ops, svcOp)
   766  
   767  	return ops, nil
   768  }
   769  
   770  // AllUnits returns all units of the service.
   771  func (s *Service) AllUnits() (units []*Unit, err error) {
   772  	return allUnits(s.st, s.doc.Name)
   773  }
   774  
   775  func allUnits(st *State, service string) (units []*Unit, err error) {
   776  	unitsCollection, closer := st.getCollection(unitsC)
   777  	defer closer()
   778  
   779  	docs := []unitDoc{}
   780  	err = unitsCollection.Find(bson.D{{"service", service}}).All(&docs)
   781  	if err != nil {
   782  		return nil, fmt.Errorf("cannot get all units from service %q: %v", service, err)
   783  	}
   784  	for i := range docs {
   785  		units = append(units, newUnit(st, &docs[i]))
   786  	}
   787  	return units, nil
   788  }
   789  
   790  // Relations returns a Relation for every relation the service is in.
   791  func (s *Service) Relations() (relations []*Relation, err error) {
   792  	return serviceRelations(s.st, s.doc.Name)
   793  }
   794  
   795  func serviceRelations(st *State, name string) (relations []*Relation, err error) {
   796  	defer errors.DeferredAnnotatef(&err, "can't get relations for service %q", name)
   797  	relationsCollection, closer := st.getCollection(relationsC)
   798  	defer closer()
   799  
   800  	docs := []relationDoc{}
   801  	err = relationsCollection.Find(bson.D{{"endpoints.servicename", name}}).All(&docs)
   802  	if err != nil {
   803  		return nil, err
   804  	}
   805  	for _, v := range docs {
   806  		relations = append(relations, newRelation(st, &v))
   807  	}
   808  	return relations, nil
   809  }
   810  
   811  // ConfigSettings returns the raw user configuration for the service's charm.
   812  // Unset values are omitted.
   813  func (s *Service) ConfigSettings() (charm.Settings, error) {
   814  	settings, err := readSettings(s.st, s.settingsKey())
   815  	if err != nil {
   816  		return nil, err
   817  	}
   818  	return settings.Map(), nil
   819  }
   820  
   821  // UpdateConfigSettings changes a service's charm config settings. Values set
   822  // to nil will be deleted; unknown and invalid values will return an error.
   823  func (s *Service) UpdateConfigSettings(changes charm.Settings) error {
   824  	charm, _, err := s.Charm()
   825  	if err != nil {
   826  		return err
   827  	}
   828  	changes, err = charm.Config().ValidateSettings(changes)
   829  	if err != nil {
   830  		return err
   831  	}
   832  	// TODO(fwereade) state.Settings is itself really problematic in just
   833  	// about every use case. This needs to be resolved some time; but at
   834  	// least the settings docs are keyed by charm url as well as service
   835  	// name, so the actual impact of a race is non-threatening.
   836  	node, err := readSettings(s.st, s.settingsKey())
   837  	if err != nil {
   838  		return err
   839  	}
   840  	for name, value := range changes {
   841  		if value == nil {
   842  			node.Delete(name)
   843  		} else {
   844  			node.Set(name, value)
   845  		}
   846  	}
   847  	_, err = node.Write()
   848  	return err
   849  }
   850  
   851  var ErrSubordinateConstraints = stderrors.New("constraints do not apply to subordinate services")
   852  
   853  // Constraints returns the current service constraints.
   854  func (s *Service) Constraints() (constraints.Value, error) {
   855  	if s.doc.Subordinate {
   856  		return constraints.Value{}, ErrSubordinateConstraints
   857  	}
   858  	return readConstraints(s.st, s.globalKey())
   859  }
   860  
   861  // SetConstraints replaces the current service constraints.
   862  func (s *Service) SetConstraints(cons constraints.Value) (err error) {
   863  	unsupported, err := s.st.validateConstraints(cons)
   864  	if len(unsupported) > 0 {
   865  		logger.Warningf(
   866  			"setting constraints on service %q: unsupported constraints: %v", s.Name(), strings.Join(unsupported, ","))
   867  	} else if err != nil {
   868  		return err
   869  	}
   870  	if s.doc.Subordinate {
   871  		return ErrSubordinateConstraints
   872  	}
   873  	defer errors.DeferredAnnotatef(&err, "cannot set constraints")
   874  	if s.doc.Life != Alive {
   875  		return errNotAlive
   876  	}
   877  	ops := []txn.Op{
   878  		{
   879  			C:      servicesC,
   880  			Id:     s.doc.DocID,
   881  			Assert: isAliveDoc,
   882  		},
   883  		setConstraintsOp(s.st, s.globalKey(), cons),
   884  	}
   885  	return onAbort(s.st.runTransaction(ops), errNotAlive)
   886  }
   887  
   888  // Networks returns the networks a service is associated with. Unlike
   889  // networks specified with constraints, these networks are required to
   890  // be present on machines hosting this service's units.
   891  func (s *Service) Networks() ([]string, error) {
   892  	return readRequestedNetworks(s.st, s.globalKey())
   893  }
   894  
   895  // MetricCredentials returns any metric credentials associated with this service.
   896  func (s *Service) MetricCredentials() []byte {
   897  	return s.doc.MetricCredentials
   898  }
   899  
   900  // SetMetricCredentials updates the metric credentials associated with this service.
   901  func (s *Service) SetMetricCredentials(b []byte) error {
   902  	buildTxn := func(attempt int) ([]txn.Op, error) {
   903  		if attempt > 0 {
   904  			alive, err := isAlive(s.st, servicesC, s.doc.DocID)
   905  			if err != nil {
   906  				return nil, errors.Trace(err)
   907  			} else if !alive {
   908  				return nil, errNotAlive
   909  			}
   910  		}
   911  		ops := []txn.Op{
   912  			{
   913  				C:      servicesC,
   914  				Id:     s.doc.DocID,
   915  				Assert: isAliveDoc,
   916  				Update: bson.M{"$set": bson.M{"metric-credentials": b}},
   917  			},
   918  		}
   919  		return ops, nil
   920  	}
   921  	if err := s.st.run(buildTxn); err != nil {
   922  		if err == errNotAlive {
   923  			return errors.New("cannot update metric credentials: service " + err.Error())
   924  		}
   925  		return errors.Annotatef(err, "cannot update metric credentials")
   926  	}
   927  	s.doc.MetricCredentials = b
   928  	return nil
   929  }
   930  
   931  func (s *Service) StorageConstraints() (map[string]StorageConstraints, error) {
   932  	return readStorageConstraints(s.st, s.globalKey())
   933  }
   934  
   935  // settingsIncRefOp returns an operation that increments the ref count
   936  // of the service settings identified by serviceName and curl. If
   937  // canCreate is false, a missing document will be treated as an error;
   938  // otherwise, it will be created with a ref count of 1.
   939  func settingsIncRefOp(st *State, serviceName string, curl *charm.URL, canCreate bool) (txn.Op, error) {
   940  	settingsrefs, closer := st.getCollection(settingsrefsC)
   941  	defer closer()
   942  
   943  	key := serviceSettingsKey(serviceName, curl)
   944  	if count, err := settingsrefs.FindId(key).Count(); err != nil {
   945  		return txn.Op{}, err
   946  	} else if count == 0 {
   947  		if !canCreate {
   948  			return txn.Op{}, errors.NotFoundf("service %q settings for charm %q", serviceName, curl)
   949  		}
   950  		return txn.Op{
   951  			C:      settingsrefsC,
   952  			Id:     st.docID(key),
   953  			Assert: txn.DocMissing,
   954  			Insert: settingsRefsDoc{
   955  				RefCount: 1,
   956  				EnvUUID:  st.EnvironUUID()},
   957  		}, nil
   958  	}
   959  	return txn.Op{
   960  		C:      settingsrefsC,
   961  		Id:     st.docID(key),
   962  		Assert: txn.DocExists,
   963  		Update: bson.D{{"$inc", bson.D{{"refcount", 1}}}},
   964  	}, nil
   965  }
   966  
   967  // settingsDecRefOps returns a list of operations that decrement the
   968  // ref count of the service settings identified by serviceName and
   969  // curl. If the ref count is set to zero, the appropriate setting and
   970  // ref count documents will both be deleted.
   971  func settingsDecRefOps(st *State, serviceName string, curl *charm.URL) ([]txn.Op, error) {
   972  	settingsrefs, closer := st.getCollection(settingsrefsC)
   973  	defer closer()
   974  
   975  	key := serviceSettingsKey(serviceName, curl)
   976  	var doc settingsRefsDoc
   977  	if err := settingsrefs.FindId(key).One(&doc); err == mgo.ErrNotFound {
   978  		return nil, errors.NotFoundf("service %q settings for charm %q", serviceName, curl)
   979  	} else if err != nil {
   980  		return nil, err
   981  	}
   982  	docID := st.docID(key)
   983  	if doc.RefCount == 1 {
   984  		return []txn.Op{{
   985  			C:      settingsrefsC,
   986  			Id:     docID,
   987  			Assert: bson.D{{"refcount", 1}},
   988  			Remove: true,
   989  		}, {
   990  			C:      settingsC,
   991  			Id:     docID,
   992  			Remove: true,
   993  		}}, nil
   994  	}
   995  	return []txn.Op{{
   996  		C:      settingsrefsC,
   997  		Id:     docID,
   998  		Assert: bson.D{{"refcount", bson.D{{"$gt", 1}}}},
   999  		Update: bson.D{{"$inc", bson.D{{"refcount", -1}}}},
  1000  	}}, nil
  1001  }
  1002  
  1003  // settingsRefsDoc holds the number of units and services using the
  1004  // settings document identified by the document's id. Every time a
  1005  // service upgrades its charm the settings doc ref count for the new
  1006  // charm url is incremented, and the old settings is ref count is
  1007  // decremented. When a unit upgrades to the new charm, the old service
  1008  // settings ref count is decremented and the ref count of the new
  1009  // charm settings is incremented. The last unit upgrading to the new
  1010  // charm is responsible for deleting the old charm's settings doc.
  1011  //
  1012  // Note: We're not using the settingsDoc for this because changing
  1013  // just the ref count is not considered a change worth reporting
  1014  // to watchers and firing config-changed hooks.
  1015  //
  1016  // There is an implicit _id field here, which mongo creates, which is
  1017  // always the same as the settingsDoc's id.
  1018  type settingsRefsDoc struct {
  1019  	RefCount int
  1020  	EnvUUID  string `bson:"env-uuid"`
  1021  }