Skip to content

Commit

Permalink
add retry to new 'do' functionality (#64)
Browse files Browse the repository at this point in the history
Co-authored-by: Dean Oren <[email protected]>
  • Loading branch information
do87 and Dean Oren authored Dec 21, 2022
1 parent 91c3a2f commit e3beb64
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 17 deletions.
38 changes: 33 additions & 5 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/SchwarzIT/community-stackit-go-client/internal/common"
"github.com/SchwarzIT/community-stackit-go-client/pkg/validate"
"golang.org/x/oauth2"
waitutil "k8s.io/apimachinery/pkg/util/wait"
)

// Client service for managing interactions with STACKIT API
Expand All @@ -26,6 +27,11 @@ type Client struct {
client *http.Client
config Config

// when an internal server error is encountered
// the call will be retried
RetryTimout time.Duration // timeout for retrying a call
RetryWait time.Duration // how long to wait before trying again

Services services

// Legacy
Expand All @@ -45,8 +51,10 @@ func New(ctx context.Context, cfg Config) (*Client, error) {
}

c := &Client{
config: cfg,
ctx: ctx,
config: cfg,
ctx: ctx,
RetryTimout: 2 * time.Minute,
RetryWait: 30 * time.Second,
}

c.setHttpClient(c.ctx)
Expand Down Expand Up @@ -121,15 +129,15 @@ func (c *Client) Request(ctx context.Context, method, path string, body []byte)
// LegacyDo performs the request, including retry if set
// To set retry, use WithRetry() which returns a shalow copy of the client
func (c *Client) LegacyDo(req *http.Request, v interface{}, errorHandlers ...func(*http.Response) error) (*http.Response, error) {
return c.do(req, v, errorHandlers...)
return c.legacyDo(req, v, errorHandlers...)
}

func (c *Client) Do(req *http.Request) (*http.Response, error) {
return c.LegacyDo(req, nil)
return c.do(req)
}

// Do performs the request and decodes the response if given interface != nil
func (c *Client) do(req *http.Request, v interface{}, errorHandlers ...func(*http.Response) error) (*http.Response, error) {
func (c *Client) legacyDo(req *http.Request, v interface{}, errorHandlers ...func(*http.Response) error) (*http.Response, error) {
resp, err := c.client.Do(req)
if err != nil {
return nil, err
Expand All @@ -153,6 +161,26 @@ func (c *Client) do(req *http.Request, v interface{}, errorHandlers ...func(*htt
return resp, err
}

// Do performs the request and decodes the response if given interface != nil
func (c *Client) do(req *http.Request) (resp *http.Response, err error) {
if err := waitutil.PollImmediate(c.RetryWait, c.RetryTimout, waitutil.ConditionFunc(
func() (bool, error) {
resp, err = c.client.Do(req)
if err != nil {
return false, err
}
if resp != nil && resp.StatusCode == http.StatusInternalServerError {
return false, nil
}
return true, nil
}),
); err != nil {
return resp, err
}

return resp, err
}

// MockServer mocks STACKIT api server
// and returns a client pointing to it, mux, teardown function and an error indicator
func MockServer() (c *Client, mux *http.ServeMux, teardown func(), err error) {
Expand Down
49 changes: 41 additions & 8 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/url"
"reflect"
"testing"
"time"

"github.com/SchwarzIT/community-stackit-go-client/pkg/consts"
)
Expand Down Expand Up @@ -244,21 +245,21 @@ func TestClient_SetToken(t *testing.T) {
}
}

func TestClient_DoWithRetryNonRetryableErrorAndTestBaseURLChange(t *testing.T) {
c, mux, teardown, err := MockServer()
func TestClient_GeneralTests(t *testing.T) {
c, _, teardown, err := MockServer()
defer teardown()
if err != nil {
t.Errorf("error from mock.AuthServer: %s", err.Error())
}

mux.HandleFunc("/err", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
})
basetime := 200 * time.Millisecond
c.RetryTimout = 5 * basetime
c.RetryWait = basetime

req, _ := c.Request(context.Background(), http.MethodGet, "/err", nil)
c.SetBaseURL("ht#@://aa")
req, _ := c.Request(context.Background(), http.MethodGet, "err2", nil)
if _, err := c.Do(req); err == nil {
t.Error("expected do request to return error but got nil instead")
t.Error("expected do request to return error")
}

c.SetBaseURL(consts.DEFAULT_BASE_URL)
Expand All @@ -270,3 +271,35 @@ func TestClient_DoWithRetryNonRetryableErrorAndTestBaseURLChange(t *testing.T) {
t.Errorf("expected base URL to be %s, got %s instead", consts.DEFAULT_BASE_URL, cfg.BaseUrl.String())
}
}

func TestClient_DoWithRetry(t *testing.T) {
c, mux, teardown, err := MockServer()
defer teardown()
if err != nil {
t.Errorf("error from mock.AuthServer: %s", err.Error())
}

basetime := 200 * time.Millisecond
ctx := context.Background()
ctx1, td1 := context.WithTimeout(ctx, 2*basetime)
defer td1()

c.RetryTimout = 5 * basetime
c.RetryWait = basetime

mux.HandleFunc("/ep", func(w http.ResponseWriter, r *http.Request) {
if ctx1.Err() == nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
w.Write([]byte("{}"))
})

req, _ := c.Request(context.Background(), http.MethodGet, "/ep", nil)
if _, err := c.Do(req); err != nil {
t.Error(err)
}
}
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ require (
github.com/pkg/errors v0.9.1
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b
golang.org/x/oauth2 v0.3.0
k8s.io/apimachinery v0.26.0
)

require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/golang/protobuf v1.5.2 // indirect
golang.org/x/net v0.3.0 // indirect
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect
)
15 changes: 12 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/do87/oapi-codegen v0.0.1 h1:Y60e0PhwIRcDafJE+W9U56dDX+U+AE6BL7P35ZWma4w=
github.com/do87/oapi-codegen v0.0.1/go.mod h1:TNxhKQvKeywM5hQPdfhD6B5O7t5ePBNGajSkA+NZv8c=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
Expand All @@ -29,8 +32,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4=
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 h1:Frnccbp+ok2GkUS2tC84yAq/U9Vg+0sIO7aRL3T4Xnc=
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand All @@ -45,3 +48,9 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg=
k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs=
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=

0 comments on commit e3beb64

Please sign in to comment.