github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/pkg/retry/retry.go (about)

     1  package retry
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"time"
     7  
     8  	"github.com/avast/retry-go/v4"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  // ExecutableHTTPFunc defines a generic HTTP function to be executed
    13  type ExecutableHTTPFunc func() (*http.Response, error)
    14  
    15  // Config configuration for the HTTPExecutor
    16  type Config struct {
    17  	Attempts uint          `envconfig:"APP_HTTP_RETRY_ATTEMPTS"`
    18  	Delay    time.Duration `envconfig:"APP_HTTP_RETRY_DELAY"`
    19  }
    20  
    21  // HTTPExecutor is capable of executing HTTP requests with a leveraged retry mechanism for more resilience
    22  type HTTPExecutor struct {
    23  	attempts              uint
    24  	delay                 time.Duration
    25  	acceptableStatusCodes []int
    26  }
    27  
    28  // NewHTTPExecutor constructs an HTTPExecutor based on the provided Config
    29  func NewHTTPExecutor(config *Config) *HTTPExecutor {
    30  	return &HTTPExecutor{
    31  		attempts:              config.Attempts,
    32  		delay:                 config.Delay,
    33  		acceptableStatusCodes: []int{http.StatusOK},
    34  	}
    35  }
    36  
    37  // WithAcceptableStatusCodes allows overriding the default acceptableStatusCodes values
    38  func (he *HTTPExecutor) WithAcceptableStatusCodes(statusCodes []int) {
    39  	he.acceptableStatusCodes = statusCodes
    40  }
    41  
    42  // Execute wraps the provided ExecutableHTTPFunc with a retry mechanism and executes it
    43  func (he *HTTPExecutor) Execute(doRequest ExecutableHTTPFunc) (*http.Response, error) {
    44  	var resp *http.Response
    45  	var err error
    46  	err = retry.Do(func() error {
    47  		resp, err = doRequest()
    48  		if err != nil {
    49  			return err
    50  		}
    51  
    52  		for _, code := range he.acceptableStatusCodes {
    53  			if resp.StatusCode == code {
    54  				return nil
    55  			}
    56  		}
    57  
    58  		return errors.New(fmt.Sprintf("unexpected status code: %d", resp.StatusCode))
    59  	}, retry.Attempts(he.attempts), retry.Delay(he.delay))
    60  
    61  	return resp, err
    62  }