From 9a3f8c93dcc12670c51309767cf0d370618cd6f5 Mon Sep 17 00:00:00 2001 From: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:38:09 +0100 Subject: [PATCH] AzureMonitor and GCM: Replace deprecated experimental/errorsource (#97360) * Update cloud monitoring * Update azure monitor * Update test --- .../azure-log-analytics-datasource.go | 21 ++++--- pkg/tsdb/azuremonitor/loganalytics/utils.go | 10 ++-- .../metrics/azuremonitor-datasource.go | 12 ++-- pkg/tsdb/azuremonitor/utils/utils.go | 9 +++ pkg/tsdb/azuremonitor/utils/utils_test.go | 59 +++++++++++++++++++ pkg/tsdb/cloud-monitoring/annotation_query.go | 9 ++- pkg/tsdb/cloud-monitoring/cloudmonitoring.go | 19 +++--- pkg/tsdb/cloud-monitoring/promql_query.go | 3 +- pkg/tsdb/cloud-monitoring/utils.go | 3 +- 9 files changed, 106 insertions(+), 39 deletions(-) create mode 100644 pkg/tsdb/azuremonitor/utils/utils_test.go diff --git a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go index 361ed9279be..dee69d9fe5c 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go @@ -18,7 +18,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/tracing" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -142,12 +141,12 @@ func (e *AzureLogAnalyticsDatasource) ExecuteTimeSeriesQuery(ctx context.Context for _, query := range originalQueries { logsQuery, err := e.buildQuery(ctx, query, dsInfo, fromAlert) if err != nil { - errorsource.AddErrorToResponse(query.RefID, result, err) + result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(err) continue } res, err := e.executeQuery(ctx, logsQuery, dsInfo, client, url) if err != nil { - errorsource.AddErrorToResponse(query.RefID, result, err) + result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(err) continue } result.Responses[query.RefID] = *res @@ -252,7 +251,7 @@ func (e *AzureLogAnalyticsDatasource) buildQuery(ctx context.Context, query back cfg := backend.GrafanaConfigFromContext(ctx) hasPromExemplarsToggle := cfg.FeatureToggles().IsEnabled("azureMonitorPrometheusExemplars") if !hasPromExemplarsToggle { - return nil, errorsource.DownstreamError(fmt.Errorf("query type unsupported as azureMonitorPrometheusExemplars feature toggle is not enabled"), false) + return nil, backend.DownstreamError(fmt.Errorf("query type unsupported as azureMonitorPrometheusExemplars feature toggle is not enabled")) } } azureAppInsightsQuery, err := buildAppInsightsQuery(ctx, query, dsInfo, appInsightsRegExp, e.Logger) @@ -269,7 +268,7 @@ func (e *AzureLogAnalyticsDatasource) buildQuery(ctx context.Context, query back func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *AzureLogAnalyticsQuery, dsInfo types.DatasourceInfo, client *http.Client, url string) (*backend.DataResponse, error) { // If azureLogAnalyticsSameAs is defined and set to false, return an error if sameAs, ok := dsInfo.JSONData["azureLogAnalyticsSameAs"]; ok && !sameAs.(bool) { - return nil, errorsource.DownstreamError(fmt.Errorf("credentials for Log Analytics are no longer supported. Go to the data source configuration to update Azure Monitor credentials"), false) + return nil, backend.DownstreamError(fmt.Errorf("credentials for Log Analytics are no longer supported. Go to the data source configuration to update Azure Monitor credentials")) } queryJSONModel := dataquery.AzureMonitorQuery{} @@ -280,7 +279,7 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A if query.QueryType == dataquery.AzureQueryTypeAzureTraces { if query.ResultFormat == dataquery.ResultFormatTrace && query.Query == "" { - return nil, errorsource.DownstreamError(fmt.Errorf("cannot visualise trace events using the trace visualiser"), false) + return nil, backend.DownstreamError(fmt.Errorf("cannot visualise trace events using the trace visualiser")) } } @@ -301,7 +300,7 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A res, err := client.Do(req) if err != nil { - return nil, errorsource.DownstreamError(err, false) + return nil, backend.DownstreamError(err) } defer func() { @@ -597,11 +596,11 @@ func getCorrelationWorkspaces(ctx context.Context, baseResource string, resource res, err := azMonService.HTTPClient.Do(req) if err != nil { - return AzureCorrelationAPIResponse{}, errorsource.DownstreamError(err, false) + return AzureCorrelationAPIResponse{}, backend.DownstreamError(err) } body, err := io.ReadAll(res.Body) if err != nil { - return AzureCorrelationAPIResponse{}, errorsource.DownstreamError(err, false) + return AzureCorrelationAPIResponse{}, backend.DownstreamError(err) } defer func() { @@ -611,7 +610,7 @@ func getCorrelationWorkspaces(ctx context.Context, baseResource string, resource }() if res.StatusCode/100 != 2 { - return AzureCorrelationAPIResponse{}, errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("request failed, status: %s, body: %s", res.Status, string(body)), false) + return AzureCorrelationAPIResponse{}, utils.CreateResponseErrorFromStatusCode(res.StatusCode, res.Status, body) } var data AzureCorrelationAPIResponse d := json.NewDecoder(bytes.NewReader(body)) @@ -675,7 +674,7 @@ func (e *AzureLogAnalyticsDatasource) unmarshalResponse(res *http.Response) (Azu }() if res.StatusCode/100 != 2 { - return AzureLogAnalyticsResponse{}, errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("request failed, status: %s, body: %s", res.Status, string(body)), false) + return AzureLogAnalyticsResponse{}, utils.CreateResponseErrorFromStatusCode(res.StatusCode, res.Status, body) } var data AzureLogAnalyticsResponse diff --git a/pkg/tsdb/azuremonitor/loganalytics/utils.go b/pkg/tsdb/azuremonitor/loganalytics/utils.go index 820f941c16b..093d5dc41da 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/utils.go +++ b/pkg/tsdb/azuremonitor/loganalytics/utils.go @@ -7,8 +7,8 @@ import ( "strings" "time" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/kinds/dataquery" ) @@ -47,18 +47,18 @@ func AddConfigLinks(frame data.Frame, dl string, title *string) data.Frame { // 4. the ds toggle is set to true func meetsBasicLogsCriteria(resources []string, fromAlert bool, basicLogsEnabled bool) (bool, error) { if fromAlert { - return false, errorsource.DownstreamError(fmt.Errorf("basic Logs queries cannot be used for alerts"), false) + return false, backend.DownstreamError(fmt.Errorf("basic Logs queries cannot be used for alerts")) } if len(resources) != 1 { - return false, errorsource.DownstreamError(fmt.Errorf("basic logs queries cannot be run against multiple resources"), false) + return false, backend.DownstreamError(fmt.Errorf("basic logs queries cannot be run against multiple resources")) } if !strings.Contains(strings.ToLower(resources[0]), "microsoft.operationalinsights/workspaces") { - return false, errorsource.DownstreamError(fmt.Errorf("basic logs queries may only be run against Log Analytics workspaces"), false) + return false, backend.DownstreamError(fmt.Errorf("basic logs queries may only be run against Log Analytics workspaces")) } if !basicLogsEnabled { - return false, errorsource.DownstreamError(fmt.Errorf("basic Logs queries are disabled for this data source"), false) + return false, backend.DownstreamError(fmt.Errorf("basic Logs queries are disabled for this data source")) } return true, nil diff --git a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go index 8c85239f9d6..88e1c72db63 100644 --- a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go +++ b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go @@ -17,7 +17,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/tracing" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -25,6 +24,7 @@ import ( "github.com/grafana/grafana/pkg/tsdb/azuremonitor/loganalytics" azTime "github.com/grafana/grafana/pkg/tsdb/azuremonitor/time" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" + "github.com/grafana/grafana/pkg/tsdb/azuremonitor/utils" ) // AzureMonitorDatasource calls the Azure Monitor API - one of the four API's supported @@ -55,12 +55,12 @@ func (e *AzureMonitorDatasource) ExecuteTimeSeriesQuery(ctx context.Context, ori for _, query := range originalQueries { azureQuery, err := e.buildQuery(query, dsInfo) if err != nil { - errorsource.AddErrorToResponse(query.RefID, result, err) + result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(err) continue } res, err := e.executeQuery(ctx, azureQuery, dsInfo, client, url) if err != nil { - errorsource.AddErrorToResponse(query.RefID, result, err) + result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(err) continue } result.Responses[query.RefID] = *res @@ -284,7 +284,7 @@ func (e *AzureMonitorDatasource) retrieveSubscriptionDetails(cli *http.Client, c } if res.StatusCode/100 != 2 { - return "", errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("request failed, status: %s, error: %s", res.Status, string(body)), false) + return "", utils.CreateResponseErrorFromStatusCode(res.StatusCode, res.Status, body) } var data types.SubscriptionsResponse @@ -321,7 +321,7 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *types. res, err := cli.Do(req) if err != nil { - return nil, errorsource.DownstreamError(err, false) + return nil, backend.DownstreamError(err) } defer func() { @@ -366,7 +366,7 @@ func (e *AzureMonitorDatasource) unmarshalResponse(res *http.Response) (types.Az } if res.StatusCode/100 != 2 { - return types.AzureMonitorResponse{}, errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("request failed, status: %s, body: %s", res.Status, string(body)), false) + return types.AzureMonitorResponse{}, utils.CreateResponseErrorFromStatusCode(res.StatusCode, res.Status, body) } var data types.AzureMonitorResponse diff --git a/pkg/tsdb/azuremonitor/utils/utils.go b/pkg/tsdb/azuremonitor/utils/utils.go index bcf9026f533..fd36558886d 100644 --- a/pkg/tsdb/azuremonitor/utils/utils.go +++ b/pkg/tsdb/azuremonitor/utils/utils.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" @@ -78,3 +79,11 @@ func ApplySourceFromError(errorMessage error, err error) error { } return errorMessage } + +func CreateResponseErrorFromStatusCode(statusCode int, status string, body []byte) error { + statusErr := fmt.Errorf("request failed, status: %s, body: %s", status, string(body)) + if backend.ErrorSourceFromHTTPStatus(statusCode) == backend.ErrorSourceDownstream { + return backend.DownstreamError(statusErr) + } + return backend.PluginError(statusErr) +} diff --git a/pkg/tsdb/azuremonitor/utils/utils_test.go b/pkg/tsdb/azuremonitor/utils/utils_test.go new file mode 100644 index 00000000000..0d34a56cf9f --- /dev/null +++ b/pkg/tsdb/azuremonitor/utils/utils_test.go @@ -0,0 +1,59 @@ +package utils + +import ( + "errors" + "testing" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/stretchr/testify/assert" +) + +func TestCreateResponseErrorFromStatusCode(t *testing.T) { + tests := []struct { + name string + statusCode int + status string + body []byte + expectedErrMessage string + expectedType backend.ErrorSource + }{ + { + name: "Downstream error for 500 status", + statusCode: 500, + status: "500 Internal Server Error", + body: []byte("body bytes"), + expectedErrMessage: "request failed, status: 500 Internal Server Error, body: body bytes", + expectedType: backend.ErrorSourceDownstream, + }, + { + name: "Plugin error for 501 status", + statusCode: 501, + status: "501 Not Implemented", + body: []byte("body bytes"), + expectedErrMessage: "request failed, status: 501 Not Implemented, body: body bytes", + expectedType: backend.ErrorSourcePlugin, + }, + { + name: "Downstream error for 502 status", + statusCode: 502, + status: "502 Gateway Error", + body: []byte("body bytes"), + expectedErrMessage: "request failed, status: 502 Gateway Error, body: body bytes", + expectedType: backend.ErrorSourceDownstream, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := CreateResponseErrorFromStatusCode(tt.statusCode, tt.status, tt.body) + assert.Error(t, err) + // Check if error is of type ErrorWithSource + var errorWithSource backend.ErrorWithSource + assert.True(t, errors.As(err, &errorWithSource)) + + // Validate the source of the error + assert.Equal(t, tt.expectedType, errorWithSource.ErrorSource()) + assert.Contains(t, err.Error(), tt.expectedErrMessage) + }) + } +} diff --git a/pkg/tsdb/cloud-monitoring/annotation_query.go b/pkg/tsdb/cloud-monitoring/annotation_query.go index 5db953fef29..5905e42317e 100644 --- a/pkg/tsdb/cloud-monitoring/annotation_query.go +++ b/pkg/tsdb/cloud-monitoring/annotation_query.go @@ -10,7 +10,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" ) type annotationEvent struct { @@ -25,10 +24,10 @@ func (s *Service) executeAnnotationQuery(ctx context.Context, req *backend.Query resp := backend.NewQueryDataResponse() dr, queryRes, _, err := queries[0].run(ctx, req, s, dsInfo, logger) if dr.Error != nil { - errorsource.AddErrorToResponse(queries[0].getRefID(), resp, dr.Error) + resp.Responses[queries[0].getRefID()] = backend.ErrorResponseWithErrorSource(dr.Error) } if err != nil { - errorsource.AddErrorToResponse(queries[0].getRefID(), resp, err) + resp.Responses[queries[0].getRefID()] = backend.ErrorResponseWithErrorSource(err) return resp, err } @@ -43,7 +42,7 @@ func (s *Service) executeAnnotationQuery(ctx context.Context, req *backend.Query err = json.Unmarshal(firstQuery.JSON, &tslq) if err != nil { logger.Error("error unmarshaling query", "error", err, "statusSource", backend.ErrorSourceDownstream) - errorsource.AddErrorToResponse(firstQuery.RefID, resp, err) + resp.Responses[firstQuery.RefID] = backend.ErrorResponseWithErrorSource(err) return resp, nil } @@ -52,7 +51,7 @@ func (s *Service) executeAnnotationQuery(ctx context.Context, req *backend.Query resp.Responses[firstQuery.RefID] = *dr if err != nil { - errorsource.AddErrorToResponse(firstQuery.RefID, resp, err) + resp.Responses[firstQuery.RefID] = backend.ErrorResponseWithErrorSource(err) return resp, err } diff --git a/pkg/tsdb/cloud-monitoring/cloudmonitoring.go b/pkg/tsdb/cloud-monitoring/cloudmonitoring.go index d71f3edfa77..753bac515c0 100644 --- a/pkg/tsdb/cloud-monitoring/cloudmonitoring.go +++ b/pkg/tsdb/cloud-monitoring/cloudmonitoring.go @@ -22,7 +22,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "github.com/grafana/grafana/pkg/tsdb/cloud-monitoring/kinds/dataquery" ) @@ -363,16 +362,16 @@ func (s *Service) executeTimeSeriesQuery(ctx context.Context, req *backend.Query for _, queryExecutor := range queries { dr, queryRes, executedQueryString, err := queryExecutor.run(ctx, req, s, dsInfo, logger) if err != nil { - errorsource.AddErrorToResponse(queryExecutor.getRefID(), resp, err) + resp.Responses[queryExecutor.getRefID()] = backend.ErrorResponseWithErrorSource(err) return resp, err } err = queryExecutor.parseResponse(dr, queryRes, executedQueryString, logger) if err != nil { dr.Error = err - // // Default to a plugin error if there's no source - errWithSource := errorsource.SourceError(backend.ErrorSourcePlugin, err, false) - dr.Error = errWithSource.Unwrap() - dr.ErrorSource = errWithSource.ErrorSource() + // If the error is a downstream error, set the error source + if backend.IsDownstreamError(err) { + dr.ErrorSource = backend.ErrorSourceDownstream + } } resp.Responses[queryExecutor.getRefID()] = *dr @@ -592,7 +591,7 @@ func (s *Service) getDefaultProject(ctx context.Context, dsInfo datasourceInfo) if dsInfo.authenticationType == gceAuthentication { project, err := s.gceDefaultProjectGetter(ctx, cloudMonitorScope) if err != nil { - return project, errorsource.DownstreamError(err, false) + return project, backend.DownstreamError(err) } return project, nil } @@ -613,7 +612,11 @@ func unmarshalResponse(res *http.Response, logger log.Logger) (cloudMonitoringRe if res.StatusCode/100 != 2 { logger.Error("Request failed", "status", res.Status, "body", string(body), "statusSource", backend.ErrorSourceDownstream) - return cloudMonitoringResponse{}, errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("query failed: %s", string(body)), false) + statusErr := fmt.Errorf("query failed: %s", string(body)) + if backend.ErrorSourceFromHTTPStatus(res.StatusCode) == backend.ErrorSourceDownstream { + return cloudMonitoringResponse{}, backend.DownstreamError(statusErr) + } + return cloudMonitoringResponse{}, backend.PluginError(statusErr) } var data cloudMonitoringResponse diff --git a/pkg/tsdb/cloud-monitoring/promql_query.go b/pkg/tsdb/cloud-monitoring/promql_query.go index b6d14c7c253..63baf4584f2 100644 --- a/pkg/tsdb/cloud-monitoring/promql_query.go +++ b/pkg/tsdb/cloud-monitoring/promql_query.go @@ -13,7 +13,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "github.com/grafana/grafana/pkg/tsdb/cloud-monitoring/converter" jsoniter "github.com/json-iterator/go" ) @@ -68,7 +67,7 @@ func doRequestProm(r *http.Request, dsInfo datasourceInfo, body map[string]any) } res, err := dsInfo.services[cloudMonitor].client.Do(r) if err != nil { - return res, errorsource.DownstreamError(err, false) + return res, backend.DownstreamError(err) } return res, nil diff --git a/pkg/tsdb/cloud-monitoring/utils.go b/pkg/tsdb/cloud-monitoring/utils.go index c4e32d1e179..4499ebe5dad 100644 --- a/pkg/tsdb/cloud-monitoring/utils.go +++ b/pkg/tsdb/cloud-monitoring/utils.go @@ -18,7 +18,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/tracing" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana-plugin-sdk-go/experimental/errorsource" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) @@ -85,7 +84,7 @@ func doRequestPage(_ context.Context, r *http.Request, dsInfo datasourceInfo, pa } res, err := dsInfo.services[cloudMonitor].client.Do(r) if err != nil { - return cloudMonitoringResponse{}, errorsource.DownstreamError(err, false) + return cloudMonitoringResponse{}, backend.DownstreamError(err) } defer func() {