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

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