github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/leadership/leadership.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package leadership 5 6 import ( 7 "time" 8 9 "github.com/juju/loggo" 10 "github.com/juju/names" 11 12 "github.com/juju/juju/apiserver/common" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/leadership" 15 "github.com/juju/juju/lease" 16 "github.com/juju/juju/state" 17 ) 18 19 const ( 20 // FacadeName is the string-representation of this API used both 21 // to register the service, and for the client to resolve the 22 // service endpoint. 23 FacadeName = "LeadershipService" 24 ) 25 26 var ( 27 logger = loggo.GetLogger("juju.apiserver.leadership") 28 // Begin injection-chain so we can instantiate leadership 29 // services. Exposed as variables so we can change the 30 // implementation for testing purposes. 31 leaseMgr = lease.Manager() 32 leaderMgr = leadership.NewLeadershipManager(leaseMgr) 33 ) 34 35 func init() { 36 37 common.RegisterStandardFacade( 38 FacadeName, 39 1, 40 NewLeadershipServiceFn(leaderMgr), 41 ) 42 } 43 44 // NewLeadershipServiceFn returns a function which can construct a 45 // LeadershipService when passed a state, resources, and authorizer. 46 // This function signature conforms to Juju's required API server 47 // signature. 48 func NewLeadershipServiceFn( 49 leadershipMgr leadership.LeadershipManager, 50 ) func(*state.State, *common.Resources, common.Authorizer) (LeadershipService, error) { 51 return func( 52 state *state.State, 53 resources *common.Resources, 54 authorizer common.Authorizer, 55 ) (LeadershipService, error) { 56 return NewLeadershipService(state, resources, authorizer, leadershipMgr) 57 } 58 } 59 60 // NewLeadershipService constructs a new LeadershipService. 61 func NewLeadershipService( 62 state *state.State, 63 resources *common.Resources, 64 authorizer common.Authorizer, 65 leadershipMgr leadership.LeadershipManager, 66 ) (LeadershipService, error) { 67 68 if !authorizer.AuthUnitAgent() { 69 return nil, common.ErrPerm 70 } 71 72 return &leadershipService{ 73 state: state, 74 authorizer: authorizer, 75 LeadershipManager: leadershipMgr, 76 }, nil 77 } 78 79 // LeadershipService implements the LeadershipManager interface and 80 // is the concrete implementation of the API endpoint. 81 type leadershipService struct { 82 state *state.State 83 authorizer common.Authorizer 84 leadership.LeadershipManager 85 } 86 87 // ClaimLeadership implements the LeadershipService interface. 88 func (m *leadershipService) ClaimLeadership(args params.ClaimLeadershipBulkParams) (params.ClaimLeadershipBulkResults, error) { 89 90 var dur time.Duration 91 claim := callWithIds(func(sid, uid string) (err error) { 92 dur, err = m.LeadershipManager.ClaimLeadership(sid, uid) 93 return err 94 }) 95 96 results := make([]params.ClaimLeadershipResults, len(args.Params)) 97 for pIdx, p := range args.Params { 98 99 result := &results[pIdx] 100 svcTag, unitTag, err := parseServiceAndUnitTags(p.ServiceTag, p.UnitTag) 101 if err != nil { 102 result.Error = err 103 continue 104 } 105 106 // In the future, situations may arise wherein units will make 107 // leadership claims for other units. For now, units can only 108 // claim leadership for themselves. 109 if !m.authorizer.AuthUnitAgent() || !m.authorizer.AuthOwner(unitTag) { 110 result.Error = common.ServerError(common.ErrPerm) 111 continue 112 } else if err := claim(svcTag, unitTag).Error; err != nil { 113 result.Error = err 114 continue 115 } 116 117 result.ClaimDurationInSec = dur.Seconds() 118 result.ServiceTag = p.ServiceTag 119 } 120 121 return params.ClaimLeadershipBulkResults{results}, nil 122 } 123 124 // ReleaseLeadership implements the LeadershipService interface. 125 func (m *leadershipService) ReleaseLeadership(args params.ReleaseLeadershipBulkParams) (params.ReleaseLeadershipBulkResults, error) { 126 127 release := callWithIds(m.LeadershipManager.ReleaseLeadership) 128 results := make([]params.ErrorResult, len(args.Params)) 129 130 for paramIdx, p := range args.Params { 131 132 result := &results[paramIdx] 133 svcTag, unitTag, err := parseServiceAndUnitTags(p.ServiceTag, p.UnitTag) 134 if err != nil { 135 result.Error = err 136 continue 137 } 138 139 // In the future, situations may arise wherein units will make 140 // leadership claims for other units. For now, units can only 141 // claim leadership for themselves. 142 if !m.authorizer.AuthUnitAgent() || !m.authorizer.AuthOwner(unitTag) { 143 result.Error = common.ServerError(common.ErrPerm) 144 continue 145 } 146 147 result.Error = release(svcTag, unitTag).Error 148 } 149 150 return params.ReleaseLeadershipBulkResults{results}, nil 151 } 152 153 // BlockUntilLeadershipReleased implements the LeadershipService interface. 154 func (m *leadershipService) BlockUntilLeadershipReleased(serviceTag names.ServiceTag) (params.ErrorResult, error) { 155 if !m.authorizer.AuthUnitAgent() { 156 return params.ErrorResult{Error: common.ServerError(common.ErrPerm)}, nil 157 } 158 159 if err := m.LeadershipManager.BlockUntilLeadershipReleased(serviceTag.Id()); err != nil { 160 return params.ErrorResult{Error: common.ServerError(err)}, nil 161 } 162 return params.ErrorResult{}, nil 163 } 164 165 // callWithIds transforms a common Leadership Election function 166 // signature into one that is conducive to use in the API Server. 167 func callWithIds(fn func(string, string) error) func(st, ut names.Tag) params.ErrorResult { 168 return func(st, ut names.Tag) (result params.ErrorResult) { 169 if err := fn(st.Id(), ut.Id()); err != nil { 170 result.Error = common.ServerError(err) 171 } 172 return result 173 } 174 } 175 176 // parseServiceAndUnitTags takes in string representations of service 177 // and unit tags and parses them into structs. 178 // 179 // NOTE: we return permissions errors when parsing fails to obfuscate 180 // the parse-failure. This is for security purposes. 181 func parseServiceAndUnitTags( 182 serviceTagString, unitTagString string, 183 ) (serviceTag names.ServiceTag, unitTag names.UnitTag, _ *params.Error) { 184 serviceTag, err := names.ParseServiceTag(serviceTagString) 185 if err != nil { 186 return serviceTag, unitTag, common.ServerError(common.ErrPerm) 187 } 188 189 unitTag, err = names.ParseUnitTag(unitTagString) 190 if err != nil { 191 return serviceTag, unitTag, common.ServerError(common.ErrPerm) 192 } 193 194 return serviceTag, unitTag, nil 195 }