diff --git a/docs/sources/datasources/aws-cloudwatch/_index.md b/docs/sources/datasources/aws-cloudwatch/_index.md index 30ffc617c10..5729ce54e01 100644 --- a/docs/sources/datasources/aws-cloudwatch/_index.md +++ b/docs/sources/datasources/aws-cloudwatch/_index.md @@ -123,6 +123,12 @@ You can attach these permissions to the IAM role or IAM user you configured in [ "Effect": "Allow", "Action": "tag:GetResources", "Resource": "*" + }, + { + "Sid": "AllowReadingResourceMetricsFromPerformanceInsights", + "Effect": "Allow", + "Action": "pi:GetResourceMetrics", + "Resource": "*" } ] } @@ -182,6 +188,12 @@ You can attach these permissions to the IAM role or IAM user you configured in [ ], "Resource": "*" }, + { + "Sid": "AllowReadingResourceMetricsFromPerformanceInsights", + "Effect": "Allow", + "Action": "pi:GetResourceMetrics", + "Resource": "*" + }, { "Sid": "AllowReadingLogsFromCloudWatch", "Effect": "Allow", diff --git a/pkg/tsdb/cloudwatch/models/query_row_response.go b/pkg/tsdb/cloudwatch/models/query_row_response.go index fa20f5c9a0e..b99913b0a91 100644 --- a/pkg/tsdb/cloudwatch/models/query_row_response.go +++ b/pkg/tsdb/cloudwatch/models/query_row_response.go @@ -1,6 +1,8 @@ package models -import "github.com/aws/aws-sdk-go/service/cloudwatch" +import ( + "github.com/aws/aws-sdk-go/service/cloudwatch" +) // queryRowResponse represents the GetMetricData response for a query row in the query editor. type QueryRowResponse struct { @@ -8,6 +10,8 @@ type QueryRowResponse struct { ErrorCodes map[string]bool HasArithmeticError bool ArithmeticErrorMessage string + HasPermissionError bool + PermissionErrorMessage string Metrics []*cloudwatch.MetricDataResult StatusCode string } @@ -23,6 +27,10 @@ func NewQueryRowResponse(errors map[string]bool) QueryRowResponse { } func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatch.MetricDataResult) { + if mdr.Label == nil { + return + } + if partialData, ok := q.partialDataSet[*mdr.Label]; ok { partialData.Timestamps = append(partialData.Timestamps, mdr.Timestamps...) partialData.Values = append(partialData.Values, mdr.Values...) @@ -44,3 +52,8 @@ func (q *QueryRowResponse) AddArithmeticError(message *string) { q.HasArithmeticError = true q.ArithmeticErrorMessage = *message } + +func (q *QueryRowResponse) AddPermissionError(message *string) { + q.HasPermissionError = true + q.PermissionErrorMessage = *message +} diff --git a/pkg/tsdb/cloudwatch/response_parser.go b/pkg/tsdb/cloudwatch/response_parser.go index 2feaf906c2c..b9248fbcd29 100644 --- a/pkg/tsdb/cloudwatch/response_parser.go +++ b/pkg/tsdb/cloudwatch/response_parser.go @@ -35,6 +35,10 @@ func (e *cloudWatchExecutor) parseResponse(ctx context.Context, startTime time.T dataRes.Error = fmt.Errorf("ArithmeticError in query %q: %s", queryRow.RefId, response.ArithmeticErrorMessage) } + if response.HasPermissionError { + dataRes.Error = fmt.Errorf("PermissionError in query %q: %s", queryRow.RefId, response.PermissionErrorMessage) + } + var err error dataRes.Frames, err = buildDataFrames(ctx, startTime, endTime, response, queryRow) if err != nil { @@ -79,6 +83,9 @@ func aggregateResponse(getMetricDataOutputs []*cloudwatch.GetMetricDataOutput) m if *message.Code == "ArithmeticError" { response.AddArithmeticError(message.Value) } + if *message.Code == "Forbidden" { + response.AddPermissionError(message.Value) + } } response.AddMetricDataResult(r) diff --git a/pkg/tsdb/cloudwatch/response_parser_test.go b/pkg/tsdb/cloudwatch/response_parser_test.go index be1d28107ce..b356099922e 100644 --- a/pkg/tsdb/cloudwatch/response_parser_test.go +++ b/pkg/tsdb/cloudwatch/response_parser_test.go @@ -135,6 +135,15 @@ func TestCloudWatchResponseParser(t *testing.T) { }) }) }) + + t.Run("when receiving a permissions error should pass it to the user", func(t *testing.T) { + getMetricDataOutputs, err := loadGetMetricDataOutputsFromFile("./testdata/permissions-error-output.json") + require.NoError(t, err) + aggregatedResponse := aggregateResponse(getMetricDataOutputs) + + assert.True(t, aggregatedResponse["a"].HasPermissionError) + assert.Equal(t, "Access denied when getting data - please check that you have the pi:GetResourceMetrics permission", aggregatedResponse["a"].PermissionErrorMessage) + }) } func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { diff --git a/pkg/tsdb/cloudwatch/testdata/permissions-error-output.json b/pkg/tsdb/cloudwatch/testdata/permissions-error-output.json new file mode 100644 index 00000000000..6815d6b27ee --- /dev/null +++ b/pkg/tsdb/cloudwatch/testdata/permissions-error-output.json @@ -0,0 +1,16 @@ +[ + { + "Messages": null, + "MetricDataResults": [ + { + "Id": "a", + "Messages": [{ + "Code": "Forbidden", + "Value": "Access denied when getting data - please check that you have the pi:GetResourceMetrics permission" + }], + "StatusCode": "Forbidden" + } + ], + "NextToken": null + } +] \ No newline at end of file