Skip to content

Commit 68655f0

Browse files
chore: add logging to snyk code bundles for troubleshooting (#700)
1 parent 0afb329 commit 68655f0

9 files changed

+79
-58
lines changed

application/di/test_init.go

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package di
1818

1919
import (
20+
"path/filepath"
2021
"testing"
2122

2223
"github.com/snyk/snyk-ls/domain/snyk/persistence"
@@ -53,6 +54,8 @@ func TestInit(t *testing.T) {
5354
defer initMutex.Unlock()
5455
t.Helper()
5556
c := config.CurrentConfig()
57+
// we want to isolate CLI fake installs
58+
c.CliSettings().SetPath(filepath.Join(t.TempDir(), "fake-cli"))
5659
// we don't want to open browsers when testing
5760
types.DefaultOpenBrowserFunc = func(url string) {}
5861
notifier = domainNotify.NewNotifier()

application/server/configuration_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ func Test_UpdateSettings(t *testing.T) {
187187
SendErrorReports: "true",
188188
Organization: expectedOrgId,
189189
ManageBinariesAutomatically: "false",
190-
CliPath: "C:\\Users\\CliPath\\snyk-ls.exe",
190+
CliPath: filepath.Join(t.TempDir(), "cli"),
191191
Token: "a fancy token",
192192
FilterSeverity: types.DefaultSeverityFilter(),
193193
TrustedFolders: []string{"trustedPath1", "trustedPath2"},
@@ -234,7 +234,7 @@ func Test_UpdateSettings(t *testing.T) {
234234
assert.True(t, c.IsErrorReportingEnabled())
235235
assert.Equal(t, expectedOrgId, c.Organization())
236236
assert.False(t, c.ManageBinariesAutomatically())
237-
assert.Equal(t, "C:\\Users\\CliPath\\snyk-ls.exe", c.CliSettings().Path())
237+
assert.Equal(t, settings.CliPath, c.CliSettings().Path())
238238
assert.Equal(t, types.DefaultSeverityFilter(), c.FilterSeverity())
239239
assert.Subset(t, []string{"trustedPath1", "trustedPath2"}, c.TrustedFolders())
240240
assert.Equal(t, settings.OsPlatform, c.OsPlatform())

application/server/notification_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package server
1818

1919
import (
2020
"context"
21+
"path/filepath"
2122
"reflect"
2223
"testing"
2324
"time"
@@ -174,7 +175,7 @@ func Test_IsAvailableCliNotification(t *testing.T) {
174175
if err != nil {
175176
t.Fatal(err)
176177
}
177-
var expected = types.SnykIsAvailableCli{CliPath: "path"}
178+
var expected = types.SnykIsAvailableCli{CliPath: filepath.Join(t.TempDir(), "cli")}
178179

179180
di.Notifier().Send(expected)
180181
assert.Eventually(

application/server/server_smoke_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ func Test_SmokeIssueCaching(t *testing.T) {
203203
checkDiagnosticPublishingForCachingSmokeTest(t, jsonRPCRecorder, 2, 3, c)
204204
checkScanResultsPublishingForCachingSmokeTest(t, jsonRPCRecorder, folderJuice, folderGoof, c)
205205
})
206+
206207
t.Run("clears issues from cache correctly", func(t *testing.T) {
207208
loc, jsonRPCRecorder := setupServer(t)
208209
c := testutil.SmokeTest(t, false)

application/server/server_test.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ func Test_TextDocumentCodeLenses_shouldReturnCodeLenses(t *testing.T) {
353353
Organization: "fancy org",
354354
Token: "xxx",
355355
ManageBinariesAutomatically: "true",
356-
CliPath: "",
356+
CliPath: filepath.Join(t.TempDir(), "cli"),
357357
FilterSeverity: types.DefaultSeverityFilter(),
358358
EnableTrustedFoldersFeature: "false",
359359
},
@@ -410,7 +410,7 @@ func Test_TextDocumentCodeLenses_dirtyFileShouldFilterCodeLenses(t *testing.T) {
410410
Organization: "fancy org",
411411
Token: "xxx",
412412
ManageBinariesAutomatically: "true",
413-
CliPath: "",
413+
CliPath: filepath.Join(t.TempDir(), "cli"),
414414
FilterSeverity: types.DefaultSeverityFilter(),
415415
EnableTrustedFoldersFeature: "false",
416416
},
@@ -638,6 +638,7 @@ func Test_initialize_handlesUntrustedFoldersWhenAutomaticAuthentication(t *testi
638638
loc, jsonRPCRecorder := setupServer(t)
639639
initializationOptions := types.Settings{
640640
EnableTrustedFoldersFeature: "true",
641+
CliPath: filepath.Join(t.TempDir(), "cli"),
641642
}
642643
params := types.InitializeParams{
643644
InitializationOptions: initializationOptions,
@@ -661,6 +662,7 @@ func Test_initialize_handlesUntrustedFoldersWhenAuthenticated(t *testing.T) {
661662
initializationOptions := types.Settings{
662663
EnableTrustedFoldersFeature: "true",
663664
Token: "token",
665+
CliPath: filepath.Join(t.TempDir(), "cli"),
664666
}
665667

666668
fakeAuthenticationProvider := di.AuthenticationService().Provider().(*authentication.FakeAuthenticationProvider)
@@ -687,6 +689,7 @@ func Test_initialize_doesnotHandleUntrustedFolders(t *testing.T) {
687689
loc, jsonRPCRecorder := setupServer(t)
688690
initializationOptions := types.Settings{
689691
EnableTrustedFoldersFeature: "true",
692+
CliPath: filepath.Join(t.TempDir(), "cli"),
690693
}
691694
params := types.InitializeParams{
692695
InitializationOptions: initializationOptions,

infrastructure/code/bundle.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func getIssueLangAndRuleId(issue snyk.Issue) (string, string, bool) {
9898
}
9999

100100
func (b *Bundle) retrieveAnalysis(ctx context.Context, t *progress.Tracker) ([]snyk.Issue, error) {
101-
logger := b.logger.With().Str("method", "retrieveAnalysis").Logger()
101+
logger := b.logger.With().Str("method", "retrieveAnalysis").Str("requestId", b.requestId).Logger()
102102

103103
if b.BundleHash == "" {
104104
logger.Warn().Str("rootPath", b.rootPath).Msg("bundle hash is empty")
@@ -129,7 +129,6 @@ func (b *Bundle) retrieveAnalysis(ctx context.Context, t *progress.Tracker) ([]s
129129

130130
if err != nil {
131131
logger.Error().Err(err).
132-
Str("requestId", b.requestId).
133132
Int("fileCount", len(b.UploadBatches)).
134133
Msg("error retrieving diagnostics...")
135134
b.errorReporter.CaptureError(err, codeClientObservability.ErrorReporterOptions{ErrorDiagnosticPath: b.rootPath})
@@ -138,8 +137,7 @@ func (b *Bundle) retrieveAnalysis(ctx context.Context, t *progress.Tracker) ([]s
138137
}
139138

140139
if status.message == completeStatus {
141-
logger.Trace().Str("requestId", b.requestId).
142-
Msg("sending diagnostics...")
140+
logger.Trace().Msg("sending diagnostics...")
143141
t.ReportWithMessage(90, "Analysis complete.")
144142

145143
b.issueEnhancer.addIssueActions(ctx, issues, b.BundleHash)

infrastructure/code/snyk_code_http_client.go

+61-46
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"context"
2222
"encoding/json"
2323
"errors"
24+
"fmt"
2425
"io"
2526
"math"
2627
"net/http"
@@ -106,12 +107,12 @@ func (s *SnykCodeHTTPClient) GetFilters(ctx context.Context) (
106107
span := s.instrumentor.StartSpan(ctx, method)
107108
defer s.instrumentor.Finish(span)
108109

109-
responseBody, err := s.doCall(span.Context(), "GET", "/filters", nil)
110+
body, _, err := s.doCall(span.Context(), "GET", "/filters", nil)
110111
if err != nil {
111112
return FiltersResponse{ConfigFiles: nil, Extensions: nil}, err
112113
}
113114

114-
err = json.Unmarshal(responseBody, &filters)
115+
err = json.Unmarshal(body, &filters)
115116
if err != nil {
116117
return FiltersResponse{ConfigFiles: nil, Extensions: nil}, err
117118
}
@@ -134,53 +135,50 @@ func (s *SnykCodeHTTPClient) CreateBundle(
134135
return "", nil, err
135136
}
136137

137-
responseBody, err := s.doCall(span.Context(), "POST", "/bundle", requestBody)
138+
body, _, err := s.doCall(span.Context(), "POST", "/bundle", requestBody)
138139
if err != nil {
139140
return "", nil, err
140141
}
141142

142143
var bundle bundleResponse
143-
err = json.Unmarshal(responseBody, &bundle)
144+
err = json.Unmarshal(body, &bundle)
144145
if err != nil {
145146
return "", nil, err
146147
}
147148
s.c.Logger().Debug().Str("method", method).Msg("API: Create done")
148149
return bundle.BundleHash, bundle.MissingFiles, nil
149150
}
150151

151-
func (s *SnykCodeHTTPClient) doCall(ctx context.Context,
152-
method string,
153-
path string,
154-
requestBody []byte,
155-
) (responseBody []byte, _ error) {
152+
func (s *SnykCodeHTTPClient) doCall(ctx context.Context, method string, path string, requestBody []byte) ([]byte, int, error) {
156153
span := s.instrumentor.StartSpan(ctx, "code.doCall")
157154
defer s.instrumentor.Finish(span)
158155

159156
const retryCount = 3
157+
158+
// we only retry, if we get a retryable http status code
160159
for i := 0; i < retryCount; i++ {
161160
requestId, err := performance2.GetTraceId(span.Context())
162161
if err != nil {
163-
return nil, errors.New("Code request id was not provided. " + err.Error())
162+
return nil, 0, errors.New("Code request id was not provided. " + err.Error())
164163
}
165164

166165
bodyBuffer, err := s.encodeIfNeeded(method, requestBody)
167166
if err != nil {
168-
return nil, err
167+
return nil, 0, err
169168
}
170169

171170
c := config.CurrentConfig()
172171
req, err := s.newRequest(c, method, path, bodyBuffer, requestId)
173172
if err != nil {
174-
return nil, err
173+
return nil, 0, err
175174
}
176175

177176
s.c.Logger().Trace().Str("requestBody", string(requestBody)).Str("snyk-request-id", requestId).Msg("SEND TO REMOTE")
178177

179-
response, body, err := s.httpCall(req) //nolint:bodyclose // false positive
180-
responseBody = body
178+
responseBody, httpStatusCode, err := s.httpCall(req)
181179

182-
if response != nil && responseBody != nil {
183-
s.c.Logger().Trace().Str("response.Status", response.Status).
180+
if responseBody != nil {
181+
s.c.Logger().Trace().Int("response.Status", httpStatusCode).
184182
Str("responseBody", string(responseBody)).
185183
Str("snyk-request-id", requestId).
186184
Msg("RECEIVED FROM REMOTE")
@@ -191,51 +189,58 @@ func (s *SnykCodeHTTPClient) doCall(ctx context.Context,
191189
}
192190

193191
if err != nil {
194-
return nil, err // no retries for errors
192+
return nil, 0, err // no retries for errors
195193
}
196194

197-
err = s.checkResponseCode(response)
195+
err = s.checkResponseCode(httpStatusCode)
198196
if err != nil {
199-
if retryErrorCodes[response.StatusCode] {
197+
if retryErrorCodes[httpStatusCode] {
200198
s.c.Logger().Debug().Err(err).Str("method", method).Int("attempts done", i+1).Msgf("retrying")
201199
if i < retryCount-1 {
202200
time.Sleep(5 * time.Second)
203201
continue
204202
}
205203
// return the error on last try
206-
return nil, err
204+
return nil, httpStatusCode, err
207205
}
208-
return nil, err
206+
return nil, httpStatusCode, err
209207
}
210208
// no error, we can break the retry loop
211-
break
209+
return responseBody, httpStatusCode, nil
212210
}
213-
return responseBody, nil
211+
return nil, 0, nil
214212
}
215213

216-
func (s *SnykCodeHTTPClient) httpCall(req *http.Request) (*http.Response, []byte, error) {
214+
func (s *SnykCodeHTTPClient) httpCall(req *http.Request) ([]byte, int, error) {
217215
method := "code.httpCall"
216+
logger := s.c.Logger().With().Str("method", method).Logger()
217+
statusCode := 0
218+
218219
response, err := s.client().Do(req)
219220
if err != nil {
220-
s.c.Logger().Err(err).Str("method", method).Msgf("got http error")
221-
s.errorReporter.CaptureError(err, codeClientObservability.ErrorReporterOptions{ErrorDiagnosticPath: req.RequestURI})
222-
return nil, nil, err
221+
logger.Err(err).Msgf("got http error")
222+
return nil, statusCode, err
223+
}
224+
225+
if response == nil {
226+
return nil, 0, nil
223227
}
224228

225229
defer func() {
226-
closeErr := response.Body.Close()
227-
if closeErr != nil {
228-
s.c.Logger().Err(closeErr).Msg("Couldn't close response body in call to Snyk Code")
230+
bodyCloseErr := response.Body.Close()
231+
if bodyCloseErr != nil {
232+
logger.Err(bodyCloseErr).Msg("failed to close response body")
229233
}
230234
}()
231-
responseBody, err := io.ReadAll(response.Body)
232235

233-
if err != nil {
234-
s.c.Logger().Err(err).Str("method", method).Msgf("error reading response body")
235-
s.errorReporter.CaptureError(err, codeClientObservability.ErrorReporterOptions{ErrorDiagnosticPath: req.RequestURI})
236-
return nil, nil, err
236+
statusCode = response.StatusCode
237+
responseBody, readErr := io.ReadAll(response.Body)
238+
239+
if readErr != nil {
240+
logger.Err(readErr).Msg("failed to read response body")
241+
return responseBody, statusCode, err
237242
}
238-
return response, responseBody, nil
243+
return responseBody, statusCode, nil
239244
}
240245

241246
func (s *SnykCodeHTTPClient) newRequest(
@@ -313,11 +318,16 @@ func (s *SnykCodeHTTPClient) ExtendBundle(
313318
removedFiles []string,
314319
) (string, []string, error) {
315320
method := "code.ExtendBundle"
316-
s.c.Logger().Debug().Str("method", method).Msg("API: Extending bundle for " + strconv.Itoa(len(files)) + " files")
317-
defer s.c.Logger().Debug().Str("method", method).Msg("API: Extend done")
318-
319321
span := s.instrumentor.StartSpan(ctx, method)
320322
defer s.instrumentor.Finish(span)
323+
requestId, err := performance2.GetTraceId(span.Context())
324+
logger := s.c.Logger().With().Str("method", method).Str("requestId", requestId).Logger()
325+
if err != nil {
326+
logger.Err(err).Msg("failed to get request id")
327+
}
328+
329+
logger.Debug().Msg("API: Extending bundle for " + strconv.Itoa(len(files)) + " files")
330+
defer logger.Debug().Msg("API: Extend done")
321331

322332
requestBody, err := json.Marshal(extendBundleRequest{
323333
Files: files,
@@ -327,7 +337,12 @@ func (s *SnykCodeHTTPClient) ExtendBundle(
327337
return "", nil, err
328338
}
329339

330-
responseBody, err := s.doCall(span.Context(), "PUT", "/bundle/"+bundleHash, requestBody)
340+
responseBody, httpStatus, err := s.doCall(span.Context(), "PUT", "/bundle/"+bundleHash, requestBody)
341+
if httpStatus == http.StatusBadRequest {
342+
logger.Err(err).Msg("got an HTTP 400 Bad Request, dumping bundle infos for analysis")
343+
logger.Error().Any("fileHashes", files).Send()
344+
logger.Error().Any("removedFiles", removedFiles).Send()
345+
}
331346
if err != nil {
332347
return "", nil, err
333348
}
@@ -364,7 +379,7 @@ func (s *SnykCodeHTTPClient) RunAnalysis(
364379
return nil, AnalysisStatus{}, err
365380
}
366381

367-
responseBody, err := s.doCall(span.Context(), "POST", "/analysis", requestBody)
382+
responseBody, _, err := s.doCall(span.Context(), "POST", "/analysis", requestBody)
368383
failed := AnalysisStatus{message: "FAILED"}
369384
if err != nil {
370385
s.c.Logger().Err(err).Str("method", method).Str("responseBody", string(responseBody)).Msg("error response from analysis")
@@ -434,11 +449,11 @@ func (s *SnykCodeHTTPClient) analysisRequestBody(options *AnalysisOptions) ([]by
434449
return requestBody, err
435450
}
436451

437-
func (s *SnykCodeHTTPClient) checkResponseCode(r *http.Response) error {
438-
if r.StatusCode >= 200 && r.StatusCode <= 299 {
452+
func (s *SnykCodeHTTPClient) checkResponseCode(statusCode int) error {
453+
if statusCode >= 200 && statusCode <= 400 {
439454
return nil
440455
}
441-
return errors.New("Unexpected response code: " + r.Status)
456+
return fmt.Errorf("Unexpected response code: %d", statusCode)
442457
}
443458

444459
type AutofixStatus struct {
@@ -508,7 +523,7 @@ func (s *SnykCodeHTTPClient) RunAutofix(ctx context.Context, options AutofixOpti
508523
return AutofixResponse{}, err
509524
}
510525

511-
responseBody, err := s.doCall(span.Context(), "POST", "/autofix/suggestions", requestBody)
526+
responseBody, _, err := s.doCall(span.Context(), "POST", "/autofix/suggestions", requestBody)
512527

513528
if err != nil {
514529
logger.Err(err).Str("responseBody", string(responseBody)).Msg("error response from autofix")
@@ -578,7 +593,7 @@ func (s *SnykCodeHTTPClient) SubmitAutofixFeedback(ctx context.Context, fixId st
578593
return err
579594
}
580595

581-
responseBody, err := s.doCall(span.Context(), "POST", "/autofix/event", requestBody)
596+
responseBody, _, err := s.doCall(span.Context(), "POST", "/autofix/event", requestBody)
582597
if err != nil {
583598
s.c.Logger().Err(err).Str("method", method).Str("responseBody", string(responseBody)).Msg("error response for autofix feedback")
584599
return err

infrastructure/code/snyk_code_http_client_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func TestSnykCodeBackendService_doCall_shouldRetry(t *testing.T) {
127127
}
128128
}
129129
s := NewSnykCodeHTTPClient(c, NewCodeInstrumentor(), newTestCodeErrorReporter(), dummyClientFunc)
130-
_, err := s.doCall(context.Background(), "GET", "https://httpstat.us/500", nil)
130+
_, _, err := s.doCall(context.Background(), "GET", "https://httpstat.us/500", nil)
131131
assert.Error(t, err)
132132
assert.Equal(t, 3, d.calls)
133133
}
@@ -139,7 +139,7 @@ func TestSnykCodeBackendService_doCall_rejected(t *testing.T) {
139139
}
140140

141141
s := NewSnykCodeHTTPClient(c, NewCodeInstrumentor(), newTestCodeErrorReporter(), dummyClientFunc)
142-
_, err := s.doCall(context.Background(), "GET", "https://127.0.0.1", nil)
142+
_, _, err := s.doCall(context.Background(), "GET", "https://127.0.0.1", nil)
143143
assert.Error(t, err)
144144
}
145145

infrastructure/snyk_api/snyk_api.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func (s *SnykApiClientImpl) getApiResponse(caller string, path string, v interfa
217217
}
218218

219219
func checkResponseCode(r *http.Response) *SnykApiError {
220-
if r.StatusCode >= 200 && r.StatusCode <= 399 {
220+
if r.StatusCode >= 200 && r.StatusCode <= 299 {
221221
return nil
222222
}
223223

0 commit comments

Comments
 (0)