github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/controller/singular/singular.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package singular 5 6 import ( 7 "context" 8 "time" 9 10 "github.com/juju/errors" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/apiserver/common" 14 "github.com/juju/juju/apiserver/facade" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/core/lease" 17 "github.com/juju/juju/state" 18 ) 19 20 // NewExternalFacade is for API registration. 21 func NewExternalFacade(context facade.Context) (*Facade, error) { 22 st := context.State() 23 auth := context.Auth() 24 25 m, err := st.Model() 26 if err != nil { 27 return nil, err 28 } 29 30 claimer, err := context.SingularClaimer() 31 if err != nil { 32 return nil, errors.Trace(err) 33 } 34 35 backend := getBackend(st, m.ModelTag()) 36 return NewFacade(backend, claimer, auth) 37 } 38 39 var getBackend = func(st *state.State, modelTag names.ModelTag) Backend { 40 return &stateBackend{st, modelTag} 41 } 42 43 type stateBackend struct { 44 *state.State 45 modelTag names.ModelTag 46 } 47 48 // ModelTag is part of the Backend interface. 49 func (b *stateBackend) ModelTag() names.ModelTag { 50 return b.modelTag 51 } 52 53 // Backend supplies capabilities required by a Facade. 54 type Backend interface { 55 // ControllerTag tells the Facade which controller it should consider 56 // requests for. 57 ControllerTag() names.ControllerTag 58 59 // ModelTag tells the Facade what models it should consider requests for. 60 ModelTag() names.ModelTag 61 } 62 63 // NewFacade returns a singular-controller API facade, backed by the supplied 64 // state, so long as the authorizer represents a controller machine. 65 func NewFacade(backend Backend, claimer lease.Claimer, auth facade.Authorizer) (*Facade, error) { 66 if !auth.AuthController() { 67 return nil, common.ErrPerm 68 } 69 return &Facade{ 70 auth: auth, 71 modelTag: backend.ModelTag(), 72 controllerTag: backend.ControllerTag(), 73 singularClaimer: claimer, 74 }, nil 75 } 76 77 // Facade allows controller machines to request exclusive rights to administer 78 // some specific model or controller for a limited time. 79 type Facade struct { 80 auth facade.Authorizer 81 controllerTag names.ControllerTag 82 modelTag names.ModelTag 83 singularClaimer lease.Claimer 84 } 85 86 // Wait waits for the singular-controller lease to expire for all supplied 87 // entities. (In practice, any requests that do not refer to the connection's 88 // model or controller will be rejected.) 89 func (facade *Facade) Wait(ctx context.Context, args params.Entities) (result params.ErrorResults) { 90 result.Results = make([]params.ErrorResult, len(args.Entities)) 91 for i, entity := range args.Entities { 92 leaseId, err := facade.tagLeaseId(entity.Tag) 93 if err != nil { 94 result.Results[i].Error = common.ServerError(err) 95 continue 96 } 97 // TODO(axw) 2017-10-30 #1728594 98 // We should be waiting for the leases in parallel, 99 // so the waits do not affect one another. 100 err = facade.singularClaimer.WaitUntilExpired(leaseId, ctx.Done()) 101 result.Results[i].Error = common.ServerError(err) 102 } 103 return result 104 } 105 106 // Claim makes the supplied singular-controller lease requests. (In practice, 107 // any requests not for the connection's model or controller, or not on behalf 108 // of the connected ModelManager machine, will be rejected.) 109 func (facade *Facade) Claim(args params.SingularClaims) (result params.ErrorResults) { 110 result.Results = make([]params.ErrorResult, len(args.Claims)) 111 for i, claim := range args.Claims { 112 err := facade.claim(claim) 113 result.Results[i].Error = common.ServerError(err) 114 } 115 return result 116 } 117 118 func (facade *Facade) claim(claim params.SingularClaim) error { 119 if !allowedDuration(claim.Duration) { 120 return common.ErrPerm 121 } 122 leaseId, err := facade.tagLeaseId(claim.EntityTag) 123 if err != nil { 124 return errors.Trace(err) 125 } 126 holder := facade.auth.GetAuthTag().String() 127 if claim.ClaimantTag != holder { 128 return common.ErrPerm 129 } 130 return facade.singularClaimer.Claim(leaseId, holder, claim.Duration) 131 } 132 133 func (facade *Facade) tagLeaseId(tagString string) (string, error) { 134 tag, err := names.ParseTag(tagString) 135 if err != nil { 136 return "", errors.Trace(err) 137 } 138 switch tag { 139 case facade.modelTag, facade.controllerTag: 140 return tag.Id(), nil 141 } 142 return "", common.ErrPerm 143 } 144 145 // allowedDuration returns true if the supplied duration is at least one second, 146 // and no more than one minute. (We expect to refine the lease-length times, but 147 // these seem like reasonable bounds.) 148 func allowedDuration(duration time.Duration) bool { 149 if duration < time.Second { 150 return false 151 } 152 return duration <= time.Minute 153 }