@@ -5,16 +5,19 @@ import (
55 "encoding/json"
66 "fmt"
77 "net/http"
8+ "time"
89
10+ "github.com/checkmarx/ast-cli/internal/logger"
911 commonParams "github.com/checkmarx/ast-cli/internal/params"
1012 "github.com/pkg/errors"
1113 "github.com/spf13/viper"
1214)
1315
1416const (
15- failedToParseGetAll = "Failed to parse list response"
16- failedToParseTags = "Failed to parse tags response"
17- failedToParseBranches = "Failed to parse branches response"
17+ failedToParseGetAll = "Failed to parse list response"
18+ failedToParseTags = "Failed to parse tags response"
19+ failedToParseBranches = "Failed to parse branches response"
20+ queueCapacityErrorCode = 142
1821)
1922
2023type ScansHTTPWrapper struct {
@@ -29,26 +32,61 @@ func NewHTTPScansWrapper(path string) ScansWrapper {
2932 }
3033}
3134
35+ // isQueueCapacityError checks if the error is due to queue capacity limits (error code 142)
36+ func isQueueCapacityError (errorModel * ErrorModel ) bool {
37+ return errorModel != nil && errorModel .Code == queueCapacityErrorCode
38+ }
39+
3240func (s * ScansHTTPWrapper ) Create (model * Scan ) (* ScanResponseModel , * ErrorModel , error ) {
3341 clientTimeout := viper .GetUint (commonParams .ClientTimeoutKey )
3442 jsonBytes , err := json .Marshal (model )
3543 if err != nil {
3644 return nil , nil , err
3745 }
3846
39- fn := func () (* http.Response , error ) {
40- return SendHTTPRequest (http .MethodPost , s .path , bytes .NewBuffer (jsonBytes ), true , clientTimeout )
41- }
42- resp , err := retryHTTPRequest (fn , retryAttempts , retryDelay )
43- if err != nil {
44- return nil , nil , err
45- }
46- defer func () {
47- if err == nil {
48- _ = resp .Body .Close ()
47+ // Get scan enqueue retry configuration
48+ scanEnqueueRetries := viper .GetInt (commonParams .ScanEnqueueRetriesKey )
49+ scanEnqueueRetryDelay := viper .GetInt (commonParams .ScanEnqueueRetryDelayKey )
50+
51+ var scanResp * ScanResponseModel
52+ var errorModel * ErrorModel
53+
54+ // Retry loop for scan creation (queue capacity errors)
55+ for attempt := 0 ; attempt <= scanEnqueueRetries ; attempt ++ {
56+ // Standard HTTP retry (for 502, 401)
57+ fn := func () (* http.Response , error ) {
58+ return SendHTTPRequest (http .MethodPost , s .path , bytes .NewBuffer (jsonBytes ), true , clientTimeout )
4959 }
50- }()
51- return handleScanResponseWithBody (resp , err , http .StatusCreated )
60+ resp , err := retryHTTPRequest (fn , retryAttempts , retryDelay )
61+ if err != nil {
62+ return nil , nil , err
63+ }
64+
65+ // Parse response
66+ scanResp , errorModel , err = handleScanResponseWithBody (resp , err , http .StatusCreated )
67+
68+ // Close response body
69+ _ = resp .Body .Close ()
70+
71+ // Check if it's a queue capacity error and we have retries left
72+ if isQueueCapacityError (errorModel ) && attempt < scanEnqueueRetries {
73+ // Calculate exponential backoff delay
74+ waitDuration := time .Duration (scanEnqueueRetryDelay ) * time .Second * (1 << attempt )
75+ logger .PrintIfVerbose (fmt .Sprintf (
76+ "Scan creation failed due to queue capacity (attempt %d/%d). Waiting %v before retry..." ,
77+ attempt + 1 ,
78+ scanEnqueueRetries ,
79+ waitDuration ,
80+ ))
81+ time .Sleep (waitDuration )
82+ continue
83+ }
84+
85+ // Success or non-retryable error - break out of loop
86+ break
87+ }
88+
89+ return scanResp , errorModel , err
5290}
5391
5492func (s * ScansHTTPWrapper ) Get (params map [string ]string ) (* ScansCollectionResponseModel , * ErrorModel , error ) {
0 commit comments