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 }