github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/common/leadership.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/apiserver/facade" 11 "github.com/juju/juju/apiserver/params" 12 "github.com/juju/juju/core/leadership" 13 "github.com/juju/juju/permission" 14 "github.com/juju/juju/state" 15 ) 16 17 //go:generate mockgen -package mocks -destination mocks/leadership.go github.com/juju/juju/apiserver/common LeadershipPinningBackend,LeadershipMachine 18 19 // LeadershipMachine is an indirection for state.machine. 20 type LeadershipMachine interface { 21 ApplicationNames() ([]string, error) 22 } 23 24 type leadershipMachine struct { 25 *state.Machine 26 } 27 28 // LeadershipPinningBacked describes state method wrappers used by this API. 29 type LeadershipPinningBackend interface { 30 Machine(string) (LeadershipMachine, error) 31 } 32 33 type leadershipPinningBackend struct { 34 *state.State 35 } 36 37 // Machine wraps state.Machine to return an implementation 38 // of the LeadershipMachine indirection. 39 func (s leadershipPinningBackend) Machine(name string) (LeadershipMachine, error) { 40 m, err := s.State.Machine(name) 41 if err != nil { 42 return nil, err 43 } 44 return leadershipMachine{m}, nil 45 } 46 47 // API exposes leadership pinning and unpinning functionality for remote use. 48 type LeadershipPinningAPI interface { 49 PinMachineApplications() (params.PinApplicationsResults, error) 50 UnpinMachineApplications() (params.PinApplicationsResults, error) 51 PinnedLeadership() (params.PinnedLeadershipResult, error) 52 } 53 54 // NewLeadershipPinningFacade creates and returns a new leadership API. 55 // This signature is suitable for facade registration. 56 func NewLeadershipPinningFacade(ctx facade.Context) (LeadershipPinningAPI, error) { 57 st := ctx.State() 58 model, err := st.Model() 59 if err != nil { 60 return nil, errors.Trace(err) 61 } 62 pinner, err := ctx.LeadershipPinner(model.UUID()) 63 if err != nil { 64 return nil, errors.Trace(err) 65 } 66 return NewLeadershipPinningAPI(leadershipPinningBackend{st}, model.ModelTag(), pinner, ctx.Auth()) 67 } 68 69 // NewLeadershipPinningAPI creates and returns a new leadership API from the 70 // input tag, Pinner implementation and facade Authorizer. 71 func NewLeadershipPinningAPI( 72 st LeadershipPinningBackend, modelTag names.ModelTag, pinner leadership.Pinner, authorizer facade.Authorizer, 73 ) (LeadershipPinningAPI, error) { 74 return &leadershipPinningAPI{ 75 st: st, 76 modelTag: modelTag, 77 pinner: pinner, 78 authorizer: authorizer, 79 }, nil 80 } 81 82 type leadershipPinningAPI struct { 83 st LeadershipPinningBackend 84 modelTag names.ModelTag 85 pinner leadership.Pinner 86 authorizer facade.Authorizer 87 } 88 89 // PinnedLeadership returns all pinned applications and the entities that 90 // require their pinned behaviour, for leadership in the current model. 91 func (a *leadershipPinningAPI) PinnedLeadership() (params.PinnedLeadershipResult, error) { 92 result := params.PinnedLeadershipResult{} 93 94 canAccess, err := a.authorizer.HasPermission(permission.ReadAccess, a.modelTag) 95 if err != nil { 96 return result, errors.Trace(err) 97 } 98 if !canAccess { 99 return result, ErrPerm 100 } 101 102 result.Result = a.pinner.PinnedLeadership() 103 return result, nil 104 } 105 106 // TODO (manadart 2018-10-29): Rename the two methods below (and on the client 107 // side) to be [Un]PinApplicationLeaders, and derive the list of applications 108 // based on the authenticating entity. 109 110 // PinMachineApplications pins leadership for applications represented by units 111 // running on the auth'd machine. 112 func (a *leadershipPinningAPI) PinMachineApplications() (params.PinApplicationsResults, error) { 113 if !a.authorizer.AuthMachineAgent() { 114 return params.PinApplicationsResults{}, ErrPerm 115 } 116 return a.pinMachineAppsOps(a.pinner.PinLeadership) 117 } 118 119 // UnpinMachineApplications unpins leadership for applications represented by 120 // units running on the auth'd machine. 121 func (a *leadershipPinningAPI) UnpinMachineApplications() (params.PinApplicationsResults, error) { 122 if !a.authorizer.AuthMachineAgent() { 123 return params.PinApplicationsResults{}, ErrPerm 124 } 125 return a.pinMachineAppsOps(a.pinner.UnpinLeadership) 126 } 127 128 // pinMachineAppsOps runs the input pin/unpin operation against all 129 // applications represented by units on the authorised machine. 130 // An assumption is made that the validity of the auth tag has been verified 131 // by the caller. 132 func (a *leadershipPinningAPI) pinMachineAppsOps(op func(string, string) error) (params.PinApplicationsResults, error) { 133 result := params.PinApplicationsResults{} 134 135 tag := a.authorizer.GetAuthTag() 136 m, err := a.st.Machine(tag.Id()) 137 if err != nil { 138 return result, errors.Trace(err) 139 } 140 apps, err := m.ApplicationNames() 141 if err != nil { 142 return result, errors.Trace(err) 143 } 144 145 results := make([]params.PinApplicationResult, len(apps)) 146 for i, app := range apps { 147 results[i] = params.PinApplicationResult{ 148 ApplicationName: app, 149 } 150 if err := op(app, tag.String()); err != nil { 151 results[i].Error = ServerError(err) 152 } 153 } 154 result.Results = results 155 return result, nil 156 }