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