Cloudwatch: Backend cleanup (#59663)

* cleanup cloudwatch.go

* streamline interface naming

* use utility func

* rename test utils file

* move util function to where they are used

* move dtos to models

* split integration tests from the rest

* Update pkg/tsdb/cloudwatch/cloudwatch.go

Co-authored-by: Isabella Siu <Isabella.siu@grafana.com>

* refactor error codes aggregation

* move error messages to models

Co-authored-by: Isabella Siu <Isabella.siu@grafana.com>
This commit is contained in:
Erik Sundell
2022-12-02 10:21:46 +01:00
committed by GitHub
co-authored by Isabella Siu
parent ada0c771ef
commit fde9a5d112
22 changed files with 798 additions and 598 deletions
+21 -18
View File
@@ -18,34 +18,18 @@ type RouteHandlerFunc func(pluginCtx backend.PluginContext, reqContextFactory Re
type RequestContext struct {
MetricsClientProvider MetricsClientProvider
LogsAPIProvider CloudWatchLogsAPIProvider
OAMClientProvider OAMClientProvider
OAMAPIProvider OAMAPIProvider
Settings CloudWatchSettings
Features featuremgmt.FeatureToggles
}
// Services
type ListMetricsProvider interface {
GetDimensionKeysByDimensionFilter(resources.DimensionKeysRequest) ([]resources.ResourceResponse[string], error)
GetDimensionValuesByDimensionFilter(resources.DimensionValuesRequest) ([]resources.ResourceResponse[string], error)
GetMetricsByNamespace(r resources.MetricsRequest) ([]resources.ResourceResponse[resources.Metric], error)
}
type MetricsClientProvider interface {
ListMetricsWithPageLimit(params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error)
}
type CloudWatchMetricsAPIProvider interface {
ListMetricsPages(*cloudwatch.ListMetricsInput, func(*cloudwatch.ListMetricsOutput, bool) bool) error
}
type CloudWatchLogsAPIProvider interface {
DescribeLogGroups(*cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error)
}
type OAMClientProvider interface {
ListSinks(*oam.ListSinksInput) (*oam.ListSinksOutput, error)
ListAttachedLinks(*oam.ListAttachedLinksInput) (*oam.ListAttachedLinksOutput, error)
}
type LogGroupsProvider interface {
GetLogGroups(request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error)
}
@@ -53,3 +37,22 @@ type LogGroupsProvider interface {
type AccountsProvider interface {
GetAccountsForCurrentUserOrRole() ([]resources.ResourceResponse[resources.Account], error)
}
// Clients
type MetricsClientProvider interface {
ListMetricsWithPageLimit(params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error)
}
// APIs - instead of using the API defined in the services within the aws-sdk-go directly, specify a subset of the API with methods that are actually used in a service or a client
type CloudWatchMetricsAPIProvider interface {
ListMetricsPages(*cloudwatch.ListMetricsInput, func(*cloudwatch.ListMetricsOutput, bool) bool) error
}
type CloudWatchLogsAPIProvider interface {
DescribeLogGroups(*cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error)
}
type OAMAPIProvider interface {
ListSinks(*oam.ListSinksInput) (*oam.ListSinksOutput, error)
ListAttachedLinks(*oam.ListAttachedLinksInput) (*oam.ListAttachedLinksOutput, error)
}
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/log/logtest"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
)
func TestCloudWatchQuery(t *testing.T) {
@@ -119,7 +120,7 @@ func TestCloudWatchQuery(t *testing.T) {
Period: 300,
Id: "id1",
MatchExact: true,
AccountId: pointer("123456789"),
AccountId: utils.Pointer("123456789"),
Label: "${PROP('Namespace')}",
Dimensions: map[string][]string{
"InstanceId": {"i-12345678"},
@@ -141,7 +142,7 @@ func TestCloudWatchQuery(t *testing.T) {
Region: "us-east-1",
Statistic: "Average",
Expression: "SEARCH(someexpression)",
AccountId: pointer("123456789"),
AccountId: utils.Pointer("123456789"),
Period: 300,
Id: "id1",
MatchExact: true,
@@ -1059,7 +1060,6 @@ func Test_ParseMetricDataQueries_migrate_alias_to_label(t *testing.T) {
dynamicLabelsFeatureToggleEnabled: false,
expectedLabel: "some label"},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
query := []backend.DataQuery{
@@ -1195,5 +1195,3 @@ func Test_ParseMetricDataQueries_account_Id(t *testing.T) {
assert.Nil(t, actual[0].AccountId)
})
}
func pointer[T any](arg T) *T { return &arg }
+15
View File
@@ -0,0 +1,15 @@
package models
const (
MaxMetricsExceeded = "MaxMetricsExceeded"
MaxQueryTimeRangeExceeded = "MaxQueryTimeRangeExceeded"
MaxQueryResultsExceeded = "MaxQueryResultsExceeded"
MaxMatchingResultsExceeded = "MaxMatchingResultsExceeded"
)
var ErrorMessages = map[string]string{
MaxMetricsExceeded: "Maximum number of allowed metrics exceeded. Your search may have been limited",
MaxQueryTimeRangeExceeded: "Max time window exceeded for query",
MaxQueryResultsExceeded: "Only the first 500 time series can be returned by a query.",
MaxMatchingResultsExceeded: "The query matched more than 10.000 metrics, results might not be accurate.",
}
@@ -0,0 +1,46 @@
package models
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 {
partialDataSet map[string]*cloudwatch.MetricDataResult
ErrorCodes map[string]bool
HasArithmeticError bool
ArithmeticErrorMessage string
Metrics []*cloudwatch.MetricDataResult
StatusCode string
}
func NewQueryRowResponse(errors map[string]bool) QueryRowResponse {
return QueryRowResponse{
partialDataSet: make(map[string]*cloudwatch.MetricDataResult),
ErrorCodes: errors,
HasArithmeticError: false,
ArithmeticErrorMessage: "",
Metrics: []*cloudwatch.MetricDataResult{},
}
}
func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatch.MetricDataResult) {
if partialData, ok := q.partialDataSet[*mdr.Label]; ok {
partialData.Timestamps = append(partialData.Timestamps, mdr.Timestamps...)
partialData.Values = append(partialData.Values, mdr.Values...)
q.StatusCode = *mdr.StatusCode
if *mdr.StatusCode != "PartialData" {
delete(q.partialDataSet, *mdr.Label)
}
return
}
q.Metrics = append(q.Metrics, mdr)
q.StatusCode = *mdr.StatusCode
if *mdr.StatusCode == "PartialData" {
q.partialDataSet[*mdr.Label] = mdr
}
}
func (q *QueryRowResponse) AddArithmeticError(message *string) {
q.HasArithmeticError = true
q.ArithmeticErrorMessage = *message
}