github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/relationunit.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	stderrors "errors"
     8  	"fmt"
     9  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  	jujutxn "github.com/juju/txn"
    13  	"gopkg.in/juju/charm.v6-unstable"
    14  	"gopkg.in/juju/names.v2"
    15  	"gopkg.in/mgo.v2"
    16  	"gopkg.in/mgo.v2/bson"
    17  	"gopkg.in/mgo.v2/txn"
    18  
    19  	"github.com/juju/juju/network"
    20  )
    21  
    22  // RelationUnit holds information about a single unit in a relation, and
    23  // allows clients to conveniently access unit-specific functionality.
    24  type RelationUnit struct {
    25  	st       *State
    26  	relation *Relation
    27  	unit     *Unit
    28  	endpoint Endpoint
    29  	scope    string
    30  }
    31  
    32  // Relation returns the relation associated with the unit.
    33  func (ru *RelationUnit) Relation() *Relation {
    34  	return ru.relation
    35  }
    36  
    37  // Endpoint returns the relation endpoint that defines the unit's
    38  // participation in the relation.
    39  func (ru *RelationUnit) Endpoint() Endpoint {
    40  	return ru.endpoint
    41  }
    42  
    43  // PrivateAddress returns the private address of the unit.
    44  func (ru *RelationUnit) PrivateAddress() (network.Address, error) {
    45  	return ru.unit.PrivateAddress()
    46  }
    47  
    48  // ErrCannotEnterScope indicates that a relation unit failed to enter its scope
    49  // due to either the unit or the relation not being Alive.
    50  var ErrCannotEnterScope = stderrors.New("cannot enter scope: unit or relation is not alive")
    51  
    52  // ErrCannotEnterScopeYet indicates that a relation unit failed to enter its
    53  // scope due to a required and pre-existing subordinate unit that is not Alive.
    54  // Once that subordinate has been removed, a new one can be created.
    55  var ErrCannotEnterScopeYet = stderrors.New("cannot enter scope yet: non-alive subordinate unit has not been removed")
    56  
    57  // EnterScope ensures that the unit has entered its scope in the relation.
    58  // When the unit has already entered its relation scope, EnterScope will report
    59  // success but make no changes to state.
    60  //
    61  // Otherwise, assuming both the relation and the unit are alive, it will enter
    62  // scope and create or overwrite the unit's settings in the relation according
    63  // to the supplied map.
    64  //
    65  // If the unit is a principal and the relation has container scope, EnterScope
    66  // will also create the required subordinate unit, if it does not already exist;
    67  // this is because there's no point having a principal in scope if there is no
    68  // corresponding subordinate to join it.
    69  //
    70  // Once a unit has entered a scope, it stays in scope without further
    71  // intervention; the relation will not be able to become Dead until all units
    72  // have departed its scopes.
    73  func (ru *RelationUnit) EnterScope(settings map[string]interface{}) error {
    74  	db, closer := ru.st.newDB()
    75  	defer closer()
    76  	relationScopes, closer := db.GetCollection(relationScopesC)
    77  	defer closer()
    78  
    79  	// Verify that the unit is not already in scope, and abort without error
    80  	// if it is.
    81  	ruKey := ru.key()
    82  	if count, err := relationScopes.FindId(ruKey).Count(); err != nil {
    83  		return err
    84  	} else if count != 0 {
    85  		return nil
    86  	}
    87  
    88  	// Collect the operations necessary to enter scope, as follows:
    89  	// * Check unit and relation state, and incref the relation.
    90  	// * TODO(fwereade): check unit status == params.StatusActive (this
    91  	//   breaks a bunch of tests in a boring but noisy-to-fix way, and is
    92  	//   being saved for a followup).
    93  	unitDocID, relationDocID := ru.unit.doc.DocID, ru.relation.doc.DocID
    94  	ops := []txn.Op{{
    95  		C:      unitsC,
    96  		Id:     unitDocID,
    97  		Assert: isAliveDoc,
    98  	}, {
    99  		C:      relationsC,
   100  		Id:     relationDocID,
   101  		Assert: isAliveDoc,
   102  		Update: bson.D{{"$inc", bson.D{{"unitcount", 1}}}},
   103  	}}
   104  
   105  	// * Create the unit settings in this relation, if they do not already
   106  	//   exist; or completely overwrite them if they do. This must happen
   107  	//   before we create the scope doc, because the existence of a scope doc
   108  	//   is considered to be a guarantee of the existence of a settings doc.
   109  	settingsChanged := func() (bool, error) { return false, nil }
   110  	settingsColl, closer := db.GetCollection(settingsC)
   111  	defer closer()
   112  	if count, err := settingsColl.FindId(ruKey).Count(); err != nil {
   113  		return err
   114  	} else if count == 0 {
   115  		ops = append(ops, createSettingsOp(settingsC, ruKey, settings))
   116  	} else {
   117  		var rop txn.Op
   118  		rop, settingsChanged, err = replaceSettingsOp(ru.st, settingsC, ruKey, settings)
   119  		if err != nil {
   120  			return err
   121  		}
   122  		ops = append(ops, rop)
   123  	}
   124  
   125  	// * Create the scope doc.
   126  	ops = append(ops, txn.Op{
   127  		C:      relationScopesC,
   128  		Id:     ruKey,
   129  		Assert: txn.DocMissing,
   130  		Insert: relationScopeDoc{
   131  			Key: ruKey,
   132  		},
   133  	})
   134  
   135  	// * If the unit should have a subordinate, and does not, create it.
   136  	var existingSubName string
   137  	if subOps, subName, err := ru.subordinateOps(); err != nil {
   138  		return err
   139  	} else {
   140  		existingSubName = subName
   141  		ops = append(ops, subOps...)
   142  	}
   143  
   144  	// Now run the complete transaction, or figure out why we can't.
   145  	if err := ru.st.runTransaction(ops); err != txn.ErrAborted {
   146  		return err
   147  	}
   148  	if count, err := relationScopes.FindId(ruKey).Count(); err != nil {
   149  		return err
   150  	} else if count != 0 {
   151  		// The scope document exists, so we're actually already in scope.
   152  		return nil
   153  	}
   154  
   155  	units, closer := db.GetCollection(unitsC)
   156  	defer closer()
   157  	relations, closer := db.GetCollection(relationsC)
   158  	defer closer()
   159  
   160  	// The relation or unit might no longer be Alive. (Note that there is no
   161  	// need for additional checks if we're trying to create a subordinate
   162  	// unit: this could fail due to the subordinate service's not being Alive,
   163  	// but this case will always be caught by the check for the relation's
   164  	// life (because a relation cannot be Alive if its services are not).)
   165  	if alive, err := isAliveWithSession(units, unitDocID); err != nil {
   166  		return err
   167  	} else if !alive {
   168  		return ErrCannotEnterScope
   169  	}
   170  	if alive, err := isAliveWithSession(relations, relationDocID); err != nil {
   171  		return err
   172  	} else if !alive {
   173  		return ErrCannotEnterScope
   174  	}
   175  
   176  	// Maybe a subordinate used to exist, but is no longer alive. If that is
   177  	// case, we will be unable to enter scope until that unit is gone.
   178  	if existingSubName != "" {
   179  		if alive, err := isAliveWithSession(units, existingSubName); err != nil {
   180  			return err
   181  		} else if !alive {
   182  			return ErrCannotEnterScopeYet
   183  		}
   184  	}
   185  
   186  	// It's possible that there was a pre-existing settings doc whose version
   187  	// has changed under our feet, preventing us from clearing it properly; if
   188  	// that is the case, something is seriously wrong (nobody else should be
   189  	// touching that doc under our feet) and we should bail out.
   190  	prefix := fmt.Sprintf("cannot enter scope for unit %q in relation %q: ", ru.unit, ru.relation)
   191  	if changed, err := settingsChanged(); err != nil {
   192  		return err
   193  	} else if changed {
   194  		return fmt.Errorf(prefix + "concurrent settings change detected")
   195  	}
   196  
   197  	// Apparently, all our assertions should have passed, but the txn was
   198  	// aborted: something is really seriously wrong.
   199  	return fmt.Errorf(prefix + "inconsistent state in EnterScope")
   200  }
   201  
   202  // subordinateOps returns any txn operations necessary to ensure sane
   203  // subordinate state when entering scope. If a required subordinate unit
   204  // exists and is Alive, its name will be returned as well; if one exists
   205  // but is not Alive, ErrCannotEnterScopeYet is returned.
   206  func (ru *RelationUnit) subordinateOps() ([]txn.Op, string, error) {
   207  	units, closer := ru.st.getCollection(unitsC)
   208  	defer closer()
   209  
   210  	if !ru.unit.IsPrincipal() || ru.endpoint.Scope != charm.ScopeContainer {
   211  		return nil, "", nil
   212  	}
   213  	related, err := ru.relation.RelatedEndpoints(ru.endpoint.ApplicationName)
   214  	if err != nil {
   215  		return nil, "", err
   216  	}
   217  	if len(related) != 1 {
   218  		return nil, "", fmt.Errorf("expected single related endpoint, got %v", related)
   219  	}
   220  	applicationname, unitName := related[0].ApplicationName, ru.unit.doc.Name
   221  	selSubordinate := bson.D{{"application", applicationname}, {"principal", unitName}}
   222  	var lDoc lifeDoc
   223  	if err := units.Find(selSubordinate).One(&lDoc); err == mgo.ErrNotFound {
   224  		application, err := ru.st.Application(applicationname)
   225  		if err != nil {
   226  			return nil, "", err
   227  		}
   228  		_, ops, err := application.addUnitOps(unitName, nil)
   229  		return ops, "", err
   230  	} else if err != nil {
   231  		return nil, "", err
   232  	} else if lDoc.Life != Alive {
   233  		return nil, "", ErrCannotEnterScopeYet
   234  	}
   235  	return []txn.Op{{
   236  		C:      unitsC,
   237  		Id:     lDoc.Id,
   238  		Assert: isAliveDoc,
   239  	}}, lDoc.Id, nil
   240  }
   241  
   242  // PrepareLeaveScope causes the unit to be reported as departed by watchers,
   243  // but does not *actually* leave the scope, to avoid triggering relation
   244  // cleanup.
   245  func (ru *RelationUnit) PrepareLeaveScope() error {
   246  	relationScopes, closer := ru.st.getCollection(relationScopesC)
   247  	defer closer()
   248  
   249  	key := ru.key()
   250  	if count, err := relationScopes.FindId(key).Count(); err != nil {
   251  		return err
   252  	} else if count == 0 {
   253  		return nil
   254  	}
   255  	ops := []txn.Op{{
   256  		C:      relationScopesC,
   257  		Id:     key,
   258  		Update: bson.D{{"$set", bson.D{{"departing", true}}}},
   259  	}}
   260  	return ru.st.runTransaction(ops)
   261  }
   262  
   263  // LeaveScope signals that the unit has left its scope in the relation.
   264  // After the unit has left its relation scope, it is no longer a member
   265  // of the relation; if the relation is dying when its last member unit
   266  // leaves, it is removed immediately. It is not an error to leave a scope
   267  // that the unit is not, or never was, a member of.
   268  func (ru *RelationUnit) LeaveScope() error {
   269  	relationScopes, closer := ru.st.getCollection(relationScopesC)
   270  	defer closer()
   271  
   272  	key := ru.key()
   273  	// The logic below is involved because we remove a dying relation
   274  	// with the last unit that leaves a scope in it. It handles three
   275  	// possible cases:
   276  	//
   277  	// 1. Relation is alive: just leave the scope.
   278  	//
   279  	// 2. Relation is dying, and other units remain: just leave the scope.
   280  	//
   281  	// 3. Relation is dying, and this is the last unit: leave the scope
   282  	//    and remove the relation.
   283  	//
   284  	// In each of those cases, proper assertions are done to guarantee
   285  	// that the condition observed is still valid when the transaction is
   286  	// applied. If an abort happens, it observes the new condition and
   287  	// retries. In theory, a worst case will try at most all of the
   288  	// conditions once, because units cannot join a scope once its relation
   289  	// is dying.
   290  	//
   291  	// Keep in mind that in the first iteration of the loop it's possible
   292  	// to have a Dying relation with a smaller-than-real unit count, because
   293  	// Destroy changes the Life attribute in memory (units could join before
   294  	// the database is actually changed).
   295  	desc := fmt.Sprintf("unit %q in relation %q", ru.unit, ru.relation)
   296  	buildTxn := func(attempt int) ([]txn.Op, error) {
   297  		if attempt > 0 {
   298  			if err := ru.relation.Refresh(); errors.IsNotFound(err) {
   299  				return nil, jujutxn.ErrNoOperations
   300  			} else if err != nil {
   301  				return nil, err
   302  			}
   303  		}
   304  		count, err := relationScopes.FindId(key).Count()
   305  		if err != nil {
   306  			return nil, fmt.Errorf("cannot examine scope for %s: %v", desc, err)
   307  		} else if count == 0 {
   308  			return nil, jujutxn.ErrNoOperations
   309  		}
   310  		ops := []txn.Op{{
   311  			C:      relationScopesC,
   312  			Id:     key,
   313  			Assert: txn.DocExists,
   314  			Remove: true,
   315  		}}
   316  		if ru.relation.doc.Life == Alive {
   317  			ops = append(ops, txn.Op{
   318  				C:      relationsC,
   319  				Id:     ru.relation.doc.DocID,
   320  				Assert: bson.D{{"life", Alive}},
   321  				Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}},
   322  			})
   323  		} else if ru.relation.doc.UnitCount > 1 {
   324  			ops = append(ops, txn.Op{
   325  				C:      relationsC,
   326  				Id:     ru.relation.doc.DocID,
   327  				Assert: bson.D{{"unitcount", bson.D{{"$gt", 1}}}},
   328  				Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}},
   329  			})
   330  		} else {
   331  			relOps, err := ru.relation.removeOps("", ru.unit)
   332  			if err != nil {
   333  				return nil, err
   334  			}
   335  			ops = append(ops, relOps...)
   336  		}
   337  		return ops, nil
   338  	}
   339  	if err := ru.st.run(buildTxn); err != nil {
   340  		return errors.Annotatef(err, "cannot leave scope for %s", desc)
   341  	}
   342  	return nil
   343  }
   344  
   345  // InScope returns whether the relation unit has entered scope and not left it.
   346  func (ru *RelationUnit) InScope() (bool, error) {
   347  	return ru.inScope(nil)
   348  }
   349  
   350  // Joined returns whether the relation unit has entered scope and neither left
   351  // it nor prepared to leave it.
   352  func (ru *RelationUnit) Joined() (bool, error) {
   353  	return ru.inScope(bson.D{{"departing", bson.D{{"$ne", true}}}})
   354  }
   355  
   356  // inScope returns whether a scope document exists satisfying the supplied
   357  // selector.
   358  func (ru *RelationUnit) inScope(sel bson.D) (bool, error) {
   359  	relationScopes, closer := ru.st.getCollection(relationScopesC)
   360  	defer closer()
   361  
   362  	sel = append(sel, bson.D{{"_id", ru.key()}}...)
   363  	count, err := relationScopes.Find(sel).Count()
   364  	if err != nil {
   365  		return false, err
   366  	}
   367  	return count > 0, nil
   368  }
   369  
   370  // WatchScope returns a watcher which notifies of counterpart units
   371  // entering and leaving the unit's scope.
   372  func (ru *RelationUnit) WatchScope() *RelationScopeWatcher {
   373  	role := counterpartRole(ru.endpoint.Role)
   374  	scope := ru.scope + "#" + string(role)
   375  	return newRelationScopeWatcher(ru.st, scope, ru.unit.Name())
   376  }
   377  
   378  // Settings returns a Settings which allows access to the unit's settings
   379  // within the relation.
   380  func (ru *RelationUnit) Settings() (*Settings, error) {
   381  	return readSettings(ru.st, settingsC, ru.key())
   382  }
   383  
   384  // ReadSettings returns a map holding the settings of the unit with the
   385  // supplied name within this relation. An error will be returned if the
   386  // relation no longer exists, or if the unit's service is not part of the
   387  // relation, or the settings are invalid; but mere non-existence of the
   388  // unit is not grounds for an error, because the unit settings are
   389  // guaranteed to persist for the lifetime of the relation, regardless
   390  // of the lifetime of the unit.
   391  func (ru *RelationUnit) ReadSettings(uname string) (m map[string]interface{}, err error) {
   392  	defer errors.DeferredAnnotatef(&err, "cannot read settings for unit %q in relation %q", uname, ru.relation)
   393  	if !names.IsValidUnit(uname) {
   394  		return nil, fmt.Errorf("%q is not a valid unit name", uname)
   395  	}
   396  	key, err := ru.unitKey(uname)
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  	node, err := readSettings(ru.st, settingsC, key)
   401  	if err != nil {
   402  		return nil, err
   403  	}
   404  	return node.Map(), nil
   405  }
   406  
   407  // unitKey returns a string, based on the relation and the supplied unit name,
   408  // which is used as a key for that unit within this relation in the settings,
   409  // presence, and relationScopes collections.
   410  func (ru *RelationUnit) unitKey(uname string) (string, error) {
   411  	uparts := strings.Split(uname, "/")
   412  	sname := uparts[0]
   413  	ep, err := ru.relation.Endpoint(sname)
   414  	if err != nil {
   415  		return "", err
   416  	}
   417  	return ru._key(string(ep.Role), uname), nil
   418  }
   419  
   420  // key returns a string, based on the relation and the current unit name,
   421  // which is used as a key for that unit within this relation in the settings,
   422  // presence, and relationScopes collections.
   423  func (ru *RelationUnit) key() string {
   424  	return ru._key(string(ru.endpoint.Role), ru.unit.Name())
   425  }
   426  
   427  func (ru *RelationUnit) _key(role, unitname string) string {
   428  	parts := []string{ru.scope, role, unitname}
   429  	return strings.Join(parts, "#")
   430  }
   431  
   432  // relationScopeDoc represents a unit which is in a relation scope.
   433  // The relation, container, role, and unit are all encoded in the key.
   434  type relationScopeDoc struct {
   435  	DocID     string `bson:"_id"`
   436  	Key       string `bson:"key"`
   437  	ModelUUID string `bson:"model-uuid"`
   438  	Departing bool
   439  }
   440  
   441  func (d *relationScopeDoc) unitName() string {
   442  	return unitNameFromScopeKey(d.Key)
   443  }
   444  
   445  func unitNameFromScopeKey(key string) string {
   446  	parts := strings.Split(key, "#")
   447  	return parts[len(parts)-1]
   448  }