github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/names" 12 13 "github.com/juju/juju/apiserver/common" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/leadership" 16 "github.com/juju/juju/lease" 17 "github.com/juju/juju/state" 18 ) 19 20 const ( 21 // FacadeName is the string-representation of this API used both 22 // to register the service, and for the client to resolve the 23 // service endpoint. 24 FacadeName = "LeadershipService" 25 26 // MinLeaseRequest is the shortest duration for which we will accept 27 // a leadership claim. 28 MinLeaseRequest = 5 * time.Second 29 30 // MaxLeaseRequest is the longest duration for which we will accept 31 // a leadership claim. 32 MaxLeaseRequest = 5 * time.Minute 33 ) 34 35 var ( 36 logger = loggo.GetLogger("juju.apiserver.leadership") 37 // Begin injection-chain so we can instantiate leadership 38 // services. Exposed as variables so we can change the 39 // implementation for testing purposes. 40 leaseMgr = lease.Manager() 41 leaderMgr = leadership.NewLeadershipManager(leaseMgr) 42 ) 43 44 func init() { 45 46 common.RegisterStandardFacade( 47 FacadeName, 48 1, 49 NewLeadershipServiceFn(leaderMgr), 50 ) 51 } 52 53 // NewLeadershipServiceFn returns a function which can construct a 54 // LeadershipService when passed a state, resources, and authorizer. 55 // This function signature conforms to Juju's required API server 56 // signature. 57 func NewLeadershipServiceFn( 58 leadershipMgr leadership.LeadershipManager, 59 ) func(*state.State, *common.Resources, common.Authorizer) (LeadershipService, error) { 60 return func( 61 state *state.State, 62 resources *common.Resources, 63 authorizer common.Authorizer, 64 ) (LeadershipService, error) { 65 return NewLeadershipService(state, resources, authorizer, leadershipMgr) 66 } 67 } 68 69 // NewLeadershipService constructs a new LeadershipService. 70 func NewLeadershipService( 71 state *state.State, 72 resources *common.Resources, 73 authorizer common.Authorizer, 74 leadershipMgr leadership.LeadershipManager, 75 ) (LeadershipService, error) { 76 77 if !authorizer.AuthUnitAgent() { 78 return nil, common.ErrPerm 79 } 80 81 return &leadershipService{ 82 state: state, 83 authorizer: authorizer, 84 LeadershipManager: leadershipMgr, 85 }, nil 86 } 87 88 // LeadershipService implements the LeadershipManager interface and 89 // is the concrete implementation of the API endpoint. 90 type leadershipService struct { 91 state *state.State 92 authorizer common.Authorizer 93 leadership.LeadershipManager 94 } 95 96 // ClaimLeadership implements the LeadershipService interface. 97 func (m *leadershipService) ClaimLeadership(args params.ClaimLeadershipBulkParams) (params.ClaimLeadershipBulkResults, error) { 98 99 results := make([]params.ErrorResult, len(args.Params)) 100 for pIdx, p := range args.Params { 101 102 result := &results[pIdx] 103 serviceTag, unitTag, err := parseServiceAndUnitTags(p.ServiceTag, p.UnitTag) 104 if err != nil { 105 result.Error = common.ServerError(err) 106 continue 107 } 108 duration := time.Duration(p.DurationSeconds * float64(time.Second)) 109 if duration > MaxLeaseRequest || duration < MinLeaseRequest { 110 result.Error = common.ServerError(errors.New("invalid duration")) 111 continue 112 } 113 114 // In the future, situations may arise wherein units will make 115 // leadership claims for other units. For now, units can only 116 // claim leadership for themselves. 117 if !m.authorizer.AuthUnitAgent() || !m.authorizer.AuthOwner(unitTag) { 118 result.Error = common.ServerError(common.ErrPerm) 119 continue 120 } 121 122 err = m.LeadershipManager.ClaimLeadership(serviceTag.Id(), unitTag.Id(), duration) 123 if err != nil { 124 result.Error = common.ServerError(err) 125 } 126 } 127 128 return params.ClaimLeadershipBulkResults{results}, nil 129 } 130 131 // ReleaseLeadership implements the LeadershipService interface. 132 func (m *leadershipService) ReleaseLeadership(args params.ReleaseLeadershipBulkParams) (params.ReleaseLeadershipBulkResults, error) { 133 134 results := make([]params.ErrorResult, len(args.Params)) 135 136 for paramIdx, p := range args.Params { 137 138 result := &results[paramIdx] 139 serviceTag, unitTag, err := parseServiceAndUnitTags(p.ServiceTag, p.UnitTag) 140 if err != nil { 141 result.Error = common.ServerError(err) 142 continue 143 } 144 145 // In the future, situations may arise wherein units will make 146 // leadership claims for other units. For now, units can only 147 // claim leadership for themselves. 148 if !m.authorizer.AuthUnitAgent() || !m.authorizer.AuthOwner(unitTag) { 149 result.Error = common.ServerError(common.ErrPerm) 150 continue 151 } 152 153 err = m.LeadershipManager.ReleaseLeadership(serviceTag.Id(), unitTag.Id()) 154 if err != nil { 155 result.Error = common.ServerError(err) 156 } 157 } 158 159 return params.ReleaseLeadershipBulkResults{results}, nil 160 } 161 162 // BlockUntilLeadershipReleased implements the LeadershipService interface. 163 func (m *leadershipService) BlockUntilLeadershipReleased(serviceTag names.ServiceTag) (params.ErrorResult, error) { 164 if !m.authorizer.AuthUnitAgent() { 165 return params.ErrorResult{Error: common.ServerError(common.ErrPerm)}, nil 166 } 167 168 if err := m.LeadershipManager.BlockUntilLeadershipReleased(serviceTag.Id()); err != nil { 169 return params.ErrorResult{Error: common.ServerError(err)}, nil 170 } 171 return params.ErrorResult{}, nil 172 } 173 174 // parseServiceAndUnitTags takes in string representations of service 175 // and unit tags and returns their corresponding tags. 176 func parseServiceAndUnitTags( 177 serviceTagString, unitTagString string, 178 ) ( 179 names.ServiceTag, names.UnitTag, error, 180 ) { 181 // TODO(fwereade) 2015-02-25 bug #1425506 182 // These permissions errors are not appropriate -- there's no permission or 183 // security issue in play here, because our tag format is public, and the 184 // error only triggers when the strings fail to match that format. 185 serviceTag, err := names.ParseServiceTag(serviceTagString) 186 if err != nil { 187 return names.ServiceTag{}, names.UnitTag{}, common.ErrPerm 188 } 189 190 unitTag, err := names.ParseUnitTag(unitTagString) 191 if err != nil { 192 return names.ServiceTag{}, names.UnitTag{}, common.ErrPerm 193 } 194 195 return serviceTag, unitTag, nil 196 }