github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/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 "time" 8 9 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/apiserver/common" 12 "github.com/juju/juju/apiserver/facade" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/core/lease" 15 "github.com/juju/juju/state" 16 ) 17 18 func init() { 19 common.RegisterStandardFacade( 20 "Singular", 1, 21 func(st *state.State, _ facade.Resources, auth facade.Authorizer) (*Facade, error) { 22 return NewFacade(st, auth) 23 }, 24 ) 25 } 26 27 // Backend supplies capabilities required by a Facade. 28 type Backend interface { 29 30 // ModelTag tells the Facade what models it should consider requests for. 31 ModelTag() names.ModelTag 32 33 // SingularClaimer allows the Facade to make claims. 34 SingularClaimer() lease.Claimer 35 } 36 37 // NewFacade returns a singular-controller API facade, backed by the supplied 38 // state, so long as the authorizer represents a controller machine. 39 func NewFacade(backend Backend, auth facade.Authorizer) (*Facade, error) { 40 if !auth.AuthModelManager() { 41 return nil, common.ErrPerm 42 } 43 return &Facade{ 44 auth: auth, 45 model: backend.ModelTag(), 46 claimer: backend.SingularClaimer(), 47 }, nil 48 } 49 50 // Facade allows controller machines to request exclusive rights to administer 51 // some specific model for a limited time. 52 type Facade struct { 53 auth facade.Authorizer 54 model names.ModelTag 55 claimer lease.Claimer 56 } 57 58 // Wait waits for the singular-controller lease to expire for all supplied 59 // entities. (In practice, any requests that do not refer to the connection's 60 // model will be rejected.) 61 func (facade *Facade) Wait(args params.Entities) (result params.ErrorResults) { 62 result.Results = make([]params.ErrorResult, len(args.Entities)) 63 for i, entity := range args.Entities { 64 var err error 65 switch { 66 case entity.Tag != facade.model.String(): 67 err = common.ErrPerm 68 default: 69 err = facade.claimer.WaitUntilExpired(facade.model.Id()) 70 } 71 result.Results[i].Error = common.ServerError(err) 72 } 73 return result 74 } 75 76 // Claim makes the supplied singular-controller lease requests. (In practice, 77 // any requests not for the connection's model, or not on behalf of the 78 // connected EnvironManager machine, will be rejected.) 79 func (facade *Facade) Claim(args params.SingularClaims) (result params.ErrorResults) { 80 result.Results = make([]params.ErrorResult, len(args.Claims)) 81 for i, claim := range args.Claims { 82 var err error 83 switch { 84 case claim.ModelTag != facade.model.String(): 85 err = common.ErrPerm 86 case claim.ControllerTag != facade.auth.GetAuthTag().String(): 87 err = common.ErrPerm 88 case !allowedDuration(claim.Duration): 89 err = common.ErrPerm 90 default: 91 err = facade.claimer.Claim(facade.model.Id(), claim.ControllerTag, claim.Duration) 92 } 93 result.Results[i].Error = common.ServerError(err) 94 } 95 return result 96 } 97 98 // allowedDuration returns true if the supplied duration is at least one second, 99 // and no more than one minute. (We expect to refine the lease-length times, but 100 // these seem like reasonable bounds.) 101 func allowedDuration(duration time.Duration) bool { 102 if duration < time.Second { 103 return false 104 } 105 return duration <= time.Minute 106 }