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