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