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