github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/names" 11 12 "github.com/juju/juju/apiserver/common" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/core/leadership" 15 "github.com/juju/juju/state" 16 ) 17 18 const ( 19 // FacadeName is the string-representation of this API used both 20 // to register the service, and for the client to resolve the 21 // service endpoint. 22 FacadeName = "LeadershipService" 23 24 // MinLeaseRequest is the shortest duration for which we will accept 25 // a leadership claim. 26 MinLeaseRequest = 5 * time.Second 27 28 // MaxLeaseRequest is the longest duration for which we will accept 29 // a leadership claim. 30 MaxLeaseRequest = 5 * time.Minute 31 ) 32 33 func init() { 34 common.RegisterStandardFacade( 35 FacadeName, 36 2, 37 NewLeadershipServiceFacade, 38 ) 39 } 40 41 // NewLeadershipServiceFacade constructs a new LeadershipService and presents 42 // a signature that can be used with RegisterStandardFacade. 43 func NewLeadershipServiceFacade( 44 state *state.State, resources *common.Resources, authorizer common.Authorizer, 45 ) (LeadershipService, error) { 46 return NewLeadershipService(state.LeadershipClaimer(), authorizer) 47 } 48 49 // NewLeadershipService constructs a new LeadershipService. 50 func NewLeadershipService( 51 claimer leadership.Claimer, authorizer common.Authorizer, 52 ) (LeadershipService, error) { 53 54 if !authorizer.AuthUnitAgent() { 55 return nil, errors.Unauthorizedf("permission denied") 56 } 57 58 return &leadershipService{ 59 claimer: claimer, 60 authorizer: authorizer, 61 }, nil 62 } 63 64 // leadershipService implements the LeadershipService interface and 65 // is the concrete implementation of the API endpoint. 66 type leadershipService struct { 67 claimer leadership.Claimer 68 authorizer common.Authorizer 69 } 70 71 // ClaimLeadership is part of the LeadershipService interface. 72 func (m *leadershipService) ClaimLeadership(args params.ClaimLeadershipBulkParams) (params.ClaimLeadershipBulkResults, error) { 73 74 results := make([]params.ErrorResult, len(args.Params)) 75 for pIdx, p := range args.Params { 76 77 result := &results[pIdx] 78 serviceTag, unitTag, err := parseServiceAndUnitTags(p.ServiceTag, p.UnitTag) 79 if err != nil { 80 result.Error = common.ServerError(err) 81 continue 82 } 83 duration := time.Duration(p.DurationSeconds * float64(time.Second)) 84 if duration > MaxLeaseRequest || duration < MinLeaseRequest { 85 result.Error = common.ServerError(errors.New("invalid duration")) 86 continue 87 } 88 89 // In the future, situations may arise wherein units will make 90 // leadership claims for other units. For now, units can only 91 // claim leadership for themselves, for their own service. 92 if !m.authorizer.AuthOwner(unitTag) || !m.authMember(serviceTag) { 93 result.Error = common.ServerError(common.ErrPerm) 94 continue 95 } 96 97 err = m.claimer.ClaimLeadership(serviceTag.Id(), unitTag.Id(), duration) 98 if err != nil { 99 result.Error = common.ServerError(err) 100 } 101 } 102 103 return params.ClaimLeadershipBulkResults{results}, nil 104 } 105 106 // BlockUntilLeadershipReleased implements the LeadershipService interface. 107 func (m *leadershipService) BlockUntilLeadershipReleased(serviceTag names.ServiceTag) (params.ErrorResult, error) { 108 if !m.authMember(serviceTag) { 109 return params.ErrorResult{Error: common.ServerError(common.ErrPerm)}, nil 110 } 111 112 if err := m.claimer.BlockUntilLeadershipReleased(serviceTag.Id()); err != nil { 113 return params.ErrorResult{Error: common.ServerError(err)}, nil 114 } 115 return params.ErrorResult{}, nil 116 } 117 118 func (m *leadershipService) authMember(serviceTag names.ServiceTag) bool { 119 ownerTag := m.authorizer.GetAuthTag() 120 unitTag, ok := ownerTag.(names.UnitTag) 121 if !ok { 122 return false 123 } 124 unitId := unitTag.Id() 125 requireServiceId, err := names.UnitService(unitId) 126 if err != nil { 127 return false 128 } 129 return serviceTag.Id() == requireServiceId 130 } 131 132 // parseServiceAndUnitTags takes in string representations of service 133 // and unit tags and returns their corresponding tags. 134 func parseServiceAndUnitTags( 135 serviceTagString, unitTagString string, 136 ) ( 137 names.ServiceTag, names.UnitTag, error, 138 ) { 139 // TODO(fwereade) 2015-02-25 bug #1425506 140 // These permissions errors are not appropriate -- there's no permission or 141 // security issue in play here, because our tag format is public, and the 142 // error only triggers when the strings fail to match that format. 143 serviceTag, err := names.ParseServiceTag(serviceTagString) 144 if err != nil { 145 return names.ServiceTag{}, names.UnitTag{}, common.ErrPerm 146 } 147 148 unitTag, err := names.ParseUnitTag(unitTagString) 149 if err != nil { 150 return names.ServiceTag{}, names.UnitTag{}, common.ErrPerm 151 } 152 153 return serviceTag, unitTag, nil 154 }