github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/state/leadership.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names"
     9  	jujutxn "github.com/juju/txn"
    10  	"gopkg.in/mgo.v2/txn"
    11  
    12  	"github.com/juju/juju/core/leadership"
    13  	corelease "github.com/juju/juju/core/lease"
    14  	"github.com/juju/juju/worker/lease"
    15  )
    16  
    17  func removeLeadershipSettingsOp(serviceId string) txn.Op {
    18  	return removeSettingsOp(leadershipSettingsKey(serviceId))
    19  }
    20  
    21  func leadershipSettingsKey(serviceId string) string {
    22  	return fmt.Sprintf("s#%s#leader", serviceId)
    23  }
    24  
    25  // LeadershipClaimer returns a leadership.Claimer for units and services in the
    26  // state's model.
    27  func (st *State) LeadershipClaimer() leadership.Claimer {
    28  	return leadershipClaimer{st.leadershipManager}
    29  }
    30  
    31  // LeadershipChecker returns a leadership.Checker for units and services in the
    32  // state's model.
    33  func (st *State) LeadershipChecker() leadership.Checker {
    34  	return leadershipChecker{st.leadershipManager}
    35  }
    36  
    37  // HackLeadership stops the state's internal leadership manager to prevent it
    38  // from interfering with apiserver shutdown.
    39  func (st *State) HackLeadership() {
    40  	// TODO(fwereade): 2015-08-07 lp:1482634
    41  	// obviously, this should not exist: it's a quick hack to address lp:1481368 in
    42  	// 1.24.4, and should be quickly replaced with something that isn't so heinous.
    43  	//
    44  	// But.
    45  	//
    46  	// I *believe* that what it'll take to fix this is to extract the mongo-session-
    47  	// opening from state.Open, so we can create a mongosessioner Manifold on which
    48  	// state, leadership, watching, tools storage, etc etc etc can all independently
    49  	// depend. (Each dependency would/should have a separate session so they can
    50  	// close them all on their own schedule, without panics -- but the failure of
    51  	// the shared component should successfully goose them all into shutting down,
    52  	// in parallel, of their own accord.)
    53  	st.leadershipManager.Kill()
    54  	st.singularManager.Kill()
    55  }
    56  
    57  // buildTxnWithLeadership returns a transaction source that combines the supplied source
    58  // with checks and asserts on the supplied token.
    59  func buildTxnWithLeadership(buildTxn jujutxn.TransactionSource, token leadership.Token) jujutxn.TransactionSource {
    60  	return func(attempt int) ([]txn.Op, error) {
    61  		var prereqs []txn.Op
    62  		if err := token.Check(&prereqs); err != nil {
    63  			return nil, errors.Annotatef(err, "prerequisites failed")
    64  		}
    65  		ops, err := buildTxn(attempt)
    66  		if err == jujutxn.ErrNoOperations {
    67  			return nil, jujutxn.ErrNoOperations
    68  		} else if err != nil {
    69  			return nil, errors.Trace(err)
    70  		}
    71  		return append(prereqs, ops...), nil
    72  	}
    73  }
    74  
    75  // leadershipSecretary implements lease.Secretary; it checks that leases are
    76  // service names, and holders are unit names.
    77  type leadershipSecretary struct{}
    78  
    79  // CheckLease is part of the lease.Secretary interface.
    80  func (leadershipSecretary) CheckLease(name string) error {
    81  	if !names.IsValidService(name) {
    82  		return errors.NewNotValid(nil, "not a service name")
    83  	}
    84  	return nil
    85  }
    86  
    87  // CheckHolder is part of the lease.Secretary interface.
    88  func (leadershipSecretary) CheckHolder(name string) error {
    89  	if !names.IsValidUnit(name) {
    90  		return errors.NewNotValid(nil, "not a unit name")
    91  	}
    92  	return nil
    93  }
    94  
    95  // CheckDuration is part of the lease.Secretary interface.
    96  func (leadershipSecretary) CheckDuration(duration time.Duration) error {
    97  	if duration <= 0 {
    98  		return errors.NewNotValid(nil, "non-positive")
    99  	}
   100  	return nil
   101  }
   102  
   103  // leadershipChecker implements leadership.Checker by wrapping a lease.Manager.
   104  type leadershipChecker struct {
   105  	manager *lease.Manager
   106  }
   107  
   108  // LeadershipCheck is part of the leadership.Checker interface.
   109  func (m leadershipChecker) LeadershipCheck(serviceName, unitName string) leadership.Token {
   110  	token := m.manager.Token(serviceName, unitName)
   111  	return leadershipToken{
   112  		serviceName: serviceName,
   113  		unitName:    unitName,
   114  		token:       token,
   115  	}
   116  }
   117  
   118  // leadershipToken implements leadership.Token by wrapping a corelease.Token.
   119  type leadershipToken struct {
   120  	serviceName string
   121  	unitName    string
   122  	token       corelease.Token
   123  }
   124  
   125  // Check is part of the leadership.Token interface.
   126  func (t leadershipToken) Check(out interface{}) error {
   127  	err := t.token.Check(out)
   128  	if errors.Cause(err) == corelease.ErrNotHeld {
   129  		return errors.Errorf("%q is not leader of %q", t.unitName, t.serviceName)
   130  	}
   131  	return errors.Trace(err)
   132  }
   133  
   134  // leadershipClaimer implements leadership.Claimer by wrappping a lease.Manager.
   135  type leadershipClaimer struct {
   136  	manager *lease.Manager
   137  }
   138  
   139  // ClaimLeadership is part of the leadership.Claimer interface.
   140  func (m leadershipClaimer) ClaimLeadership(serviceName, unitName string, duration time.Duration) error {
   141  	err := m.manager.Claim(serviceName, unitName, duration)
   142  	if errors.Cause(err) == corelease.ErrClaimDenied {
   143  		return leadership.ErrClaimDenied
   144  	}
   145  	return errors.Trace(err)
   146  }
   147  
   148  // BlockUntilLeadershipReleased is part of the leadership.Claimer interface.
   149  func (m leadershipClaimer) BlockUntilLeadershipReleased(serviceName string) error {
   150  	err := m.manager.WaitUntilExpired(serviceName)
   151  	return errors.Trace(err)
   152  }