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 = &params.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  }