fix: add retry + User-Agent to Legacy provider doRequest (same P0.1 fix)
This commit is contained in:
+70
-32
@@ -173,44 +173,82 @@ func (c *UniversalClient) CreateGenericInstance(ctx context.Context, serviceId i
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *UniversalClient) doRequest(ctx context.Context, method, path string, payload interface{}) ([]byte, http.Header, error) {
|
func (c *UniversalClient) doRequest(ctx context.Context, method, path string, payload interface{}) ([]byte, http.Header, error) {
|
||||||
var body io.Reader
|
// User-Agent: браузерный, чтобы пройти DDoS-Guard (см. docs/ops/API_TOKENS.md).
|
||||||
if payload != nil {
|
const userAgent = "Mozilla/5.0 (compatible; Terraform-Provider-Nubes)"
|
||||||
b, err := json.Marshal(payload)
|
const maxRetries = 3
|
||||||
|
baseDelay := 2 * time.Second
|
||||||
|
|
||||||
|
var lastErr error
|
||||||
|
for attempt := 0; attempt <= maxRetries; attempt++ {
|
||||||
|
if attempt > 0 {
|
||||||
|
delay := baseDelay * time.Duration(1<<(attempt-1))
|
||||||
|
select {
|
||||||
|
case <-time.After(delay):
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, nil, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var body io.Reader
|
||||||
|
if payload != nil {
|
||||||
|
b, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
body = bytes.NewBuffer(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, method, c.ApiEndpoint+path, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
body = bytes.NewBuffer(b)
|
|
||||||
|
req.Close = true
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("User-Agent", userAgent)
|
||||||
|
if c.ApiToken != "" {
|
||||||
|
req.Header.Set("Authorization", "Bearer "+c.ApiToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.HttpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
if attempt < maxRetries {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
if attempt < maxRetries {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
err := fmt.Errorf("API error %d: %s", resp.StatusCode, string(respBody))
|
||||||
|
if attempt < maxRetries && method == "GET" && isRetryable(resp.StatusCode) {
|
||||||
|
lastErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBody, resp.Header, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, method, c.ApiEndpoint+path, body)
|
return nil, nil, fmt.Errorf("doRequest failed after %d retries: %w", maxRetries, lastErr)
|
||||||
if err != nil {
|
}
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force close connection
|
func isRetryable(statusCode int) bool {
|
||||||
req.Close = true
|
return statusCode == http.StatusTooManyRequests ||
|
||||||
|
statusCode == http.StatusServiceUnavailable ||
|
||||||
req.Header.Set("Content-Type", "application/json")
|
statusCode == http.StatusBadGateway ||
|
||||||
if c.ApiToken != "" {
|
statusCode == http.StatusGatewayTimeout
|
||||||
req.Header.Set("Authorization", "Bearer "+c.ApiToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.HttpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode >= 400 {
|
|
||||||
return nil, nil, fmt.Errorf("API error %d: %s", resp.StatusCode, string(respBody))
|
|
||||||
}
|
|
||||||
|
|
||||||
return respBody, resp.Header, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== UNIVERSAL FLOW (APPEND-ONLY) =====
|
// ===== UNIVERSAL FLOW (APPEND-ONLY) =====
|
||||||
|
|||||||
Reference in New Issue
Block a user