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