github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/juju/charm/v12"
    12  	"github.com/juju/collections/set"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/mgo/v3"
    15  	"github.com/juju/mgo/v3/bson"
    16  	"github.com/juju/mgo/v3/txn"
    17  	"github.com/juju/names/v5"
    18  	jujutxn "github.com/juju/txn/v3"
    19  	"github.com/kr/pretty"
    20  
    21  	stateerrors "github.com/juju/juju/state/errors"
    22  )
    23  
    24  var rulogger = logger.Child("relationunit")
    25  
    26  // RelationUnit holds information about a single unit in a relation, and
    27  // allows clients to conveniently access unit-specific functionality.
    28  type RelationUnit struct {
    29  	st          *State
    30  	relation    *Relation
    31  	unitName    string
    32  	isPrincipal bool
    33  	endpoint    Endpoint
    34  	scope       string
    35  
    36  	// isLocalUnit is true for relation units representing
    37  	// the local side of a cross model relation, or for
    38  	// any 2 units in a non cross model relation.
    39  	isLocalUnit bool
    40  }
    41  
    42  // Relation returns the relation associated with the unit.
    43  func (ru *RelationUnit) Relation() *Relation {
    44  	return ru.relation
    45  }
    46  
    47  // Endpoint returns the relation endpoint that defines the unit's
    48  // participation in the relation.
    49  func (ru *RelationUnit) Endpoint() Endpoint {
    50  	return ru.endpoint
    51  }
    52  
    53  // UnitName returns the name of the unit in the relation.
    54  func (ru *RelationUnit) UnitName() string {
    55  	return ru.unitName
    56  }
    57  
    58  // EnterScope ensures that the unit has entered its scope in the relation.
    59  // When the unit has already entered its relation scope, EnterScope will report
    60  // success but make no changes to state.
    61  //
    62  // Otherwise, assuming both the relation and the unit are alive, it will enter
    63  // scope and create or overwrite the unit's settings in the relation according
    64  // to the supplied map.
    65  //
    66  // If the unit is a principal and the relation has container scope, EnterScope
    67  // will also create the required subordinate unit, if it does not already exist;
    68  // this is because there's no point having a principal in scope if there is no
    69  // corresponding subordinate to join it.
    70  //
    71  // Once a unit has entered a scope, it stays in scope without further
    72  // intervention; the relation will not be able to become Dead until all units
    73  // have departed its scopes.
    74  func (ru *RelationUnit) EnterScope(settings map[string]interface{}) error {
    75  	db, dbCloser := ru.st.newDB()
    76  	defer dbCloser()
    77  	relationScopes, rsCloser := db.GetCollection(relationScopesC)
    78  	defer rsCloser()
    79  	ruKey := ru.key()
    80  	relationDocID := ru.relation.doc.DocID
    81  
    82  	var settingsChanged func() (bool, error)
    83  	var existingSubName string
    84  	prefix := fmt.Sprintf("unit %q in relation %q: ", ru.unitName, ru.relation)
    85  
    86  	buildTxn := func(attempt int) ([]txn.Op, error) {
    87  		// Before retrying the transaction, check the following
    88  		// assertions:
    89  		if attempt > 0 {
    90  			if count, err := relationScopes.FindId(ruKey).Count(); err != nil {
    91  				return nil, errors.Trace(err)
    92  			} else if count != 0 {
    93  				// The scope document exists, so we're actually already in scope.
    94  				return nil, nil
    95  			}
    96  
    97  			// The relation or unit might no longer be Alive. (Note that there is no
    98  			// need for additional checks if we're trying to create a subordinate
    99  			// unit: this could fail due to the subordinate applications not being Alive,
   100  			// but this case will always be caught by the check for the relation's
   101  			// life (because a relation cannot be Alive if its applications are not).)
   102  			relations, rCloser := db.GetCollection(relationsC)
   103  			defer rCloser()
   104  			if alive, err := isAliveWithSession(relations, relationDocID); err != nil {
   105  				return nil, errors.Trace(err)
   106  			} else if !alive {
   107  				return nil, errors.Annotate(stateerrors.ErrCannotEnterScope, prefix+"relation is no longer alive")
   108  			}
   109  			if ru.isLocalUnit {
   110  				units, uCloser := db.GetCollection(unitsC)
   111  				defer uCloser()
   112  				if alive, err := isAliveWithSession(units, ru.unitName); err != nil {
   113  					return nil, errors.Trace(err)
   114  				} else if !alive {
   115  					return nil, errors.Annotate(stateerrors.ErrCannotEnterScope, prefix+"unit is no longer alive")
   116  
   117  				}
   118  
   119  				// Maybe a subordinate used to exist, but is no longer alive. If that is
   120  				// case, we will be unable to enter scope until that unit is gone.
   121  				if existingSubName != "" {
   122  					if alive, err := isAliveWithSession(units, existingSubName); err != nil {
   123  						return nil, errors.Trace(err)
   124  					} else if !alive {
   125  						return nil, errors.Annotatef(stateerrors.ErrCannotEnterScopeYet, prefix+"subordinate %v is no longer alive", existingSubName)
   126  					}
   127  				}
   128  			}
   129  
   130  			// It's possible that there was a pre-existing settings doc whose version
   131  			// has changed under our feet, preventing us from clearing it properly; if
   132  			// that is the case, something is seriously wrong (nobody else should be
   133  			// touching that doc under our feet) and we should bail out.
   134  			if changed, err := settingsChanged(); err != nil {
   135  				return nil, errors.Trace(err)
   136  			} else if changed {
   137  				return nil, fmt.Errorf(prefix + "concurrent settings change detected")
   138  			}
   139  		}
   140  
   141  		// Verify that the unit is not already in scope, and exit without error
   142  		// if it is.
   143  		if count, err := relationScopes.FindId(ruKey).Count(); err != nil {
   144  			return nil, errors.Trace(err)
   145  		} else if count != 0 {
   146  			return nil, nil
   147  		}
   148  
   149  		// Collect the operations necessary to enter scope, as follows:
   150  		// * Check unit and relation state, and incref the relation.
   151  		// * TODO(fwereade): check unit status == params.StatusActive (this
   152  		//   breaks a bunch of tests in a boring but noisy-to-fix way, and is
   153  		//   being saved for a followup).
   154  		var ops []txn.Op
   155  		if ru.isLocalUnit {
   156  			ops = append(ops, txn.Op{
   157  				C:      unitsC,
   158  				Id:     ru.unitName,
   159  				Assert: isAliveDoc,
   160  			})
   161  		}
   162  		ops = append(ops, txn.Op{
   163  			C:      relationsC,
   164  			Id:     relationDocID,
   165  			Assert: isAliveDoc,
   166  			Update: bson.D{{"$inc", bson.D{{"unitcount", 1}}}},
   167  		})
   168  
   169  		// * Create the unit settings in this relation, if they do not already
   170  		//   exist; or completely overwrite them if they do. This must happen
   171  		//   before we create the scope doc, because the existence of a scope doc
   172  		//   is considered to be a guarantee of the existence of a settings doc.
   173  		settingsColl, sCloser := db.GetCollection(settingsC)
   174  		defer sCloser()
   175  		if count, err := settingsColl.FindId(ruKey).Count(); err != nil {
   176  			return nil, errors.Trace(err)
   177  		} else if count == 0 {
   178  			ops = append(ops, createSettingsOp(settingsC, ruKey, settings))
   179  			settingsChanged = func() (bool, error) { return false, nil }
   180  		} else {
   181  			var rop txn.Op
   182  			rop, settingsChanged, err = replaceSettingsOp(ru.st.db(), settingsC, ruKey, settings)
   183  			if err != nil {
   184  				return nil, errors.Trace(err)
   185  			}
   186  			ops = append(ops, rop)
   187  		}
   188  
   189  		// * Create the scope doc.
   190  		ops = append(ops, txn.Op{
   191  			C:      relationScopesC,
   192  			Id:     ruKey,
   193  			Assert: txn.DocMissing,
   194  			Insert: relationScopeDoc{
   195  				Key: ruKey,
   196  			},
   197  		})
   198  
   199  		// * If the unit should have a subordinate, and does not, create it.
   200  		if subOps, subName, err := ru.subordinateOps(); err != nil {
   201  			return nil, errors.Trace(err)
   202  		} else {
   203  			existingSubName = subName
   204  			ops = append(ops, subOps...)
   205  		}
   206  		return ops, nil
   207  	}
   208  
   209  	// Now run the complete transaction.
   210  	return ru.st.db().Run(buildTxn)
   211  }
   212  
   213  // CounterpartApplications returns the slice of application names that are the counterpart of this unit.
   214  // (So for Peer relations, app is returned, for a Provider the apps on Requirer side is returned
   215  func (ru *RelationUnit) CounterpartApplications() []string {
   216  	counterApps := set.NewStrings()
   217  	counterRole := counterpartRole(ru.endpoint.Role)
   218  	for _, ep := range ru.relation.Endpoints() {
   219  		if ep.Role == counterRole {
   220  			counterApps.Add(ep.ApplicationName)
   221  		}
   222  	}
   223  	return counterApps.SortedValues()
   224  }
   225  
   226  // counterpartApplicationSettingsKeys is the database keys of related applications.
   227  func (ru *RelationUnit) counterpartApplicationSettingsKeys() []string {
   228  	counterpartApps := ru.CounterpartApplications()
   229  	out := make([]string, len(counterpartApps))
   230  	for i, appName := range counterpartApps {
   231  		out[i] = relationApplicationSettingsKey(ru.relation.Id(), appName)
   232  	}
   233  	return out
   234  }
   235  
   236  // subordinateOps returns any txn operations necessary to ensure sane
   237  // subordinate state when entering scope. If a required subordinate unit
   238  // exists and is Alive, its name will be returned as well; if one exists
   239  // but is not Alive, ErrCannotEnterScopeYet is returned.
   240  func (ru *RelationUnit) subordinateOps() ([]txn.Op, string, error) {
   241  	units, closer := ru.st.db().GetCollection(unitsC)
   242  	defer closer()
   243  
   244  	if !ru.isPrincipal || ru.endpoint.Scope != charm.ScopeContainer {
   245  		return nil, "", nil
   246  	}
   247  	related, err := ru.relation.RelatedEndpoints(ru.endpoint.ApplicationName)
   248  	if err != nil {
   249  		return nil, "", err
   250  	}
   251  	if len(related) != 1 {
   252  		return nil, "", errors.Errorf("expected single related endpoint, got %v", related)
   253  	}
   254  	// Find the machine ID that the principal unit is deployed on, and use
   255  	// that for the subordinate. It is worthwhile noting that if the unit is
   256  	// in a CAAS model, there are no machines.
   257  	principal, err := ru.st.Unit(ru.unitName)
   258  	if err != nil {
   259  		return nil, "", errors.Annotate(err, "unable to load principal unit")
   260  	}
   261  	var principalMachineID string
   262  	if principal.ShouldBeAssigned() {
   263  		// We don't care just now if the machine isn't assigned, as CAAS models
   264  		// will return that error. For IAAS models, the machine *should* always
   265  		// be assigned before it is able to enter scope.
   266  		// We don't check the error here now because it'll cause *huge* test
   267  		// fallout as many tests don't follow reality, particularly when
   268  		// relations are being tested.
   269  		principalMachineID, _ = principal.AssignedMachineId()
   270  	}
   271  
   272  	applicationname, unitName := related[0].ApplicationName, ru.unitName
   273  	selSubordinate := bson.D{{"application", applicationname}, {"principal", unitName}}
   274  	var lDoc lifeDoc
   275  	if err := units.Find(selSubordinate).One(&lDoc); err == mgo.ErrNotFound {
   276  		application, err := ru.st.Application(applicationname)
   277  		if err != nil {
   278  			return nil, "", err
   279  		}
   280  		_, ops, err := application.addUnitOps(unitName, AddUnitParams{
   281  			machineID: principalMachineID,
   282  		}, nil)
   283  		return ops, "", err
   284  	} else if err != nil {
   285  		return nil, "", err
   286  	} else if lDoc.Life != Alive {
   287  		return nil, "", stateerrors.ErrCannotEnterScopeYet
   288  	}
   289  	return []txn.Op{{
   290  		C:      unitsC,
   291  		Id:     lDoc.Id,
   292  		Assert: isAliveDoc,
   293  	}}, lDoc.Id, nil
   294  }
   295  
   296  // PrepareLeaveScope causes the unit to be reported as departed by watchers,
   297  // but does not *actually* leave the scope, to avoid triggering relation
   298  // cleanup.
   299  func (ru *RelationUnit) PrepareLeaveScope() error {
   300  	relationScopes, closer := ru.st.db().GetCollection(relationScopesC)
   301  	defer closer()
   302  
   303  	key := ru.key()
   304  	if count, err := relationScopes.FindId(key).Count(); err != nil {
   305  		return err
   306  	} else if count == 0 {
   307  		return nil
   308  	}
   309  	ops := []txn.Op{{
   310  		C:      relationScopesC,
   311  		Id:     key,
   312  		Update: bson.D{{"$set", bson.D{{"departing", true}}}},
   313  	}}
   314  	return ru.st.db().RunTransaction(ops)
   315  }
   316  
   317  // LeaveScopeOperation returns a model operation that will allow relation to leave scope.
   318  func (ru *RelationUnit) LeaveScopeOperation(force bool) *LeaveScopeOperation {
   319  	return &LeaveScopeOperation{
   320  		ru: &RelationUnit{
   321  			st:          ru.st,
   322  			relation:    ru.relation,
   323  			unitName:    ru.unitName,
   324  			isPrincipal: ru.isPrincipal,
   325  			endpoint:    ru.endpoint,
   326  			scope:       ru.scope,
   327  			isLocalUnit: ru.isLocalUnit,
   328  		},
   329  		ForcedOperation: ForcedOperation{Force: force},
   330  	}
   331  }
   332  
   333  // LeaveScopeOperation is a model operation for relation to leave scope.
   334  type LeaveScopeOperation struct {
   335  	// ForcedOperation stores needed information to force this operation.
   336  	ForcedOperation
   337  
   338  	// ru holds the unit relation that wants to leave scope.
   339  	ru *RelationUnit
   340  }
   341  
   342  // Build is part of the ModelOperation interface.
   343  func (op *LeaveScopeOperation) Build(attempt int) ([]txn.Op, error) {
   344  	rulogger.Tracef("%v attempt %d to leave scope", op.Description(), attempt+1)
   345  	if attempt > 0 {
   346  		if err := op.ru.relation.Refresh(); errors.IsNotFound(err) {
   347  			return nil, jujutxn.ErrNoOperations
   348  		} else if err != nil {
   349  			return nil, err
   350  		}
   351  	}
   352  	// When 'force' is set on the operation, this call will return needed operations
   353  	// and accumulate all operational errors encountered in the operation.
   354  	// If the 'force' is not set, any error will be fatal and no operations will be returned.
   355  	switch ops, err := op.internalLeaveScope(); err {
   356  	case errRefresh:
   357  	case errAlreadyDying:
   358  		return nil, jujutxn.ErrNoOperations
   359  	case nil:
   360  		return ops, nil
   361  	default:
   362  		if op.Force {
   363  			rulogger.Warningf("forcing %v to leave scope despite error %v", op.Description(), err)
   364  			return ops, nil
   365  		}
   366  		return nil, err
   367  	}
   368  	return nil, jujutxn.ErrNoOperations
   369  }
   370  
   371  func (op *LeaveScopeOperation) Description() string {
   372  	return fmt.Sprintf("unit %q in relation %q", op.ru.unitName, op.ru.relation)
   373  }
   374  
   375  // Done is part of the ModelOperation interface.
   376  func (op *LeaveScopeOperation) Done(err error) error {
   377  	if err != nil {
   378  		if !op.Force {
   379  			return errors.Annotatef(err, "%v cannot leave scope", op.Description())
   380  		}
   381  		op.AddError(errors.Errorf("%v tried to forcefully leave scope but proceeded despite encountering ERROR %v", op.Description(), err))
   382  	}
   383  	return nil
   384  }
   385  
   386  // LeaveScopeWithForce in addition to doing what LeaveScope() does,
   387  // when force is passed in as 'true', forces relation unit to leave scope,
   388  // ignoring errors.
   389  func (ru *RelationUnit) LeaveScopeWithForce(force bool, maxWait time.Duration) ([]error, error) {
   390  	op := ru.LeaveScopeOperation(force)
   391  	op.MaxWait = maxWait
   392  	err := ru.st.ApplyOperation(op)
   393  	return op.Errors, err
   394  }
   395  
   396  // LeaveScope signals that the unit has left its scope in the relation.
   397  // After the unit has left its relation scope, it is no longer a member
   398  // of the relation; if the relation is dying when its last member unit
   399  // leaves, it is removed immediately. It is not an error to leave a scope
   400  // that the unit is not, or never was, a member of.
   401  func (ru *RelationUnit) LeaveScope() error {
   402  	errs, err := ru.LeaveScopeWithForce(false, time.Duration(0))
   403  	if len(errs) != 0 {
   404  		rulogger.Warningf("operational errors leaving scope for unit %q in relation %q: %v", ru.unitName, ru.relation, errs)
   405  	}
   406  	return err
   407  }
   408  
   409  // leaveScopeForcedOps is an internal method used by other state objects when they just want
   410  // to get database operations that are involved in leaving scope without
   411  // the actual immediate act of leaving scope.
   412  func (ru *RelationUnit) leaveScopeForcedOps(existingOperation *ForcedOperation) ([]txn.Op, error) {
   413  	// It does not matter that we are say false to force here- we'll overwrite the whole ForcedOperation.
   414  	leaveScopeOperation := ru.LeaveScopeOperation(false)
   415  	leaveScopeOperation.ForcedOperation = *existingOperation
   416  	return leaveScopeOperation.internalLeaveScope()
   417  }
   418  
   419  // When 'force' is set, this call will return needed operations
   420  // and will accumulate all operational errors encountered in the operation.
   421  // If the 'force' is not set, any error will be fatal and no operations will be applied.
   422  func (op *LeaveScopeOperation) internalLeaveScope() ([]txn.Op, error) {
   423  	relationScopes, closer := op.ru.st.db().GetCollection(relationScopesC)
   424  	defer closer()
   425  
   426  	key := op.ru.key()
   427  	// The logic below is involved because we remove a dying relation
   428  	// with the last unit that leaves a scope in it. It handles three
   429  	// possible cases:
   430  	//
   431  	// 1. Relation is alive: just leave the scope.
   432  	//
   433  	// 2. Relation is dying, and other units remain: just leave the scope.
   434  	//
   435  	// 3. Relation is dying, and this is the last unit: leave the scope
   436  	//    and remove the relation.
   437  	//
   438  	// In each of those cases, proper assertions are done to guarantee
   439  	// that the condition observed is still valid when the transaction is
   440  	// applied. If an abort happens, it observes the new condition and
   441  	// retries. In theory, a worst case will try at most all of the
   442  	// conditions once, because units cannot join a scope once its relation
   443  	// is dying.
   444  	//
   445  	// Keep in mind that in the first iteration of the loop it's possible
   446  	// to have a Dying relation with a smaller-than-real unit count, because
   447  	// Destroy changes the Life attribute in memory (units could join before
   448  	// the database is actually changed).
   449  	rulogger.Debugf("%v leaving scope: unit count %d, life %v", op.Description(), op.ru.relation.doc.UnitCount, op.ru.relation.doc.Life)
   450  	count, err := relationScopes.FindId(key).Count()
   451  	if op.FatalError(errors.Annotatef(err, "cannot examine scope for %s", op.Description())) {
   452  		return nil, err
   453  	} else if count == 0 {
   454  		return nil, jujutxn.ErrNoOperations
   455  	}
   456  	ops := []txn.Op{{
   457  		C:      relationScopesC,
   458  		Id:     key,
   459  		Assert: txn.DocExists,
   460  		Remove: true,
   461  	}}
   462  	if op.ru.relation.doc.Life == Alive {
   463  		ops = append(ops, txn.Op{
   464  			C:      relationsC,
   465  			Id:     op.ru.relation.doc.DocID,
   466  			Assert: bson.D{{"life", Alive}},
   467  			Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}},
   468  		})
   469  	} else if op.ru.relation.doc.UnitCount > 1 {
   470  		ops = append(ops, txn.Op{
   471  			C:      relationsC,
   472  			Id:     op.ru.relation.doc.DocID,
   473  			Assert: bson.D{{"unitcount", bson.D{{"$gt", 1}}}},
   474  			Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}},
   475  		})
   476  	} else {
   477  		// When 'force' is set, this call will return needed operations
   478  		// and accumulate all operational errors encountered in the operation.
   479  		// If the 'force' is not set, any error will be fatal and no operations will be returned.
   480  		relOps, err := op.ru.relation.removeOps("", op.ru.unitName, &op.ForcedOperation)
   481  		if op.FatalError(err) {
   482  			return nil, err
   483  		}
   484  		ops = append(ops, relOps...)
   485  	}
   486  	if rulogger.IsTraceEnabled() {
   487  		rulogger.Tracef("leave scope ops for %s: %s", op.Description(), pretty.Sprint(ops))
   488  	}
   489  	return ops, nil
   490  }
   491  
   492  // Valid returns whether this RelationUnit is one that can actually
   493  // exist in the relation. For container-scoped relations, RUs can be
   494  // created for subordinate units whose principal unit isn't a member
   495  // of the relation. There are too many places that rely on being able
   496  // to construct a nonsensical RU to query InScope or Joined, so we
   497  // allow them to be constructed but they will always return false for
   498  // Valid.
   499  // TODO(babbageclunk): unpick the reliance on creating invalid RUs.
   500  func (ru *RelationUnit) Valid() (bool, error) {
   501  	if ru.endpoint.Scope != charm.ScopeContainer || ru.isPrincipal {
   502  		return true, nil
   503  	}
   504  	// A subordinate container-scoped relation unit is valid if:
   505  	// the other end of the relation is also a subordinate charm
   506  	// or its principal unit is also a member of the relation.
   507  	appName, err := names.UnitApplication(ru.unitName)
   508  	if err != nil {
   509  		return false, errors.Trace(err)
   510  	}
   511  	var otherAppName string
   512  	for _, ep := range ru.relation.Endpoints() {
   513  		if ep.ApplicationName != appName {
   514  			otherAppName = ep.ApplicationName
   515  		}
   516  	}
   517  	if otherAppName == "" {
   518  		return false, errors.Errorf("couldn't find other endpoint")
   519  	}
   520  	otherApp, err := ru.st.Application(otherAppName)
   521  	if err != nil {
   522  		return false, errors.Trace(err)
   523  	}
   524  	if !otherApp.IsPrincipal() {
   525  		return true, nil
   526  	}
   527  
   528  	unit, err := ru.st.Unit(ru.unitName)
   529  	if err != nil {
   530  		return false, errors.Trace(err)
   531  	}
   532  	// No need to check the flag here - we know we're subordinate.
   533  	pName, _ := unit.PrincipalName()
   534  	principalAppName, err := names.UnitApplication(pName)
   535  	if err != nil {
   536  		return false, errors.Trace(err)
   537  	}
   538  	// If the other application is a principal, only allow it if it's in the relation.
   539  	_, err = ru.relation.Endpoint(principalAppName)
   540  	if errors.IsNotFound(err) {
   541  		return false, nil
   542  	} else if err != nil {
   543  		return false, errors.Trace(err)
   544  	}
   545  	return true, nil
   546  }
   547  
   548  // InScope returns whether the relation unit has entered scope and not left it.
   549  func (ru *RelationUnit) InScope() (bool, error) {
   550  	return ru.inScope(nil)
   551  }
   552  
   553  // Joined returns whether the relation unit has entered scope and neither left
   554  // it nor prepared to leave it.
   555  func (ru *RelationUnit) Joined() (bool, error) {
   556  	return ru.inScope(bson.D{{"departing", bson.D{{"$ne", true}}}})
   557  }
   558  
   559  // inScope returns whether a scope document exists satisfying the supplied
   560  // selector.
   561  func (ru *RelationUnit) inScope(sel bson.D) (bool, error) {
   562  	relationScopes, closer := ru.st.db().GetCollection(relationScopesC)
   563  	defer closer()
   564  
   565  	sel = append(sel, bson.D{{"_id", ru.key()}}...)
   566  	count, err := relationScopes.Find(sel).Count()
   567  	if err != nil {
   568  		return false, err
   569  	}
   570  	return count > 0, nil
   571  }
   572  
   573  // WatchScope returns a watcher which notifies of counterpart units
   574  // entering and leaving the unit's scope.
   575  func (ru *RelationUnit) WatchScope() *RelationScopeWatcher {
   576  	role := counterpartRole(ru.endpoint.Role)
   577  	return watchRelationScope(ru.st, ru.scope, role, ru.unitName)
   578  }
   579  
   580  func watchRelationScope(
   581  	st *State, scope string, role charm.RelationRole, ignore string,
   582  ) *RelationScopeWatcher {
   583  	scope = scope + "#" + string(role)
   584  	return newRelationScopeWatcher(st, scope, ignore)
   585  }
   586  
   587  // Settings returns a Settings which allows access to the unit's settings
   588  // within the relation.
   589  func (ru *RelationUnit) Settings() (*Settings, error) {
   590  	s, err := readSettings(ru.st.db(), settingsC, ru.key())
   591  	if err != nil {
   592  		return nil, errors.Annotatef(err, "unit %q", ru.unitName)
   593  	}
   594  	return s, nil
   595  }
   596  
   597  // ReadSettings returns a map holding the settings of the unit with the
   598  // supplied name within this relation. An error will be returned if the
   599  // relation no longer exists, or if the unit's application is not part of the
   600  // relation, or the settings are invalid; but mere non-existence of the
   601  // unit is not grounds for an error, because the unit settings are
   602  // guaranteed to persist for the lifetime of the relation, regardless
   603  // of the lifetime of the unit.
   604  func (ru *RelationUnit) ReadSettings(uname string) (m map[string]interface{}, err error) {
   605  	defer errors.DeferredAnnotatef(&err, "cannot read settings for unit %q in relation %q", uname, ru.relation)
   606  	if !names.IsValidUnit(uname) {
   607  		return nil, fmt.Errorf("%q is not a valid unit name", uname)
   608  	}
   609  	key, err := ru.unitKey(uname)
   610  	if err != nil {
   611  		return nil, err
   612  	}
   613  	node, err := readSettings(ru.st.db(), settingsC, key)
   614  	if err != nil {
   615  		return nil, errors.Annotatef(err, "unit %q", uname)
   616  	}
   617  	return node.Map(), nil
   618  }
   619  
   620  // unitKey returns a string, based on the relation and the supplied unit name,
   621  // which is used as a key for that unit within this relation in the settings,
   622  // presence, and relationScopes collections.
   623  func (ru *RelationUnit) unitKey(uname string) (string, error) {
   624  	uparts := strings.Split(uname, "/")
   625  	sname := uparts[0]
   626  	ep, err := ru.relation.Endpoint(sname)
   627  	if err != nil {
   628  		return "", err
   629  	}
   630  	return ru._key(string(ep.Role), uname), nil
   631  }
   632  
   633  // key returns a string, based on the relation and the current unit name,
   634  // which is used as a key for that unit within this relation in the settings,
   635  // presence, and relationScopes collections.
   636  func (ru *RelationUnit) key() string {
   637  	return ru._key(string(ru.endpoint.Role), ru.unitName)
   638  }
   639  
   640  func (ru *RelationUnit) _key(role, unitname string) string {
   641  	parts := []string{ru.scope, role, unitname}
   642  	return strings.Join(parts, "#")
   643  }
   644  
   645  // relationScopeDoc represents a unit which is in a relation scope.
   646  // The relation, container, role, and unit are all encoded in the key.
   647  type relationScopeDoc struct {
   648  	DocID     string `bson:"_id"`
   649  	Key       string `bson:"key"`
   650  	ModelUUID string `bson:"model-uuid"`
   651  	Departing bool   `bson:"departing"`
   652  }
   653  
   654  func (d *relationScopeDoc) unitName() string {
   655  	return unitNameFromScopeKey(d.Key)
   656  }
   657  
   658  func unitNameFromScopeKey(key string) string {
   659  	parts := strings.Split(key, "#")
   660  	return parts[len(parts)-1]
   661  }
   662  
   663  // unpackScopeKey returns the scope, role and unitname from the
   664  // relation scope key.
   665  func unpackScopeKey(key string) (string, string, string, error) {
   666  	if _, localID, ok := splitDocID(key); ok {
   667  		key = localID
   668  	}
   669  	parts := strings.Split(key, "#")
   670  	if len(parts) < 4 {
   671  		return "", "", "", errors.Errorf("%q has too few parts to be a relation scope key", key)
   672  	}
   673  	unitName := parts[len(parts)-1]
   674  	role := parts[len(parts)-2]
   675  	scope := strings.Join(parts[:len(parts)-2], "#")
   676  	return scope, role, unitName, nil
   677  }