diff --git a/CHANGELOG.md b/CHANGELOG.md index df08fd9bd..75eae87b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,41 @@ -## Release (YYYY-MM-DD) +## Release (2024-02-27) -- `core`: [v0.y.z] +- `core`: [v0.10.0](core/CHANGELOG.md#v0100-2024-02-27) - **Feature:** Add package `runtime`, which implements methods to be used when performing API requests. - **Feature:** Add method `WithCaptureHTTPResponse` to package `runtime`, which does the same as `config.WithCaptureHTTPResponse`. Method was moved to avoid confusion due to it not being a configuration option, and will be removed in a later release. - **Feature:** Add configuration option that, for the key flow, enables a goroutine to be spawned that will refresh the access token when it's close to expiring - **Deprecation:** Mark method `config.WithCaptureHTTPResponse` as deprecated, to avoid confusion due to it not being a configuration option. Use `runtime.WithCaptureHTTPResponse` instead. - **Deprecation:** Mark method `config.WithJWKSEndpoint` and field `config.Configuration.JWKSCustomUrl` as deprecated. Validation using JWKS was removed, for being redundant with token validation done in the APIs. These have no effect. -- **Breaking Change:** Remove method `KeyFlow.Clone`, that was no longer being used. +- **Deprecation:** + - Methods: + - `config.WithMaxRetries` + - `config.WithWaitBetweenCalls` + - `config.WithRetryTimeout` + - `clients.NewRetryConfig` + - Fields: + - `clients.KeyFlowConfig.ClientRetry` + - `clients.TokenFlowConfig.ClientRetry` + - `clients.NoAuthFlowConfig.ClientRetry` + - `clients.RetryConfig` + - Retry options removed to reduce complexity of the clients. If this functionality is needed, you can provide your own custom HTTP client. +- **Breaking Change:** Change signature of `auth.NoAuth`, which no longer takes `clients.RetryConfig` as argument. +- **Breaking Change:** + - Methods: + - `clients.KeyFlow.Clone` + - `clients.TokenFlow.Clone` + - `clients.NoAuthFlow.Clone` + - `clients.Do` + - Fields: + - `clients.DefaultRetryMaxRetries` + - `clients.DefaultRetryWaitBetweenCalls` + - `clients.DefaultRetryTimeout` + - Constants: + - `clients.ClientTimeoutErr` + - `clients.ClientContextDeadlineErr` + - `clients.ClientConnectionRefusedErr` + - `clients.ClientEOFError` + - `clients.Environment` + - Removed to reduce complexity of the clients, they were no longer being used. ## Release (2024-02-07) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 07e9e770a..77cf30423 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,6 +1,36 @@ -## v0.10.0 (YYYY-MM-DD) +## v0.10.0 (2024-02-27) - **Feature:** Add configuration option that, for the key flow, enables a goroutine to be spawned that will refresh the access token when it's close to expiring +- **Deprecation:** + - Methods: + - `config.WithMaxRetries` + - `config.WithWaitBetweenCalls` + - `config.WithRetryTimeout` + - `clients.NewRetryConfig` + - Fields: + - `clients.KeyFlowConfig.ClientRetry` + - `clients.TokenFlowConfig.ClientRetry` + - `clients.NoAuthFlowConfig.ClientRetry` + - `clients.RetryConfig` + - Retry options were removed to reduce complexity of the clients. If this functionality is needed, you can provide your own custom HTTP client. +- **Breaking Change:** Change signature of `auth.NoAuth`, which no longer takes `clients.RetryConfig` as argument. +- **Breaking Change:** + - Methods: + - `clients.KeyFlow.Clone` + - `clients.TokenFlow.Clone` + - `clients.NoAuthFlow.Clone` + - `clients.Do` + - Fields: + - `clients.DefaultRetryMaxRetries` + - `clients.DefaultRetryWaitBetweenCalls` + - `clients.DefaultRetryTimeout` + - Constants: + - `clients.ClientTimeoutErr` + - `clients.ClientContextDeadlineErr` + - `clients.ClientConnectionRefusedErr` + - `clients.ClientEOFError` + - `clients.Environment` + - Removed to reduce complexity of the clients, they were no longer being used. ## v0.9.0 (2024-02-19) diff --git a/core/auth/auth.go b/core/auth/auth.go index 45d2e046d..9164b6859 100644 --- a/core/auth/auth.go +++ b/core/auth/auth.go @@ -39,7 +39,7 @@ func SetupAuth(cfg *config.Configuration) (rt http.RoundTripper, err error) { if cfg.CustomAuth != nil { return cfg.CustomAuth, nil } else if cfg.NoAuth { - noAuthRoundTripper, err := NoAuth(cfg.RetryOptions) + noAuthRoundTripper, err := NoAuth() if err != nil { return nil, fmt.Errorf("configuring no auth client: %w", err) } @@ -93,10 +93,8 @@ func DefaultAuth(cfg *config.Configuration) (rt http.RoundTripper, err error) { // NoAuth configures a flow without authentication and returns an http.RoundTripper // that can be used to make unauthenticated requests -func NoAuth(retryOptions *clients.RetryConfig) (rt http.RoundTripper, err error) { - noAuthConfig := clients.NoAuthFlowConfig{ - ClientRetry: retryOptions, - } +func NoAuth() (rt http.RoundTripper, err error) { + noAuthConfig := clients.NoAuthFlowConfig{} noAuthRoundTripper := &clients.NoAuthFlow{} if err := noAuthRoundTripper.Init(noAuthConfig); err != nil { return nil, fmt.Errorf("initializing client: %w", err) @@ -125,7 +123,6 @@ func TokenAuth(cfg *config.Configuration) (http.RoundTripper, error) { tokenCfg := clients.TokenFlowConfig{ ServiceAccountToken: cfg.Token, - ClientRetry: cfg.RetryOptions, } client := &clients.TokenFlow{} @@ -181,7 +178,6 @@ func KeyAuth(cfg *config.Configuration) (http.RoundTripper, error) { keyCfg := clients.KeyFlowConfig{ ServiceAccountKey: serviceAccountKey, PrivateKey: cfg.PrivateKey, - ClientRetry: cfg.RetryOptions, TokenUrl: cfg.TokenCustomUrl, BackgroundTokenRefreshContext: cfg.BackgroundTokenRefreshContext, } diff --git a/core/auth/auth_test.go b/core/auth/auth_test.go index c7c0419de..f48858471 100644 --- a/core/auth/auth_test.go +++ b/core/auth/auth_test.go @@ -558,7 +558,7 @@ func TestNoAuth(t *testing.T) { } { t.Run(test.desc, func(t *testing.T) { // Get the default authentication client and ensure that it's not nil - authClient, err := NoAuth(nil) + authClient, err := NoAuth() if err != nil { t.Fatalf("Test returned error on valid test case: %v", err) } diff --git a/core/clients/clients.go b/core/clients/clients.go index 0ea4bd118..9d7fed6dc 100644 --- a/core/clients/clients.go +++ b/core/clients/clients.go @@ -1,35 +1,14 @@ package clients import ( - "context" - "fmt" - "net/http" - "strings" "time" - - "github.com/cenkalti/backoff/v4" -) - -const ( - // API configuration options: - Environment = "STACKIT_ENV" -) - -const ( - // Known error messages - ClientTimeoutErr = "Client.Timeout exceeded while awaiting headers" - ClientContextDeadlineErr = "context deadline exceeded" - ClientConnectionRefusedErr = "connection refused" - ClientEOFError = "unexpected EOF" ) const ( - DefaultClientTimeout = time.Minute - DefaultRetryMaxRetries = 3 - DefaultRetryWaitBetweenCalls = 30 * time.Second - DefaultRetryTimeout = 2 * time.Minute + DefaultClientTimeout = time.Minute ) +// Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. type RetryConfig struct { MaxRetries int // Max retries WaitBetweenCalls time.Duration // Time to wait between requests @@ -37,69 +16,7 @@ type RetryConfig struct { ClientTimeout time.Duration // HTTP Client timeout } +// Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. func NewRetryConfig() *RetryConfig { - return &RetryConfig{ - MaxRetries: DefaultRetryMaxRetries, - WaitBetweenCalls: DefaultRetryWaitBetweenCalls, - RetryTimeout: DefaultRetryTimeout, - ClientTimeout: DefaultClientTimeout, - } -} - -// Do performs the request, retrying according to the configurations provided -func Do(client *http.Client, req *http.Request, cfg *RetryConfig) (resp *http.Response, err error) { - if cfg == nil { - cfg = NewRetryConfig() - } - if client == nil { - client = &http.Client{} - } - client.Timeout = cfg.ClientTimeout - - maxRetries := cfg.MaxRetries - ctx, cancel := context.WithTimeout(req.Context(), cfg.RetryTimeout) - defer cancel() - b := backoff.WithContext(backoff.NewConstantBackOff(cfg.WaitBetweenCalls), ctx) - - err = backoff.Retry(func() error { - resp, err = client.Do(req) //nolint:bodyclose // body is closed by the caller functions - if err != nil { - if maxRetries > 0 { - if errorIsOneOf(err, ClientTimeoutErr, ClientContextDeadlineErr, ClientConnectionRefusedErr) || - (req.Method == http.MethodGet && strings.Contains(err.Error(), ClientEOFError)) { - // reduce retries counter and retry - maxRetries-- - return err - } - } - return backoff.Permanent(err) - } - if maxRetries > 0 && resp != nil { - if resp.StatusCode == http.StatusBadGateway || - resp.StatusCode == http.StatusGatewayTimeout { - maxRetries-- - return fmt.Errorf("request returned a gateway error") - } - } - return nil - }, b) - if err != nil { - return resp, fmt.Errorf("url: %s\nmethod: %s\n err: %w", req.URL.String(), req.Method, err) - } - - return resp, err -} - -// ErrorIsOneOf checks if a given error message -// has one of the provided sub strings -func errorIsOneOf(err error, msgs ...string) bool { - if err == nil { - return false - } - for _, m := range msgs { - if strings.Contains(err.Error(), m) { - return true - } - } - return false + return &RetryConfig{} } diff --git a/core/clients/clients_test.go b/core/clients/clients_test.go deleted file mode 100644 index 07f349b65..000000000 --- a/core/clients/clients_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package clients - -import ( - "fmt" - "io" - "net/http" - "net/http/httptest" - "net/url" - "reflect" - "strings" - "testing" - "time" -) - -func TestNewRetryConfig(t *testing.T) { - got := NewRetryConfig() - want := RetryConfig{ - MaxRetries: DefaultRetryMaxRetries, - WaitBetweenCalls: DefaultRetryWaitBetweenCalls, - RetryTimeout: DefaultRetryTimeout, - ClientTimeout: DefaultClientTimeout, - } - if got == nil { - t.Error("NewRetryConfig returned nil") - } else if !reflect.DeepEqual(*got, want) { - t.Errorf("%+v != %+v", *got, want) - } -} - -func TestDo(t *testing.T) { - type args struct { - cfg *RetryConfig - serverStatus int - serverResponse string - } - tests := []struct { - name string - args args - wantResp *http.Response - wantErr bool - errMsg string - }{ - {"all ok", args{ - cfg: &RetryConfig{0, time.Microsecond, time.Second, DefaultClientTimeout}, - serverStatus: http.StatusOK, - serverResponse: `{"status":"ok", "testing": "%s"}`, - }, &http.Response{StatusCode: http.StatusOK}, false, ""}, - {"all ok nil client", args{ - cfg: &RetryConfig{0, time.Microsecond, time.Second, DefaultClientTimeout}, - serverStatus: http.StatusOK, - serverResponse: `{"status":"ok", "testing": "%s"}`, - }, &http.Response{StatusCode: http.StatusOK}, false, ""}, - {"fail 1", args{ - cfg: &RetryConfig{1, time.Microsecond, time.Second, DefaultClientTimeout}, - serverStatus: http.StatusInternalServerError, - serverResponse: `{"status":"error 1", "testing": "%s"}`, - }, &http.Response{StatusCode: http.StatusInternalServerError}, false, ""}, - {"fail 2 - timeout error", args{ - cfg: &RetryConfig{3, time.Microsecond, time.Second, DefaultClientTimeout}, - serverStatus: http.StatusOK, - serverResponse: `{"status":"ok", "testing": "%s"}`, - }, &http.Response{StatusCode: http.StatusOK}, true, "no such host"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(tt.args.serverStatus) - fmt.Fprintln(w, fmt.Sprintf(tt.args.serverResponse, tt.name)) - }) - server := httptest.NewServer(handler) - defer server.Close() - u, err := url.Parse(server.URL) - if err != nil { - t.Error(err) - return - } - req, err := http.NewRequest(http.MethodGet, u.String(), http.NoBody) - if err != nil { - t.Error(err) - return - } - c := &http.Client{} - if tt.name == "fail 2 - timeout error" && server != nil { - t.Log("closing server") - server.Close() - } - if tt.name == "all ok nil client" { - c = nil - } - gotResp, err := Do(c, req, tt.args.cfg) - if err == nil { - // Defer discard and close the body - defer func() { - if _, discardErr := io.Copy(io.Discard, gotResp.Body); discardErr != nil && err == nil { - err = discardErr - } - if closeErr := gotResp.Body.Close(); closeErr != nil && err == nil { - err = closeErr - } - }() - } - if server != nil { - server.Close() - } - if (err != nil) != tt.wantErr && (err != nil && !strings.Contains(err.Error(), tt.errMsg)) { - body := []byte{} - if gotResp != nil { - body, err = io.ReadAll(gotResp.Body) - if err != nil { - t.Error(err) - return - } - } - t.Errorf("do() error = %v, wantErr %v, got: %s", err, tt.wantErr, string(body)) - return - } - if gotResp != nil && tt.wantResp.StatusCode != gotResp.StatusCode { - t.Errorf("do() status code = %v, want %v", tt.wantResp.StatusCode, gotResp.StatusCode) - } - }) - } -} diff --git a/core/clients/key_flow.go b/core/clients/key_flow.go index e935abdb6..3c8223318 100644 --- a/core/clients/key_flow.go +++ b/core/clients/key_flow.go @@ -36,7 +36,7 @@ const ( type KeyFlow struct { client *http.Client config *KeyFlowConfig - doer func(client *http.Client, req *http.Request, cfg *RetryConfig) (resp *http.Response, err error) + doer func(req *http.Request) (resp *http.Response, err error) key *ServiceAccountKeyResponse privateKey *rsa.PrivateKey privateKeyPEM []byte @@ -47,8 +47,9 @@ type KeyFlow struct { // KeyFlowConfig is the flow config type KeyFlowConfig struct { - ServiceAccountKey *ServiceAccountKeyResponse - PrivateKey string + ServiceAccountKey *ServiceAccountKeyResponse + PrivateKey string + // Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. ClientRetry *RetryConfig TokenUrl string BackgroundTokenRefreshContext context.Context // Functionality is enabled if this isn't nil @@ -119,15 +120,11 @@ func (c *KeyFlow) Init(cfg *KeyFlowConfig) error { // No concurrency at this point, so no mutex check needed c.token = &TokenResponseBody{} c.config = cfg - c.doer = Do if c.config.TokenUrl == "" { c.config.TokenUrl = tokenAPI } c.configureHTTPClient() - if c.config.ClientRetry == nil { - c.config.ClientRetry = NewRetryConfig() - } err := c.validate() if err != nil { return err @@ -175,11 +172,15 @@ func (c *KeyFlow) RoundTrip(req *http.Request) (*http.Response, error) { return nil, err } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken)) - return c.doer(c.client, req, c.config.ClientRetry) + return c.doer(req) } // GetAccessToken returns a short-lived access token and saves the access and refresh tokens in the token field func (c *KeyFlow) GetAccessToken() (string, error) { + if c.client == nil { + return "", fmt.Errorf("nil http client, please run Init()") + } + c.tokenMutex.RLock() accessToken := c.token.AccessToken c.tokenMutex.RUnlock() @@ -202,6 +203,7 @@ func (c *KeyFlow) configureHTTPClient() { client := &http.Client{} client.Timeout = DefaultClientTimeout c.client = client + c.doer = c.client.Do } // validate the client is configured well @@ -272,6 +274,10 @@ func (c *KeyFlow) createAccessToken() (err error) { // createAccessTokenWithRefreshToken creates an access token using // an existing pre-validated refresh token func (c *KeyFlow) createAccessTokenWithRefreshToken() (err error) { + if c.client == nil { + return fmt.Errorf("nil http client, please run Init()") + } + c.tokenMutex.RLock() refreshToken := c.token.RefreshToken c.tokenMutex.RUnlock() @@ -319,7 +325,7 @@ func (c *KeyFlow) requestToken(grant, assertion string) (*http.Response, error) return nil, err } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - return c.doer(&http.Client{}, req, c.config.ClientRetry) + return c.doer(req) } // parseTokenResponse parses the response from the server diff --git a/core/clients/key_flow_continuous_refresh_test.go b/core/clients/key_flow_continuous_refresh_test.go index d439f91cb..43e4fc4b6 100644 --- a/core/clients/key_flow_continuous_refresh_test.go +++ b/core/clients/key_flow_continuous_refresh_test.go @@ -86,7 +86,7 @@ func TestContinuousRefreshToken(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { numberDoCalls := 0 - mockDo := func(client *http.Client, req *http.Request, cfg *RetryConfig) (resp *http.Response, err error) { + mockDo := func(req *http.Request) (resp *http.Response, err error) { numberDoCalls++ if tt.doError != nil { @@ -127,10 +127,10 @@ func TestContinuousRefreshToken(t *testing.T) { keyFlow := &KeyFlow{ config: &KeyFlowConfig{ - ClientRetry: NewRetryConfig(), BackgroundTokenRefreshContext: ctx, }, - doer: mockDo, + client: &http.Client{}, + doer: mockDo, token: &TokenResponseBody{ AccessToken: accessToken, }, @@ -212,7 +212,7 @@ func TestContinuousRefreshTokenConcurrency(t *testing.T) { doTestPhase1RequestDone := false doTestPhase2RequestDone := false doTestPhase4RequestDone := false - mockDo := func(client *http.Client, req *http.Request, cfg *RetryConfig) (resp *http.Response, err error) { + mockDo := func(req *http.Request) (resp *http.Response, err error) { switch currentTestPhase { default: t.Fatalf("Do call: unexpected request during test phase %d", currentTestPhase) @@ -298,7 +298,6 @@ func TestContinuousRefreshTokenConcurrency(t *testing.T) { keyFlow := &KeyFlow{ client: &http.Client{}, config: &KeyFlowConfig{ - ClientRetry: NewRetryConfig(), BackgroundTokenRefreshContext: ctx, }, doer: mockDo, diff --git a/core/clients/key_flow_test.go b/core/clients/key_flow_test.go index afca6d106..9d8673c1e 100644 --- a/core/clients/key_flow_test.go +++ b/core/clients/key_flow_test.go @@ -268,12 +268,12 @@ func TestRequestToken(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { - mockDo := func(client *http.Client, req *http.Request, cfg *RetryConfig) (resp *http.Response, err error) { + mockDo := func(req *http.Request) (resp *http.Response, err error) { return tt.mockResponse, tt.mockError } c := &KeyFlow{ - config: &KeyFlowConfig{ClientRetry: NewRetryConfig()}, + config: &KeyFlowConfig{}, doer: mockDo, } diff --git a/core/clients/no_auth_flow.go b/core/clients/no_auth_flow.go index 552ca5a77..4db1bf156 100644 --- a/core/clients/no_auth_flow.go +++ b/core/clients/no_auth_flow.go @@ -12,6 +12,7 @@ type NoAuthFlow struct { // NoAuthFlowConfig holds the configuration for the unauthenticated flow type NoAuthFlowConfig struct { + // Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. ClientRetry *RetryConfig } @@ -23,34 +24,18 @@ func (c *NoAuthFlow) GetConfig() NoAuthFlowConfig { return *c.config } -func (c *NoAuthFlow) Init(cfg NoAuthFlowConfig) error { - c.config = &NoAuthFlowConfig{ - ClientRetry: cfg.ClientRetry, - } +func (c *NoAuthFlow) Init(_ NoAuthFlowConfig) error { + c.config = &NoAuthFlowConfig{} c.client = &http.Client{ Timeout: DefaultClientTimeout, } - if c.config.ClientRetry == nil { - c.config.ClientRetry = NewRetryConfig() - } return nil } -// Clone creates a clone of the client -func (c *NoAuthFlow) Clone() interface{} { - sc := *c - nc := &sc - cl := *nc.client - cf := *nc.config - nc.client = &cl - nc.config = &cf - return c -} - // Roundtrip performs the request func (c *NoAuthFlow) RoundTrip(req *http.Request) (*http.Response, error) { if c.client == nil { return nil, fmt.Errorf("please run Init()") } - return Do(c.client, req, c.config.ClientRetry) + return c.client.Do(req) } diff --git a/core/clients/no_auth_flow_test.go b/core/clients/no_auth_flow_test.go index 3801a1df6..00c913d87 100644 --- a/core/clients/no_auth_flow_test.go +++ b/core/clients/no_auth_flow_test.go @@ -28,9 +28,6 @@ func TestNoAuthFlow_Init(t *testing.T) { if err := c.Init(tt.args.cfg); (err != nil) != tt.wantErr { t.Errorf("NoAuthFlow.Init() error = %v, wantErr %v", err, tt.wantErr) } - if c.config == nil { - t.Error("config is nil") - } }) } } @@ -38,7 +35,6 @@ func TestNoAuthFlow_Init(t *testing.T) { func TestNoAuthFlow_Do(t *testing.T) { type fields struct { client *http.Client - config *NoAuthFlowConfig } type args struct{} tests := []struct { @@ -50,7 +46,7 @@ func TestNoAuthFlow_Do(t *testing.T) { }{ { name: "fail", - fields: fields{nil, nil}, + fields: fields{nil}, args: args{}, want: 0, wantErr: true, @@ -59,9 +55,6 @@ func TestNoAuthFlow_Do(t *testing.T) { name: "success", fields: fields{ &http.Client{}, - &NoAuthFlowConfig{ - ClientRetry: &RetryConfig{}, - }, }, args: args{}, want: http.StatusOK, @@ -72,7 +65,6 @@ func TestNoAuthFlow_Do(t *testing.T) { t.Run(tt.name, func(t *testing.T) { c := &NoAuthFlow{ client: tt.fields.client, - config: tt.fields.config, } handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") diff --git a/core/clients/token_flow.go b/core/clients/token_flow.go index ed2d8c3c2..5aad94321 100644 --- a/core/clients/token_flow.go +++ b/core/clients/token_flow.go @@ -21,7 +21,8 @@ type TokenFlow struct { type TokenFlowConfig struct { ServiceAccountEmail string ServiceAccountToken string - ClientRetry *RetryConfig + // Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. + ClientRetry *RetryConfig } // GetConfig returns the flow configuration @@ -35,23 +36,9 @@ func (c *TokenFlow) GetConfig() TokenFlowConfig { func (c *TokenFlow) Init(cfg *TokenFlowConfig) error { c.config = cfg c.configureHTTPClient() - if c.config.ClientRetry == nil { - c.config.ClientRetry = NewRetryConfig() - } return c.validate() } -// Clone creates a clone of the client -func (c *TokenFlow) Clone() interface{} { - sc := *c - nc := &sc - cl := *nc.client - cf := *nc.config - nc.client = &cl - nc.config = &cf - return c -} - // configureHTTPClient configures the HTTP client func (c *TokenFlow) configureHTTPClient() { client := &http.Client{} @@ -73,5 +60,5 @@ func (c *TokenFlow) RoundTrip(req *http.Request) (*http.Response, error) { return nil, fmt.Errorf("please run Init()") } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.config.ServiceAccountToken)) - return Do(c.client, req, c.config.ClientRetry) + return c.client.Do(req) } diff --git a/core/clients/token_flow_test.go b/core/clients/token_flow_test.go index 3faaf403a..cc6e62a41 100644 --- a/core/clients/token_flow_test.go +++ b/core/clients/token_flow_test.go @@ -7,7 +7,6 @@ import ( "net/http/httptest" "net/url" "os" - "reflect" "testing" ) @@ -64,7 +63,7 @@ func TestTokenFlow_Do(t *testing.T) { wantErr bool }{ {"fail", fields{nil, nil}, args{}, 0, true}, - {"success", fields{&http.Client{}, &TokenFlowConfig{ClientRetry: &RetryConfig{}}}, args{}, http.StatusOK, false}, + {"success", fields{&http.Client{}, &TokenFlowConfig{}}, args{}, http.StatusOK, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -111,19 +110,3 @@ func TestTokenFlow_Do(t *testing.T) { }) } } - -func TestTokenClone(t *testing.T) { - c := &TokenFlow{ - client: &http.Client{}, - config: &TokenFlowConfig{}, - } - - clone, ok := c.Clone().(*TokenFlow) - if !ok { - t.Fatalf("Type assertion failed") - } - - if !reflect.DeepEqual(c, clone) { - t.Errorf("Clone() = %v, want %v", clone, c) - } -} diff --git a/core/config/config.go b/core/config/config.go index aa4e33dbb..7f6183384 100644 --- a/core/config/config.go +++ b/core/config/config.go @@ -83,7 +83,6 @@ type Configuration struct { Servers ServerConfigurations OperationServers map[string]ServerConfigurations HTTPClient *http.Client - RetryOptions *clients.RetryConfig // If != nil, a goroutine will be launched that will refresh the service account's access token when it's close to being expired. // The goroutine is killed whenever this context is canceled. @@ -93,6 +92,8 @@ type Configuration struct { // Deprecated: validation using JWKS was removed, for being redundant with token validation done in the APIs. This field has no effect, and will be removed in a later update JWKSCustomUrl string `json:"jwksCustomUrl,omitempty"` + // Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. This field has no effect, and will be removed in a later update + RetryOptions *clients.RetryConfig //nolint:staticcheck //will be removed in a later update setCustomEndpoint bool } @@ -231,36 +232,23 @@ func WithToken(token string) ConfigurationOption { } } -// WithMaxRetries returns a ConfigurationOption that specifies the maximum number of retries in case of an error -// when making a request -func WithMaxRetries(maxRetries int) ConfigurationOption { +// Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. This option has no effect, and will be removed in a later update +func WithMaxRetries(_ int) ConfigurationOption { return func(config *Configuration) error { - if config.RetryOptions == nil { - config.RetryOptions = clients.NewRetryConfig() - } - config.RetryOptions.MaxRetries = maxRetries return nil } } -// WithWaitBetweenCalls returns a ConfigurationOption that specifies the time to wait between calls to an API -func WithWaitBetweenCalls(wait time.Duration) ConfigurationOption { +// Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. This option has no effect, and will be removed in a later update +func WithWaitBetweenCalls(_ time.Duration) ConfigurationOption { return func(config *Configuration) error { - if config.RetryOptions == nil { - config.RetryOptions = clients.NewRetryConfig() - } - config.RetryOptions.WaitBetweenCalls = wait return nil } } -// WithRetryTimeout returns a ConfigurationOption that specifies the maximum time for retries -func WithRetryTimeout(retryTimeout time.Duration) ConfigurationOption { +// Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. This option has no effect, and will be removed in a later update +func WithRetryTimeout(_ time.Duration) ConfigurationOption { return func(config *Configuration) error { - if config.RetryOptions == nil { - config.RetryOptions = clients.NewRetryConfig() - } - config.RetryOptions.RetryTimeout = retryTimeout return nil } } diff --git a/core/go.mod b/core/go.mod index cc67b514a..a5c52be99 100644 --- a/core/go.mod +++ b/core/go.mod @@ -3,7 +3,6 @@ module github.com/stackitcloud/stackit-sdk-go/core go 1.18 require ( - github.com/cenkalti/backoff/v4 v4.2.1 github.com/golang-jwt/jwt/v5 v5.2.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 diff --git a/core/go.sum b/core/go.sum index d2b7f854f..893a691b7 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,3 @@ -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= diff --git a/examples/configuration/configuration.go b/examples/configuration/configuration.go index 68470a0aa..ec6964cdb 100644 --- a/examples/configuration/configuration.go +++ b/examples/configuration/configuration.go @@ -35,16 +35,6 @@ func main() { os.Exit(1) } - // Create a new API client with custom configuration for retries - _, err = dns.NewAPIClient( - config.WithMaxRetries(5), - config.WithWaitBetweenCalls(10*time.Second), - config.WithRetryTimeout(1*time.Minute)) - if err != nil { - fmt.Fprintf(os.Stderr, "[DNS API] Creating API client: %v\n", err) - os.Exit(1) - } - // Create a new API client with custom endpoint, e.g. for accessing the QA environment _, err = dns.NewAPIClient(config.WithEndpoint("www.api-qa-url.com")) if err != nil {