github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/retrystrategy/retrystrategy.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Copyright 2016 Cloudbase Solutions 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 package retrystrategy 6 7 import ( 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/state" 17 "github.com/juju/juju/state/watcher" 18 ) 19 20 // Right now, these are defined as constants, but the plan is to maybe make 21 // them configurable in the future 22 const ( 23 MinRetryTime = 5 * time.Second 24 MaxRetryTime = 5 * time.Minute 25 JitterRetryTime = true 26 RetryTimeFactor = 2 27 ) 28 29 func init() { 30 common.RegisterStandardFacade("RetryStrategy", 1, NewRetryStrategyAPI) 31 } 32 33 // RetryStrategy defines the methods exported by the RetryStrategy API facade. 34 type RetryStrategy interface { 35 RetryStrategy(params.Entities) (params.RetryStrategyResults, error) 36 WatchRetryStrategy(params.Entities) (params.NotifyWatchResults, error) 37 } 38 39 // RetryStrategyAPI implements RetryStrategy 40 type RetryStrategyAPI struct { 41 st *state.State 42 accessUnit common.GetAuthFunc 43 resources facade.Resources 44 } 45 46 var _ RetryStrategy = (*RetryStrategyAPI)(nil) 47 48 // NewRetryStrategyAPI creates a new API endpoint for getting retry strategies. 49 func NewRetryStrategyAPI( 50 st *state.State, 51 resources facade.Resources, 52 authorizer facade.Authorizer, 53 ) (*RetryStrategyAPI, error) { 54 if !authorizer.AuthUnitAgent() { 55 return nil, common.ErrPerm 56 } 57 return &RetryStrategyAPI{ 58 st: st, 59 accessUnit: func() (common.AuthFunc, error) { 60 return authorizer.AuthOwner, nil 61 }, 62 resources: resources, 63 }, nil 64 } 65 66 // RetryStrategy returns RetryStrategyResults that can be used by any code that uses 67 // to configure the retry timer that's currently in juju utils. 68 func (h *RetryStrategyAPI) RetryStrategy(args params.Entities) (params.RetryStrategyResults, error) { 69 results := params.RetryStrategyResults{ 70 Results: make([]params.RetryStrategyResult, len(args.Entities)), 71 } 72 canAccess, err := h.accessUnit() 73 if err != nil { 74 return params.RetryStrategyResults{}, errors.Trace(err) 75 } 76 config, err := h.st.ModelConfig() 77 if err != nil { 78 return params.RetryStrategyResults{}, errors.Trace(err) 79 } 80 for i, entity := range args.Entities { 81 tag, err := names.ParseTag(entity.Tag) 82 if err != nil { 83 results.Results[i].Error = common.ServerError(err) 84 continue 85 } 86 err = common.ErrPerm 87 if canAccess(tag) { 88 // Right now the only real configurable value is ShouldRetry, 89 // which is taken from the environment 90 // The rest are hardcoded 91 results.Results[i].Result = ¶ms.RetryStrategy{ 92 ShouldRetry: config.AutomaticallyRetryHooks(), 93 MinRetryTime: MinRetryTime, 94 MaxRetryTime: MaxRetryTime, 95 JitterRetryTime: JitterRetryTime, 96 RetryTimeFactor: RetryTimeFactor, 97 } 98 err = nil 99 } 100 results.Results[i].Error = common.ServerError(err) 101 } 102 return results, nil 103 } 104 105 // WatchRetryStrategy watches for changes to the environment. Currently we only allow 106 // changes to the boolean that determines whether retries should be attempted or not. 107 func (h *RetryStrategyAPI) WatchRetryStrategy(args params.Entities) (params.NotifyWatchResults, error) { 108 results := params.NotifyWatchResults{ 109 Results: make([]params.NotifyWatchResult, len(args.Entities)), 110 } 111 canAccess, err := h.accessUnit() 112 if err != nil { 113 return params.NotifyWatchResults{}, errors.Trace(err) 114 } 115 for i, entity := range args.Entities { 116 tag, err := names.ParseTag(entity.Tag) 117 if err != nil { 118 results.Results[i].Error = common.ServerError(err) 119 continue 120 } 121 err = common.ErrPerm 122 if canAccess(tag) { 123 watch := h.st.WatchForModelConfigChanges() 124 // Consume the initial event. Technically, API calls to Watch 125 // 'transmit' the initial event in the Watch response. But 126 // NotifyWatchers have no state to transmit. 127 if _, ok := <-watch.Changes(); ok { 128 results.Results[i].NotifyWatcherId = h.resources.Register(watch) 129 err = nil 130 } else { 131 err = watcher.EnsureErr(watch) 132 } 133 } 134 results.Results[i].Error = common.ServerError(err) 135 } 136 return results, nil 137 }