fix: add retry + User-Agent to Legacy provider doRequest (same P0.1 fix)

This commit is contained in:
“Naeel”
2026-06-30 17:31:05 +04:00
parent 1252014324
commit df450b1eec
+70 -32
View File
@@ -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) =====