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 }