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