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