github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/relation.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  	"fmt"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/juju/charm/v12"
    14  	"github.com/juju/errors"
    15  	"github.com/juju/mgo/v3"
    16  	"github.com/juju/mgo/v3/bson"
    17  	"github.com/juju/mgo/v3/txn"
    18  	"github.com/juju/names/v5"
    19  	jujutxn "github.com/juju/txn/v3"
    20  
    21  	"github.com/juju/juju/core/leadership"
    22  	"github.com/juju/juju/core/status"
    23  )
    24  
    25  // relationKey returns a string describing the relation defined by
    26  // endpoints, for use in various contexts (including error messages).
    27  func relationKey(endpoints []Endpoint) string {
    28  	eps := epSlice{}
    29  	for _, ep := range endpoints {
    30  		eps = append(eps, ep)
    31  	}
    32  	sort.Sort(eps)
    33  	endpointNames := []string{}
    34  	for _, ep := range eps {
    35  		endpointNames = append(endpointNames, ep.String())
    36  	}
    37  	return strings.Join(endpointNames, " ")
    38  }
    39  
    40  // relationDoc is the internal representation of a Relation in MongoDB.
    41  // Note the correspondence with RelationInfo in core/multiwatcher.
    42  type relationDoc struct {
    43  	DocID           string     `bson:"_id"`
    44  	Key             string     `bson:"key"`
    45  	ModelUUID       string     `bson:"model-uuid"`
    46  	Id              int        `bson:"id"`
    47  	Endpoints       []Endpoint `bson:"endpoints"`
    48  	Life            Life       `bson:"life"`
    49  	UnitCount       int        `bson:"unitcount"`
    50  	Suspended       bool       `bson:"suspended"`
    51  	SuspendedReason string     `bson:"suspended-reason"`
    52  }
    53  
    54  // Relation represents a relation between one or two application endpoints.
    55  type Relation struct {
    56  	st  *State
    57  	doc relationDoc
    58  }
    59  
    60  func newRelation(st *State, doc *relationDoc) *Relation {
    61  	return &Relation{
    62  		st:  st,
    63  		doc: *doc,
    64  	}
    65  }
    66  
    67  func (r *Relation) String() string {
    68  	return r.doc.Key
    69  }
    70  
    71  // Tag returns a name identifying the relation.
    72  func (r *Relation) Tag() names.Tag {
    73  	return names.NewRelationTag(r.doc.Key)
    74  }
    75  
    76  // UnitCount is the number of units still in relation scope.
    77  func (r *Relation) UnitCount() int {
    78  	return r.doc.UnitCount
    79  }
    80  
    81  // Suspended returns true if the relation is suspended.
    82  func (r *Relation) Suspended() bool {
    83  	return r.doc.Suspended
    84  }
    85  
    86  // SuspendedReason returns the reason why the relation is suspended.
    87  func (r *Relation) SuspendedReason() string {
    88  	return r.doc.SuspendedReason
    89  }
    90  
    91  // Refresh refreshes the contents of the relation from the underlying
    92  // state. It returns an error that satisfies errors.IsNotFound if the
    93  // relation has been removed.
    94  func (r *Relation) Refresh() error {
    95  	relations, closer := r.st.db().GetCollection(relationsC)
    96  	defer closer()
    97  
    98  	doc := relationDoc{}
    99  	err := relations.FindId(r.doc.DocID).One(&doc)
   100  	if err == mgo.ErrNotFound {
   101  		return errors.NotFoundf("relation %v", r)
   102  	}
   103  	if err != nil {
   104  		return errors.Annotatef(err, "cannot refresh relation %v", r)
   105  	}
   106  	if r.doc.Id != doc.Id {
   107  		// The relation has been destroyed and recreated. This is *not* the
   108  		// same relation; if we pretend it is, we run the risk of violating
   109  		// the lifecycle-only-advances guarantee.
   110  		return errors.NotFoundf("relation %v", r)
   111  	}
   112  	r.doc = doc
   113  	return nil
   114  }
   115  
   116  // Life returns the relation's current life state.
   117  func (r *Relation) Life() Life {
   118  	return r.doc.Life
   119  }
   120  
   121  // Status returns the relation's current status data.
   122  func (r *Relation) Status() (status.StatusInfo, error) {
   123  	rStatus, err := getStatus(r.st.db(), r.globalScope(), "relation")
   124  	if err != nil {
   125  		return rStatus, err
   126  	}
   127  	return rStatus, nil
   128  }
   129  
   130  // SetStatus sets the status of the relation.
   131  func (r *Relation) SetStatus(statusInfo status.StatusInfo) error {
   132  	currentStatus, err := r.Status()
   133  	if err != nil {
   134  		return errors.Trace(err)
   135  	}
   136  
   137  	if currentStatus.Status != statusInfo.Status {
   138  		validTransition := true
   139  		switch statusInfo.Status {
   140  		case status.Broken:
   141  		case status.Suspending:
   142  			validTransition = currentStatus.Status != status.Broken && currentStatus.Status != status.Suspended
   143  		case status.Joining:
   144  			validTransition = currentStatus.Status != status.Broken && currentStatus.Status != status.Joined
   145  		case status.Joined, status.Suspended:
   146  			validTransition = currentStatus.Status != status.Broken
   147  		case status.Error:
   148  			if statusInfo.Message == "" {
   149  				return errors.Errorf("cannot set status %q without info", statusInfo.Status)
   150  			}
   151  		default:
   152  			return errors.NewNotValid(nil, fmt.Sprintf("cannot set invalid status %q", statusInfo.Status))
   153  		}
   154  		if !validTransition {
   155  			return errors.NewNotValid(nil, fmt.Sprintf(
   156  				"cannot set status %q when relation has status %q", statusInfo.Status, currentStatus.Status))
   157  		}
   158  	}
   159  	return setStatus(r.st.db(), setStatusParams{
   160  		badge:     "relation",
   161  		globalKey: r.globalScope(),
   162  		status:    statusInfo.Status,
   163  		message:   statusInfo.Message,
   164  		rawData:   statusInfo.Data,
   165  		updated:   timeOrNow(statusInfo.Since, r.st.clock()),
   166  	})
   167  }
   168  
   169  // SetSuspended sets whether the relation is suspended.
   170  func (r *Relation) SetSuspended(suspended bool, suspendedReason string) error {
   171  	if r.doc.Suspended == suspended {
   172  		return nil
   173  	}
   174  	if !suspended && suspendedReason != "" {
   175  		return errors.New("cannot set suspended reason if not suspended")
   176  	}
   177  
   178  	var buildTxn jujutxn.TransactionSource = func(attempt int) ([]txn.Op, error) {
   179  
   180  		if attempt > 1 {
   181  			if err := r.Refresh(); err != nil {
   182  				return nil, errors.Trace(err)
   183  			}
   184  		}
   185  		return []txn.Op{{
   186  			C:      relationsC,
   187  			Id:     r.doc.DocID,
   188  			Assert: bson.D{{"suspended", r.doc.Suspended}},
   189  			Update: bson.D{
   190  				{"$set", bson.D{{"suspended", suspended}}},
   191  				{"$set", bson.D{{"suspended-reason", suspendedReason}}},
   192  			},
   193  		}}, nil
   194  	}
   195  
   196  	err := r.st.db().Run(buildTxn)
   197  	if err == nil {
   198  		r.doc.Suspended = suspended
   199  	}
   200  	return err
   201  }
   202  
   203  // DestroyOperation returns a model operation that will allow relation to leave scope.
   204  func (r *Relation) DestroyOperation(force bool) *DestroyRelationOperation {
   205  	return &DestroyRelationOperation{
   206  		r:               &Relation{r.st, r.doc},
   207  		ForcedOperation: ForcedOperation{Force: force},
   208  	}
   209  }
   210  
   211  // DestroyRelationOperation is a model operation destroy relation.
   212  type DestroyRelationOperation struct {
   213  	// ForcedOperation stores needed information to force this operation.
   214  	ForcedOperation
   215  
   216  	// r holds the relation to destroy.
   217  	r *Relation
   218  }
   219  
   220  // Build is part of the ModelOperation interface.
   221  func (op *DestroyRelationOperation) Build(attempt int) ([]txn.Op, error) {
   222  	if attempt > 0 {
   223  		if err := op.r.Refresh(); errors.IsNotFound(err) {
   224  			return nil, jujutxn.ErrNoOperations
   225  		} else if err != nil {
   226  			return nil, err
   227  		}
   228  	}
   229  	// When 'force' is set on the operation, this call will return needed operations
   230  	// and accumulate all operational errors encountered in the operation.
   231  	// If the 'force' is not set, any error will be fatal and no operations will be returned.
   232  	switch ops, err := op.internalDestroy(); err {
   233  	case errRefresh:
   234  	case errAlreadyDying:
   235  		return nil, jujutxn.ErrNoOperations
   236  	case nil:
   237  		return ops, nil
   238  	default:
   239  		if op.Force {
   240  			logger.Warningf("force destroying relation %v despite error %v", op.r, err)
   241  			return ops, nil
   242  		}
   243  		return nil, err
   244  	}
   245  	return nil, jujutxn.ErrNoOperations
   246  }
   247  
   248  // Done is part of the ModelOperation interface.
   249  func (op *DestroyRelationOperation) Done(err error) error {
   250  	if err != nil {
   251  		if !op.Force {
   252  			return errors.Annotatef(err, "cannot destroy relation %q", op.r)
   253  		}
   254  		op.AddError(errors.Errorf("forcefully destroying relation %v proceeded despite encountering ERROR %v", op.r, err))
   255  	}
   256  	return nil
   257  }
   258  
   259  // DestroyWithForce may force the destruction of the relation.
   260  // In addition, this function also returns all non-fatal operational errors
   261  // encountered.
   262  func (r *Relation) DestroyWithForce(force bool, maxWait time.Duration) ([]error, error) {
   263  	op := r.DestroyOperation(force)
   264  	op.MaxWait = maxWait
   265  	err := r.st.ApplyOperation(op)
   266  	return op.Errors, err
   267  }
   268  
   269  // Destroy ensures that the relation will be removed at some point; if no units
   270  // are currently in scope, it will be removed immediately.
   271  func (r *Relation) Destroy() error {
   272  	errs, err := r.DestroyWithForce(false, time.Duration(0))
   273  	if len(errs) != 0 {
   274  		logger.Warningf("operational errors removing relation %v: %v", r.Id(), errs)
   275  	}
   276  	return err
   277  }
   278  
   279  // destroyCrossModelRelationUnitsOps returns the operations necessary for the units
   280  // of the specified remote application to leave scope.
   281  func destroyCrossModelRelationUnitsOps(op *ForcedOperation, remoteApp *RemoteApplication, rel *Relation, onlyForTerminated bool) ([]txn.Op, error) {
   282  	if onlyForTerminated {
   283  		statusInfo, err := remoteApp.Status()
   284  		if op.FatalError(err) && !errors.IsNotFound(err) {
   285  			return nil, errors.Trace(err)
   286  		}
   287  		if err != nil || statusInfo.Status != status.Terminated {
   288  			return nil, jujutxn.ErrNoOperations
   289  		}
   290  	}
   291  	logger.Debugf("forcing cleanup of units for %v", remoteApp.Name())
   292  	remoteUnits, err := rel.AllRemoteUnits(remoteApp.Name())
   293  	if op.FatalError(err) {
   294  		return nil, errors.Trace(err)
   295  	} else if err != nil {
   296  		logger.Warningf("could not get remote units for %q: %v", remoteApp.Name(), err)
   297  	}
   298  
   299  	var ops []txn.Op
   300  	logger.Debugf("got %v relation units to clean", len(remoteUnits))
   301  	failRemoteUnits := false
   302  	for _, ru := range remoteUnits {
   303  		leaveScopeOps, err := ru.leaveScopeForcedOps(op)
   304  		if err != nil && err != jujutxn.ErrNoOperations {
   305  			op.AddError(err)
   306  			failRemoteUnits = true
   307  		}
   308  		ops = append(ops, leaveScopeOps...)
   309  	}
   310  	if !op.Force && failRemoteUnits {
   311  		return nil, errors.Trace(op.LastError())
   312  	}
   313  	return ops, nil
   314  }
   315  
   316  // When 'force' is set, this call will construct and apply needed operations
   317  // as well as accumulate all operational errors encountered.
   318  // If the 'force' is not set, any error will be fatal and no operations will be applied.
   319  func (op *DestroyRelationOperation) internalDestroy() (ops []txn.Op, err error) {
   320  	if len(op.r.doc.Endpoints) == 1 && op.r.doc.Endpoints[0].Role == charm.RolePeer {
   321  		return nil, errors.Errorf("is a peer relation")
   322  	}
   323  	defer func() {
   324  		if err == nil {
   325  			// This is a white lie; the document might actually be removed.
   326  			op.r.doc.Life = Dying
   327  		}
   328  	}()
   329  	rel := &Relation{op.r.st, op.r.doc}
   330  
   331  	remoteApp, isCrossModel, err := op.r.RemoteApplication()
   332  	if op.FatalError(err) {
   333  		return nil, errors.Trace(err)
   334  	} else if isCrossModel {
   335  		// If the status of the consumed app is terminated, we will never
   336  		// get an orderly exit of units from scope so force the issue.
   337  		// TODO(wallyworld) - this should be in a force cleanup job after giving things a change to complete normally.
   338  		destroyOps, err := destroyCrossModelRelationUnitsOps(&op.ForcedOperation, remoteApp, op.r, !op.Force)
   339  		if err != nil && err != jujutxn.ErrNoOperations {
   340  			return nil, errors.Trace(err)
   341  		}
   342  		ops = append(ops, destroyOps...)
   343  		// On the offering side, if this is the last relation to the consumer proxy, we may need to remove it also,
   344  		// but only if the unit count is already 0. This is just a backstop to allow force to fully clean up; normally
   345  		// unit count is not 0 so this doesn't get run.
   346  		if remoteApp.IsConsumerProxy() && remoteApp.doc.RelationCount <= 1 && (op.Force || rel.doc.UnitCount == 0) {
   347  			logger.Debugf("removing cross model consumer proxy and last relation %d: %v", op.r.doc.Id, op.r.doc.Key)
   348  			removeRelOps, err := rel.removeOps("", "", &op.ForcedOperation)
   349  			if err != nil && err != jujutxn.ErrNoOperations {
   350  				return nil, errors.Trace(err)
   351  			}
   352  			ops = append(ops, removeRelOps...)
   353  			var hasLastRefs bson.D
   354  			if !op.Force {
   355  				hasLastRefs = bson.D{{"life", remoteApp.doc.Life}, {"relationcount", remoteApp.doc.RelationCount}}
   356  			}
   357  			removeAppOps, err := remoteApp.removeOps(hasLastRefs)
   358  			if err != nil {
   359  				return nil, errors.Trace(err)
   360  			}
   361  			return append(ops, removeAppOps...), nil
   362  		}
   363  	}
   364  
   365  	// In this context, aborted transactions indicate that the number of units
   366  	// in scope have changed between 0 and not-0. The chances of 5 successive
   367  	// attempts each hitting this change -- which is itself an unlikely one --
   368  	// are considered to be extremely small.
   369  	// When 'force' is set, this call will return  needed operations
   370  	// and accumulate all operational errors encountered in the operation.
   371  	// If the 'force' is not set, any error will be fatal and no operations will be returned.
   372  	destroyOps, _, err := rel.destroyOps("", &op.ForcedOperation)
   373  	if err == errAlreadyDying {
   374  		return nil, jujutxn.ErrNoOperations
   375  	} else if op.FatalError(err) {
   376  		return nil, err
   377  	}
   378  	return append(ops, destroyOps...), nil
   379  }
   380  
   381  // destroyOps returns the operations necessary to destroy the relation, and
   382  // whether those operations will lead to the relation's removal. These
   383  // operations may include changes to the relation's applications; however, if
   384  // ignoreApplication is not empty, no operations modifying that application will
   385  // be generated.
   386  // When 'force' is set, this call will return both operations to remove this
   387  // relation as well as all operational errors encountered.
   388  // If the 'force' is not set, any error will be fatal and no operations will be returned.
   389  func (r *Relation) destroyOps(ignoreApplication string, op *ForcedOperation) (ops []txn.Op, isRemove bool, err error) {
   390  	if r.doc.Life != Alive {
   391  		if !op.Force {
   392  			return nil, false, errAlreadyDying
   393  		}
   394  	}
   395  	scopes, closer := r.st.db().GetCollection(relationScopesC)
   396  	defer closer()
   397  	sel := bson.M{"_id": bson.M{
   398  		"$regex": fmt.Sprintf("^%s#", r.st.docID(r.globalScope())),
   399  	}}
   400  	unitsInScope, err := scopes.Find(sel).Count()
   401  	if err != nil {
   402  		return nil, false, errors.Trace(err)
   403  	}
   404  	if unitsInScope != r.doc.UnitCount {
   405  		if op.Force {
   406  			logger.Warningf("ignoring unit count mismatch on relation %v: expected %d units in scope but got %d", r, r.doc.UnitCount, unitsInScope)
   407  		} else {
   408  			return nil, false, errors.Errorf("unit count mismatch on relation %v: expected %d units in scope but got %d", r, r.doc.UnitCount, unitsInScope)
   409  		}
   410  	}
   411  
   412  	if r.doc.UnitCount == 0 || unitsInScope == 0 {
   413  		// When 'force' is set, this call will return both needed operations
   414  		// as well as all operational errors encountered.
   415  		// If the 'force' is not set, any error will be fatal and no operations will be returned.
   416  		removeOps, err := r.removeOps(ignoreApplication, "", op)
   417  		if err != nil {
   418  			if !op.Force {
   419  				return nil, false, err
   420  			}
   421  			logger.Warningf("ignoring error (%v) while constructing relation %v destroy operations since force is used", err, r)
   422  		}
   423  		return removeOps, true, nil
   424  	}
   425  
   426  	lifeAssert := isAliveDoc
   427  	if op.Force {
   428  		// Since we are force destroying, life assert should be current relation's life.
   429  		lifeAssert = bson.D{{"life", r.doc.Life}}
   430  		deadline := r.st.stateClock.Now().Add(op.MaxWait)
   431  		ops = append(ops, newCleanupAtOp(deadline, cleanupForceDestroyedRelation, strconv.Itoa(r.Id())))
   432  	}
   433  
   434  	ops = append(ops, txn.Op{
   435  		C:      relationsC,
   436  		Id:     r.doc.DocID,
   437  		Assert: append(bson.D{{"unitcount", bson.D{{"$gt", 0}}}}, lifeAssert...),
   438  		Update: bson.D{{"$set", bson.D{{"life", Dying}}}},
   439  	})
   440  	return ops, false, nil
   441  }
   442  
   443  // removeOps returns the operations necessary to remove the relation. If
   444  // ignoreApplication is not empty, no operations affecting that application will be
   445  // included; if departingUnitName is non-empty, this implies that the
   446  // relation's applications may be Dying and otherwise unreferenced, and may thus
   447  // require removal themselves.
   448  // When 'force' is set, this call will return needed operations
   449  // and accumulate all operational errors encountered in the operation.
   450  // If the 'force' is not set, any error will be fatal and no operations will be returned.
   451  func (r *Relation) removeOps(ignoreApplication string, departingUnitName string, op *ForcedOperation) ([]txn.Op, error) {
   452  	relOp := txn.Op{
   453  		C:      relationsC,
   454  		Id:     r.doc.DocID,
   455  		Remove: true,
   456  	}
   457  	if op.Force {
   458  		// There can be a mismatch between the unit count recorded on a relation and the actual number
   459  		// of units in scope if a multi-controller cross model relation is removed and the controllers
   460  		// can't talk to coordinate cleanup.
   461  		relOp.Assert = bson.D{{"life", r.doc.Life}, {"unitcount", r.doc.UnitCount}}
   462  	} else {
   463  		if departingUnitName != "" {
   464  			relOp.Assert = bson.D{{"life", Dying}, {"unitcount", 1}}
   465  		} else {
   466  			relOp.Assert = bson.D{{"life", Alive}, {"unitcount", 0}}
   467  		}
   468  	}
   469  	ops := []txn.Op{relOp}
   470  	for _, ep := range r.doc.Endpoints {
   471  		if ep.ApplicationName == ignoreApplication {
   472  			continue
   473  		}
   474  		app, err := applicationByName(r.st, ep.ApplicationName)
   475  		if err != nil {
   476  			op.AddError(err)
   477  		} else {
   478  			if app.IsRemote() {
   479  				epOps, err := r.removeRemoteEndpointOps(ep, departingUnitName != "")
   480  				if err != nil {
   481  					op.AddError(err)
   482  				}
   483  				ops = append(ops, epOps...)
   484  			} else {
   485  				// When 'force' is set, this call will return both needed operations
   486  				// as well as all operational errors encountered.
   487  				// If the 'force' is not set, any error will be fatal and no operations will be returned.
   488  				epOps, err := r.removeLocalEndpointOps(ep, departingUnitName, op)
   489  				if err != nil {
   490  					op.AddError(err)
   491  				}
   492  				ops = append(ops, epOps...)
   493  			}
   494  		}
   495  	}
   496  	ops = append(ops, removeStatusOp(r.st, r.globalScope()))
   497  	ops = append(ops, removeRelationNetworksOps(r.st, r.doc.Key)...)
   498  	re := r.st.RemoteEntities()
   499  	tokenOps := re.removeRemoteEntityOps(r.Tag())
   500  	ops = append(ops, tokenOps...)
   501  	offerOps := removeOfferConnectionsForRelationOps(r.Id())
   502  	ops = append(ops, offerOps...)
   503  	secretPermissionsOps, err := r.st.removeScopedSecretPermissionOps(r.Tag())
   504  	if err != nil {
   505  		return nil, errors.Trace(err)
   506  	}
   507  	ops = append(ops, secretPermissionsOps...)
   508  	// This cleanup does not need to be forced.
   509  	cleanupOp := newCleanupOp(cleanupRelationSettings, fmt.Sprintf("r#%d#", r.Id()))
   510  	return append(ops, cleanupOp), nil
   511  }
   512  
   513  // When 'force' is set, this call will return both needed operations
   514  // as well as all operational errors encountered.
   515  // If the 'force' is not set, any error will be fatal and no operations will be returned.
   516  func (r *Relation) removeLocalEndpointOps(ep Endpoint, departingUnitName string, op *ForcedOperation) ([]txn.Op, error) {
   517  	var asserts bson.D
   518  	hasRelation := bson.D{{"relationcount", bson.D{{"$gt", 0}}}}
   519  	departingUnitApplicationMatchesEndpoint := func() bool {
   520  		s, err := names.UnitApplication(departingUnitName)
   521  		return err == nil && s == ep.ApplicationName
   522  	}
   523  	var cleanupOps []txn.Op
   524  	if departingUnitName == "" {
   525  		// We're constructing a destroy operation, either of the relation
   526  		// or one of its applications, and can therefore be assured that both
   527  		// applications are Alive.
   528  		asserts = append(hasRelation, isAliveDoc...)
   529  	} else if departingUnitApplicationMatchesEndpoint() {
   530  		// This application must have at least one unit -- the one that's
   531  		// departing the relation -- so it cannot be ready for removal.
   532  		cannotDieYet := bson.D{{"unitcount", bson.D{{"$gt", 0}}}}
   533  		asserts = append(hasRelation, cannotDieYet...)
   534  	} else {
   535  		// This application may require immediate removal.
   536  		// Check if the application is Dying, and if so, queue up a potential
   537  		// cleanup in case this was the last reference.
   538  		applications, closer := r.st.db().GetCollection(applicationsC)
   539  		defer closer()
   540  
   541  		asserts = append(hasRelation)
   542  		var appDoc applicationDoc
   543  		if err := applications.FindId(ep.ApplicationName).One(&appDoc); err == nil {
   544  			if appDoc.Life != Alive {
   545  				cleanupOps = append(cleanupOps, newCleanupOp(
   546  					cleanupApplication,
   547  					ep.ApplicationName,
   548  					false, // destroyStorage
   549  					op.Force,
   550  				))
   551  			}
   552  		} else if !op.Force {
   553  			return nil, errors.Trace(err)
   554  		}
   555  	}
   556  	return append([]txn.Op{{
   557  		C:      applicationsC,
   558  		Id:     r.st.docID(ep.ApplicationName),
   559  		Assert: asserts,
   560  		Update: bson.D{{"$inc", bson.D{{"relationcount", -1}}}},
   561  	}}, cleanupOps...), nil
   562  }
   563  
   564  func (r *Relation) removeRemoteEndpointOps(ep Endpoint, unitDying bool) ([]txn.Op, error) {
   565  	var asserts bson.D
   566  	hasRelation := bson.D{{"relationcount", bson.D{{"$gt", 0}}}}
   567  	if !unitDying {
   568  		// We're constructing a destroy operation, either of the relation
   569  		// or one of its application, and can therefore be assured that both
   570  		// applications are Alive.
   571  		asserts = append(hasRelation, isAliveDoc...)
   572  	} else {
   573  		// The remote application may require immediate removal if all relations are being
   574  		// removed and it's either dying or on the offering side as a consumer proxy.
   575  		applications, closer := r.st.db().GetCollection(remoteApplicationsC)
   576  		defer closer()
   577  
   578  		app := &RemoteApplication{st: r.st}
   579  		hasLastRef := bson.D{{"relationcount", 1}}
   580  		shouldRemove := bson.D{{"$or", []bson.D{
   581  			{{"life", bson.D{{"$ne", Alive}}}},
   582  			{{"is-consumer-proxy", true}},
   583  		}}}
   584  
   585  		removable := append(bson.D{{"_id", ep.ApplicationName}}, hasLastRef...)
   586  		removable = append(removable, shouldRemove...)
   587  		if err := applications.Find(removable).One(&app.doc); err == nil {
   588  			removeOps, err := app.removeOps(hasLastRef)
   589  			return removeOps, errors.Trace(err)
   590  		} else if err != mgo.ErrNotFound {
   591  			return nil, err
   592  		}
   593  		// If not, we must check that this is still the case when the
   594  		// transaction is applied.
   595  		asserts = bson.D{{"$or", []bson.D{
   596  			{{"life", Alive}},
   597  			{{"is-consumer-proxy", false}},
   598  			{{"relationcount", bson.D{{"$gt", 1}}}},
   599  		}}}
   600  	}
   601  	return []txn.Op{{
   602  		C:      remoteApplicationsC,
   603  		Id:     r.st.docID(ep.ApplicationName),
   604  		Assert: asserts,
   605  		Update: bson.D{{"$inc", bson.D{{"relationcount", -1}}}},
   606  	}}, nil
   607  }
   608  
   609  // Id returns the integer internal relation key. This is exposed
   610  // because the unit agent needs to expose a value derived from this
   611  // (as JUJU_RELATION_ID) to allow relation hooks to differentiate
   612  // between relations with different applications.
   613  func (r *Relation) Id() int {
   614  	return r.doc.Id
   615  }
   616  
   617  // Endpoint returns the endpoint of the relation for the named application.
   618  // If the application is not part of the relation, an error will be returned.
   619  func (r *Relation) Endpoint(applicationname string) (Endpoint, error) {
   620  	for _, ep := range r.doc.Endpoints {
   621  		if ep.ApplicationName == applicationname {
   622  			return ep, nil
   623  		}
   624  	}
   625  	msg := fmt.Sprintf("application %q is not a member of %q", applicationname, r)
   626  	return Endpoint{}, errors.NewNotFound(nil, msg)
   627  }
   628  
   629  // ModelUUID returns the model UUID for the relation.
   630  func (r *Relation) ModelUUID() string {
   631  	return r.doc.ModelUUID
   632  }
   633  
   634  // Endpoints returns the endpoints for the relation.
   635  func (r *Relation) Endpoints() []Endpoint {
   636  	return r.doc.Endpoints
   637  }
   638  
   639  // RelatedEndpoints returns the endpoints of the relation r with which
   640  // units of the named application will establish relations. If the application
   641  // is not part of the relation r, an error will be returned.
   642  func (r *Relation) RelatedEndpoints(applicationname string) ([]Endpoint, error) {
   643  	local, err := r.Endpoint(applicationname)
   644  	if err != nil {
   645  		return nil, err
   646  	}
   647  	role := counterpartRole(local.Role)
   648  	var eps []Endpoint
   649  	for _, ep := range r.doc.Endpoints {
   650  		if ep.Role == role {
   651  			eps = append(eps, ep)
   652  		}
   653  	}
   654  	if eps == nil {
   655  		return nil, errors.Errorf("no endpoints of %q relate to application %q", r, applicationname)
   656  	}
   657  	return eps, nil
   658  }
   659  
   660  // Unit returns a RelationUnit for the supplied unit.
   661  func (r *Relation) Unit(u *Unit) (*RelationUnit, error) {
   662  	const isLocalUnit = true
   663  	return r.unit(u.Name(), u.doc.Principal, u.IsPrincipal(), isLocalUnit)
   664  }
   665  
   666  // RemoteUnit returns a RelationUnit for the supplied unit
   667  // of a remote application.
   668  func (r *Relation) RemoteUnit(unitName string) (*RelationUnit, error) {
   669  	// Verify that the unit belongs to a remote application.
   670  	appName, err := names.UnitApplication(unitName)
   671  	if err != nil {
   672  		return nil, errors.Trace(err)
   673  	}
   674  	if _, err := r.st.RemoteApplication(appName); err != nil {
   675  		return nil, errors.Trace(err)
   676  	}
   677  	// Only non-subordinate applications may be offered for remote
   678  	// relation, so all remote units are principals.
   679  	const principal = ""
   680  	const isPrincipal = true
   681  	const isLocalUnit = false
   682  	return r.unit(unitName, principal, isPrincipal, isLocalUnit)
   683  }
   684  
   685  // AllRemoteUnits returns all the RelationUnits for the remote
   686  // application units for a given application.
   687  func (r *Relation) AllRemoteUnits(appName string) ([]*RelationUnit, error) {
   688  	// Verify that the unit belongs to a remote application.
   689  	if _, err := r.st.RemoteApplication(appName); err != nil {
   690  		return nil, errors.Trace(err)
   691  	}
   692  
   693  	relationScopes, closer := r.st.db().GetCollection(relationScopesC)
   694  	defer closer()
   695  
   696  	ep, err := r.Endpoint(appName)
   697  	if err != nil {
   698  		return nil, err
   699  	}
   700  	scope := r.globalScope()
   701  	parts := []string{"^" + scope, string(ep.Role), appName + "/"}
   702  	ruRegex := strings.Join(parts, "#")
   703  
   704  	var docs []relationScopeDoc
   705  	if err := relationScopes.Find(bson.D{{"key", bson.D{{"$regex", ruRegex}}}}).All(&docs); err != nil {
   706  		return nil, errors.Trace(err)
   707  	}
   708  	result := make([]*RelationUnit, len(docs))
   709  	for i, doc := range docs {
   710  		result[i] = &RelationUnit{
   711  			st:          r.st,
   712  			relation:    r,
   713  			unitName:    doc.unitName(),
   714  			isPrincipal: true,
   715  			isLocalUnit: false,
   716  			endpoint:    ep,
   717  			scope:       scope,
   718  		}
   719  	}
   720  	return result, nil
   721  }
   722  
   723  // RemoteApplication returns the remote application if
   724  // this relation is a cross-model relation, and a bool
   725  // indicating if it cross-model or not.
   726  func (r *Relation) RemoteApplication() (*RemoteApplication, bool, error) {
   727  	for _, ep := range r.Endpoints() {
   728  		app, err := r.st.RemoteApplication(ep.ApplicationName)
   729  		if err == nil {
   730  			return app, true, nil
   731  		} else if !errors.IsNotFound(err) {
   732  			return nil, false, errors.Trace(err)
   733  		}
   734  	}
   735  	return nil, false, nil
   736  }
   737  
   738  func (r *Relation) unit(
   739  	unitName string,
   740  	principal string,
   741  	isPrincipal bool,
   742  	isLocalUnit bool,
   743  ) (*RelationUnit, error) {
   744  	appName, err := names.UnitApplication(unitName)
   745  	if err != nil {
   746  		return nil, err
   747  	}
   748  	ep, err := r.Endpoint(appName)
   749  	if err != nil {
   750  		return nil, err
   751  	}
   752  	scope := r.globalScope()
   753  	if ep.Scope == charm.ScopeContainer {
   754  		container := principal
   755  		if container == "" {
   756  			container = unitName
   757  		}
   758  		scope = fmt.Sprintf("%s#%s", scope, container)
   759  	}
   760  	return &RelationUnit{
   761  		st:          r.st,
   762  		relation:    r,
   763  		unitName:    unitName,
   764  		isPrincipal: isPrincipal,
   765  		isLocalUnit: isLocalUnit,
   766  		endpoint:    ep,
   767  		scope:       scope,
   768  	}, nil
   769  }
   770  
   771  // globalScope returns the scope prefix for relation scope document keys
   772  // in the global scope.
   773  func (r *Relation) globalScope() string {
   774  	return relationGlobalScope(r.doc.Id)
   775  }
   776  
   777  func relationGlobalScope(id int) string {
   778  	return fmt.Sprintf("r#%d", id)
   779  }
   780  
   781  // relationSettingsCleanupChange removes the settings doc.
   782  type relationSettingsCleanupChange struct {
   783  	Prefix string
   784  }
   785  
   786  // Prepare is part of the Change interface.
   787  func (change relationSettingsCleanupChange) Prepare(db Database) ([]txn.Op, error) {
   788  	settings, closer := db.GetCollection(settingsC)
   789  	defer closer()
   790  	sel := bson.D{{"_id", bson.D{{"$regex", "^" + change.Prefix}}}}
   791  	var docs []struct {
   792  		DocID string `bson:"_id"`
   793  	}
   794  	err := settings.Find(sel).Select(bson.D{{"_id", 1}}).All(&docs)
   795  	if err != nil {
   796  		return nil, errors.Trace(err)
   797  	}
   798  	if len(docs) == 0 {
   799  		return nil, ErrChangeComplete
   800  	}
   801  
   802  	ops := make([]txn.Op, len(docs))
   803  	for i, doc := range docs {
   804  		ops[i] = txn.Op{
   805  			C:      settingsC,
   806  			Id:     doc.DocID,
   807  			Remove: true,
   808  		}
   809  	}
   810  	return ops, nil
   811  
   812  }
   813  
   814  func relationApplicationSettingsKey(id int, application string) string {
   815  	return fmt.Sprintf("%s#%s", relationGlobalScope(id), application)
   816  }
   817  
   818  // ApplicationSettings returns the application-level settings for the
   819  // specified application in this relation.
   820  func (r *Relation) ApplicationSettings(appName string) (map[string]interface{}, error) {
   821  	ep, err := r.Endpoint(appName)
   822  	if err != nil {
   823  		return nil, errors.Trace(err)
   824  	}
   825  	applicationKey := relationApplicationSettingsKey(r.Id(), ep.ApplicationName)
   826  	s, err := readSettings(r.st.db(), settingsC, applicationKey)
   827  	if err != nil {
   828  		return nil, errors.Annotatef(err, "relation %q application %q", r.String(), appName)
   829  	}
   830  	return s.Map(), nil
   831  }
   832  
   833  // UpdateApplicationSettings updates the given application's settings
   834  // in this relation. It requires a current leadership token.
   835  func (r *Relation) UpdateApplicationSettings(appName string, token leadership.Token, updates map[string]interface{}) error {
   836  	modelOp, err := r.UpdateApplicationSettingsOperation(appName, token, updates)
   837  	if err != nil {
   838  		return errors.Trace(err)
   839  	}
   840  
   841  	err = r.st.ApplyOperation(modelOp)
   842  	if errors.IsNotFound(err) {
   843  		return errors.NotFoundf("relation %q application %q", r, appName)
   844  	} else if err != nil {
   845  		return errors.Annotatef(err, "relation %q application %q", r, appName)
   846  	}
   847  	return nil
   848  }
   849  
   850  // UpdateApplicationSettingsOperation returns a ModelOperation for updating the
   851  // given application's settings in this relation. It requires a current
   852  // leadership token.
   853  func (r *Relation) UpdateApplicationSettingsOperation(appName string, token leadership.Token, updates map[string]interface{}) (ModelOperation, error) {
   854  	// We can calculate the actual update ahead of time; it's not dependent
   855  	// upon the current state of the document. (*Writing* it should depend
   856  	// on document state, but that's handled below.)
   857  	ep, err := r.Endpoint(appName)
   858  	if err != nil {
   859  		return nil, errors.Trace(err)
   860  	}
   861  
   862  	key := relationApplicationSettingsKey(r.Id(), ep.ApplicationName)
   863  	return newUpdateLeaderSettingsOperation(r.st.db(), token, key, updates), nil
   864  }
   865  
   866  // WatchApplicationSettings returns a notify watcher that will signal
   867  // whenever the specified application's relation settings are changed.
   868  func (r *Relation) WatchApplicationSettings(app *Application) (NotifyWatcher, error) {
   869  	ep, err := r.Endpoint(app.Name())
   870  	if err != nil {
   871  		return nil, errors.Trace(err)
   872  	}
   873  	key := relationApplicationSettingsKey(r.Id(), ep.ApplicationName)
   874  	watcher := newEntityWatcher(r.st, settingsC, r.st.docID(key))
   875  	return watcher, nil
   876  }