Skip to content

Commit

Permalink
fix(sdk): For any call to api.ovh.com returning a 500 vendor, retry c…
Browse files Browse the repository at this point in the history
…all on ca.api.ovh.com

Should cover the cases where a canadian tenant is freshly created and agora sync has not yet been done on EU API.
This is a temporary fix until the issue is correctly handled

Signed-off-by: Xavier Duthil <[email protected]>
  • Loading branch information
XavierDuthil committed Mar 10, 2023
1 parent c0219b2 commit 52f4de5
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 2 deletions.
15 changes: 15 additions & 0 deletions cluster-autoscaler/cloudprovider/ovhcloud/sdk/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ limitations under the License.
package sdk

import (
"errors"
"fmt"
"net/http"
"strings"
)

const canadianTenantSyncErrorMessage = "Internal Server Error"

// APIError represents an error that can occurred while calling the API.
type APIError struct {
// Error message.
Expand Down Expand Up @@ -48,3 +53,13 @@ type (
func (e Error) Error() string {
return fmt.Sprintf("%d: %s", e.StatusCode, e.Message)
}

// IsPossiblyCanadianTenantSyncError returns whether the given error and URL could be due to the tenant being canadian and too recent.
// This is a temporary fix until the issue is correctly handled
func IsPossiblyCanadianTenantSyncError(err error, url string) bool {
var apiError *APIError
return (strings.HasPrefix(url, OvhEU) || strings.HasPrefix(url, "https://api.ovh.com/1.0")) &&
errors.As(err, &apiError) &&
apiError.Code == http.StatusInternalServerError &&
apiError.Message == canadianTenantSyncErrorMessage
}
26 changes: 24 additions & 2 deletions cluster-autoscaler/cloudprovider/ovhcloud/sdk/ovh.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ func (c *Client) DeleteUnAuthWithContext(ctx context.Context, url string, result

// timeDelta returns the time delta between the host and the remote API
func (c *Client) getTimeDelta() (time.Duration, error) {

if !c.timeDeltaDone {
// Ensure only one thread is updating
c.timeDeltaMutex.Lock()
Expand Down Expand Up @@ -434,12 +433,35 @@ func (c *Client) CallAPIWithContext(ctx context.Context, method, path string, re
if err != nil {
return err
}

req = req.WithContext(ctx)
response, err := c.Do(req)
if err != nil {
return err
}
return c.UnmarshalResponse(response, result)

err = c.UnmarshalResponse(response, result)
if err != nil {
// An error 500 on api.ovh.com could be due to the tenant being canadian and too recent, so let's retry on ca.api.ovh.
// This is a temporary fix until the issue is correctly handled
if IsPossiblyCanadianTenantSyncError(err, req.URL.String()) {
// Create a canadian API client with the same token
client, err2 := NewClient(OvhCA, "none", "none", "none")
if err2 != nil {
return fmt.Errorf("failed to create canadian ovh API client for fallback: %w", err2)
}
client.openStackToken = c.openStackToken

// Execute the same call on ca.api.ovh.com and ignore the potential error, we will return the original one
err2 = client.CallAPIWithContext(ctx, method, path, reqBody, result, queryParams, headers, needAuth)
if err2 == nil {
// OK on the canadian API, our job is done
return nil
}
}
}

return err
}

// UnmarshalResponse checks the response and unmarshals it into the response
Expand Down

0 comments on commit 52f4de5

Please sign in to comment.