diff --git a/.gitignore b/.gitignore index 7052962..64d612d 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,6 @@ universal_rebuild/docs_template_gen_v2 secrets/test.token.new secrets/*.token.new terraform-provider-nubes_* +universal_rebuild/service_spec_gen +universal_rebuild/service_ops_gen +universal_rebuild/service_params_gen diff --git a/devops/01_generate_yamls.sh b/devops/01_generate_yamls.sh index 38d6740..52ac253 100755 --- a/devops/01_generate_yamls.sh +++ b/devops/01_generate_yamls.sh @@ -139,6 +139,14 @@ fi rm -f "$FAILURES_FILE" +# Проверка что список сервисов не пустой +svc_count=$(grep -cv '^\s*$\|^\s*#' "$SERVICES_FILE" || true) +if [[ "$svc_count" -eq 0 ]]; then + echo "Error: services list is empty or all commented out: $SERVICES_FILE" >&2 + exit 2 +fi +echo "Generating YAML for ${svc_count} services from ${SERVICES_FILE}" + # Читаем список сервисов, пропуская пустые строки и комментарии. while IFS= read -r line; do line="${line//$'\r'/}" @@ -164,28 +172,41 @@ while IFS= read -r line; do # Если имя не указано в списке — подтягиваем по API. if [[ -z "$svc_name" ]]; then svc_name=$(python3 - <&2 + echo "${sid} api_error" >> "$FAILURES_FILE" + continue + fi fi # Один YAML на сервис формируется Go-генератором. diff --git a/internal/core/client.go b/internal/core/client.go index 77af981..012df85 100644 --- a/internal/core/client.go +++ b/internal/core/client.go @@ -25,11 +25,11 @@ func debugLog(format string, args ...interface{}) { // UniversalClient handles Nubes API logic type UniversalClient struct { - HttpClient *http.Client - ApiEndpoint string - ApiToken string - ProviderVersion string - LogLevel string + HttpClient *http.Client + ApiEndpoint string + ApiToken string + ProviderVersion string + LogLevel string } // Request models diff --git a/universal_rebuild/tools/service_spec_gen/generate_service_spec.go b/universal_rebuild/tools/service_spec_gen/generate_service_spec.go index 123959a..cc91655 100644 --- a/universal_rebuild/tools/service_spec_gen/generate_service_spec.go +++ b/universal_rebuild/tools/service_spec_gen/generate_service_spec.go @@ -388,30 +388,59 @@ func (c *apiClient) getServiceOperation(svcOperationId int) (serviceOperationInf } func (c *apiClient) getViaProxy(endpoint string, out interface{}) error { - // The API uses a proxy query parameter called "endpoint". - req, err := http.NewRequest("GET", c.endpoint, nil) - if err != nil { - return err + const maxRetries = 3 + baseDelay := 2 * time.Second + + var lastErr error + for attempt := 0; attempt <= maxRetries; attempt++ { + if attempt > 0 { + time.Sleep(baseDelay * time.Duration(1<<(attempt-1))) + } + + req, err := http.NewRequest("GET", c.endpoint, nil) + if err != nil { + return err + } + q := req.URL.Query() + q.Set("endpoint", endpoint) + req.URL.RawQuery = q.Encode() + if c.token != "" { + req.Header.Set("Authorization", "Bearer "+c.token) + } + req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Terraform-Provider-Nubes/Generator)") + resp, err := c.httpClient.Do(req) + if err != nil { + lastErr = err + if attempt < maxRetries { + continue + } + return err + } + + body, readErr := io.ReadAll(resp.Body) + resp.Body.Close() + if readErr != nil { + lastErr = readErr + if attempt < maxRetries { + continue + } + return readErr + } + + if resp.StatusCode != 200 { + err := fmt.Errorf("status %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) + // Retry on transient errors + if attempt < maxRetries && (resp.StatusCode == 429 || resp.StatusCode >= 500) { + lastErr = err + continue + } + return err + } + + return json.Unmarshal(body, out) } - q := req.URL.Query() - q.Set("endpoint", endpoint) - req.URL.RawQuery = q.Encode() - if c.token != "" { - req.Header.Set("Authorization", "Bearer "+c.token) - } - // User-Agent: браузерный, чтобы пройти DDoS-Guard (см. docs/ops/API_TOKENS.md). - req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Terraform-Provider-Nubes/Generator)") - resp, err := c.httpClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != 200 { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("status %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) - } - dec := json.NewDecoder(resp.Body) - return dec.Decode(out) + + return fmt.Errorf("getViaProxy failed after %d retries: %w", maxRetries, lastErr) } func (c *apiClient) collectOperations(ops []operationInfo) ([]OperationSpec, bool, bool, bool, error) {