mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
GCM: Improve error handling (#93474)
* Improve error handling * More manual errorsource * Revert some changes from original PR * Update test
This commit is contained in:
parent
19c7e1f376
commit
e18d029a24
@ -23,7 +23,10 @@ type annotationEvent struct {
|
|||||||
func (s *Service) executeAnnotationQuery(ctx context.Context, req *backend.QueryDataRequest, dsInfo datasourceInfo, queries []cloudMonitoringQueryExecutor, logger log.Logger) (
|
func (s *Service) executeAnnotationQuery(ctx context.Context, req *backend.QueryDataRequest, dsInfo datasourceInfo, queries []cloudMonitoringQueryExecutor, logger log.Logger) (
|
||||||
*backend.QueryDataResponse, error) {
|
*backend.QueryDataResponse, error) {
|
||||||
resp := backend.NewQueryDataResponse()
|
resp := backend.NewQueryDataResponse()
|
||||||
queryRes, dr, _, err := queries[0].run(ctx, req, s, dsInfo, logger)
|
dr, queryRes, _, err := queries[0].run(ctx, req, s, dsInfo, logger)
|
||||||
|
if dr.Error != nil {
|
||||||
|
errorsource.AddErrorToResponse(queries[0].getRefID(), resp, dr.Error)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorsource.AddErrorToResponse(queries[0].getRefID(), resp, err)
|
errorsource.AddErrorToResponse(queries[0].getRefID(), resp, err)
|
||||||
return resp, err
|
return resp, err
|
||||||
@ -39,10 +42,14 @@ func (s *Service) executeAnnotationQuery(ctx context.Context, req *backend.Query
|
|||||||
firstQuery := req.Queries[0]
|
firstQuery := req.Queries[0]
|
||||||
err = json.Unmarshal(firstQuery.JSON, &tslq)
|
err = json.Unmarshal(firstQuery.JSON, &tslq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logger.Error("error unmarshaling query", "error", err, "statusSource", backend.ErrorSourceDownstream)
|
||||||
|
errorsource.AddErrorToResponse(firstQuery.RefID, resp, err)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
err = parseToAnnotations(req.Queries[0].RefID, queryRes, dr.(cloudMonitoringResponse), tslq.TimeSeriesList.Title, tslq.TimeSeriesList.Text)
|
|
||||||
resp.Responses[firstQuery.RefID] = *queryRes
|
// parseToAnnotations never actually returns an error
|
||||||
|
err = parseToAnnotations(req.Queries[0].RefID, dr, queryRes.(cloudMonitoringResponse), tslq.TimeSeriesList.Title, tslq.TimeSeriesList.Text)
|
||||||
|
resp.Responses[firstQuery.RefID] = *dr
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorsource.AddErrorToResponse(firstQuery.RefID, resp, err)
|
errorsource.AddErrorToResponse(firstQuery.RefID, resp, err)
|
||||||
|
@ -361,20 +361,21 @@ func (s *Service) executeTimeSeriesQuery(ctx context.Context, req *backend.Query
|
|||||||
*backend.QueryDataResponse, error) {
|
*backend.QueryDataResponse, error) {
|
||||||
resp := backend.NewQueryDataResponse()
|
resp := backend.NewQueryDataResponse()
|
||||||
for _, queryExecutor := range queries {
|
for _, queryExecutor := range queries {
|
||||||
queryRes, dr, executedQueryString, err := queryExecutor.run(ctx, req, s, dsInfo, logger)
|
dr, queryRes, executedQueryString, err := queryExecutor.run(ctx, req, s, dsInfo, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorsource.AddErrorToResponse(queryExecutor.getRefID(), resp, err)
|
errorsource.AddErrorToResponse(queryExecutor.getRefID(), resp, err)
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
err = queryExecutor.parseResponse(queryRes, dr, executedQueryString, logger)
|
err = queryExecutor.parseResponse(dr, queryRes, executedQueryString, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Default to a plugin error if there's no source
|
dr.Error = err
|
||||||
|
// // Default to a plugin error if there's no source
|
||||||
errWithSource := errorsource.SourceError(backend.ErrorSourcePlugin, err, false)
|
errWithSource := errorsource.SourceError(backend.ErrorSourcePlugin, err, false)
|
||||||
queryRes.Error = errWithSource.Unwrap()
|
dr.Error = errWithSource.Unwrap()
|
||||||
queryRes.ErrorSource = errWithSource.ErrorSource()
|
dr.ErrorSource = errWithSource.ErrorSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.Responses[queryExecutor.getRefID()] = *queryRes
|
resp.Responses[queryExecutor.getRefID()] = *dr
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -608,21 +609,21 @@ func unmarshalResponse(res *http.Response, logger log.Logger) (cloudMonitoringRe
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
if res.StatusCode/100 != 2 {
|
if res.StatusCode/100 != 2 {
|
||||||
logger.Error("Request failed", "status", res.Status, "body", string(body))
|
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)
|
return cloudMonitoringResponse{}, errorsource.SourceError(backend.ErrorSourceFromHTTPStatus(res.StatusCode), fmt.Errorf("query failed: %s", string(body)), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
var data cloudMonitoringResponse
|
var data cloudMonitoringResponse
|
||||||
err = json.Unmarshal(body, &data)
|
err = json.Unmarshal(body, &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to unmarshal CloudMonitoring response", "error", err, "status", res.Status, "body", string(body))
|
logger.Error("Failed to unmarshal CloudMonitoring response", "error", err, "status", res.Status, "body", string(body), "statusSource", backend.ErrorSourceDownstream)
|
||||||
return cloudMonitoringResponse{}, fmt.Errorf("failed to unmarshal query response: %w", err)
|
return cloudMonitoringResponse{}, fmt.Errorf("failed to unmarshal query response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addConfigData(frames data.Frames, dl string, unit string, period *string) data.Frames {
|
func addConfigData(frames data.Frames, dl string, unit string, period *string, logger log.Logger) data.Frames {
|
||||||
for i := range frames {
|
for i := range frames {
|
||||||
if frames[i].Fields[1].Config == nil {
|
if frames[i].Fields[1].Config == nil {
|
||||||
frames[i].Fields[1].Config = &data.FieldConfig{}
|
frames[i].Fields[1].Config = &data.FieldConfig{}
|
||||||
@ -646,7 +647,7 @@ func addConfigData(frames data.Frames, dl string, unit string, period *string) d
|
|||||||
if period != nil && *period != "" {
|
if period != nil && *period != "" {
|
||||||
err := addInterval(*period, frames[i].Fields[0])
|
err := addInterval(*period, frames[i].Fields[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
backend.Logger.Error("Failed to add interval", "error", err)
|
logger.Error("Failed to add interval: %s", err, "statusSource", backend.ErrorSourceDownstream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ func (promQLQ *cloudMonitoringProm) run(ctx context.Context, req *backend.QueryD
|
|||||||
dr := &backend.DataResponse{}
|
dr := &backend.DataResponse{}
|
||||||
projectName, err := s.ensureProject(ctx, dsInfo, promQLQ.parameters.ProjectName)
|
projectName, err := s.ensureProject(ctx, dsInfo, promQLQ.parameters.ProjectName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dr, backend.DataResponse{}, "", err
|
dr.Error = err
|
||||||
|
return dr, backend.DataResponse{}, "", nil
|
||||||
}
|
}
|
||||||
r, err := createRequest(ctx, &dsInfo, path.Join("/v1/projects", projectName, "location/global/prometheus/api/v1/query_range"), nil)
|
r, err := createRequest(ctx, &dsInfo, path.Join("/v1/projects", projectName, "location/global/prometheus/api/v1/query_range"), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -43,12 +44,13 @@ func (promQLQ *cloudMonitoringProm) run(ctx context.Context, req *backend.QueryD
|
|||||||
|
|
||||||
res, err := doRequestProm(r, dsInfo, requestBody)
|
res, err := doRequestProm(r, dsInfo, requestBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dr, backend.DataResponse{}, "", err
|
dr.Error = err
|
||||||
|
return dr, backend.DataResponse{}, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := res.Body.Close(); err != nil {
|
if err := res.Body.Close(); err != nil {
|
||||||
s.logger.Error("Failed to close response body", "err", err)
|
s.logger.Error("Failed to close response body", "err", err, "statusSource", backend.ErrorSourceDownstream)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -59,8 +59,10 @@ func TestPromqlQuery(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dr, parsedProm, _, err := query.run(context.Background(), &backend.QueryDataRequest{}, service, dsInfo, service.logger)
|
dr, parsedProm, _, err := query.run(context.Background(), &backend.QueryDataRequest{}, service, dsInfo, service.logger)
|
||||||
require.Error(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "not found!", err.Error())
|
require.Error(t, dr.Error)
|
||||||
|
require.Equal(t, "not found!", dr.Error.Error())
|
||||||
|
require.True(t, backend.IsDownstreamError(dr.Error))
|
||||||
|
|
||||||
err = query.parseResponse(dr, parsedProm, "", service.logger)
|
err = query.parseResponse(dr, parsedProm, "", service.logger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -386,7 +386,7 @@ func writeResponseBytes(rw http.ResponseWriter, code int, msg []byte) {
|
|||||||
rw.WriteHeader(code)
|
rw.WriteHeader(code)
|
||||||
_, err := rw.Write(msg)
|
_, err := rw.Write(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
backend.Logger.Error("Unable to write HTTP response", "error", err)
|
backend.Logger.Error("Unable to write HTTP response", "error", err, "statusSource", backend.ErrorSourceDownstream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesList) run(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseTimeSeriesResponse(queryRes *backend.DataResponse,
|
func parseTimeSeriesResponse(queryRes *backend.DataResponse,
|
||||||
response cloudMonitoringResponse, executedQueryString string, query cloudMonitoringQueryExecutor, params url.Values, groupBys []string, _ log.Logger) error {
|
response cloudMonitoringResponse, executedQueryString string, query cloudMonitoringQueryExecutor, params url.Values, groupBys []string, logger log.Logger) error {
|
||||||
frames := data.Frames{}
|
frames := data.Frames{}
|
||||||
|
|
||||||
for _, series := range response.TimeSeries {
|
for _, series := range response.TimeSeries {
|
||||||
@ -47,7 +47,7 @@ func parseTimeSeriesResponse(queryRes *backend.DataResponse,
|
|||||||
if len(response.TimeSeries) > 0 {
|
if len(response.TimeSeries) > 0 {
|
||||||
dl := query.buildDeepLink()
|
dl := query.buildDeepLink()
|
||||||
aggregationAlignmentString := params.Get("aggregation.alignmentPeriod")
|
aggregationAlignmentString := params.Get("aggregation.alignmentPeriod")
|
||||||
frames = addConfigData(frames, dl, response.Unit, &aggregationAlignmentString)
|
frames = addConfigData(frames, dl, response.Unit, &aggregationAlignmentString, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
queryRes.Frames = frames
|
queryRes.Frames = frames
|
||||||
@ -93,6 +93,7 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesList) buildDeepLink() string {
|
|||||||
"Failed to generate deep link: unable to parse metrics explorer URL",
|
"Failed to generate deep link: unable to parse metrics explorer URL",
|
||||||
"ProjectName", timeSeriesFilter.parameters.ProjectName,
|
"ProjectName", timeSeriesFilter.parameters.ProjectName,
|
||||||
"error", err,
|
"error", err,
|
||||||
|
"statusSource", backend.ErrorSourcePlugin,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *b
|
|||||||
}
|
}
|
||||||
if len(response.TimeSeriesData) > 0 {
|
if len(response.TimeSeriesData) > 0 {
|
||||||
dl := timeSeriesQuery.buildDeepLink()
|
dl := timeSeriesQuery.buildDeepLink()
|
||||||
frames = addConfigData(frames, dl, response.Unit, timeSeriesQuery.parameters.GraphPeriod)
|
frames = addConfigData(frames, dl, response.Unit, timeSeriesQuery.parameters.GraphPeriod, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
queryRes.Frames = frames
|
queryRes.Frames = frames
|
||||||
@ -106,6 +106,7 @@ func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) buildDeepLink() string {
|
|||||||
"Failed to generate deep link: unable to parse metrics explorer URL",
|
"Failed to generate deep link: unable to parse metrics explorer URL",
|
||||||
"ProjectName", timeSeriesQuery.parameters.Query,
|
"ProjectName", timeSeriesQuery.parameters.Query,
|
||||||
"error", err,
|
"error", err,
|
||||||
|
"statusSource", backend.ErrorSourcePlugin,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ func createRequest(ctx context.Context, dsInfo *datasourceInfo, proxyPass string
|
|||||||
}
|
}
|
||||||
req, err := http.NewRequestWithContext(ctx, method, dsInfo.services[cloudMonitor].url, body)
|
req, err := http.NewRequestWithContext(ctx, method, dsInfo.services[cloudMonitor].url, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
backend.Logger.Error("Failed to create request", "error", err)
|
backend.Logger.Error("Failed to create request", "error", err, "statusSource", backend.ErrorSourceDownstream)
|
||||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,8 @@ func runTimeSeriesRequest(ctx context.Context, req *backend.QueryDataRequest,
|
|||||||
dr := &backend.DataResponse{}
|
dr := &backend.DataResponse{}
|
||||||
projectName, err := s.ensureProject(ctx, dsInfo, projectName)
|
projectName, err := s.ensureProject(ctx, dsInfo, projectName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dr, cloudMonitoringResponse{}, "", err
|
dr.Error = err
|
||||||
|
return dr, cloudMonitoringResponse{}, "", nil
|
||||||
}
|
}
|
||||||
timeSeriesMethod := "timeSeries"
|
timeSeriesMethod := "timeSeries"
|
||||||
if body != nil {
|
if body != nil {
|
||||||
@ -150,7 +151,8 @@ func runTimeSeriesRequest(ctx context.Context, req *backend.QueryDataRequest,
|
|||||||
}
|
}
|
||||||
r, err := createRequest(ctx, &dsInfo, path.Join("/v3/projects", projectName, timeSeriesMethod), nil)
|
r, err := createRequest(ctx, &dsInfo, path.Join("/v3/projects", projectName, timeSeriesMethod), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dr, cloudMonitoringResponse{}, "", err
|
dr.Error = err
|
||||||
|
return dr, cloudMonitoringResponse{}, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
span := traceReq(ctx, req, dsInfo, r, params.Encode())
|
span := traceReq(ctx, req, dsInfo, r, params.Encode())
|
||||||
@ -158,7 +160,8 @@ func runTimeSeriesRequest(ctx context.Context, req *backend.QueryDataRequest,
|
|||||||
|
|
||||||
d, err := doRequestWithPagination(ctx, r, dsInfo, params, body, logger)
|
d, err := doRequestWithPagination(ctx, r, dsInfo, params, body, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dr, cloudMonitoringResponse{}, "", err
|
dr.Error = err
|
||||||
|
return dr, cloudMonitoringResponse{}, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr, d, r.URL.RawQuery, nil
|
return dr, d, r.URL.RawQuery, nil
|
||||||
|
Loading…
Reference in New Issue
Block a user