CloudWatch: Migrate to aws-sdk-go-v2 (#103106)
* Cloudwatch: Migrate to aws-sdk-go-v2 (#99643) * CloudWatch: use PDC fix from new grafana-aws-sdk
This commit is contained in:
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"net/url"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/oam"
|
||||
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
|
||||
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
||||
"github.com/aws/aws-sdk-go-v2/service/oam"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
|
||||
@@ -19,12 +19,13 @@ type RequestContextFactoryFunc func(ctx context.Context, pluginCtx backend.Plugi
|
||||
type RouteHandlerFunc func(ctx context.Context, pluginCtx backend.PluginContext, reqContextFactory RequestContextFactoryFunc, parameters url.Values) ([]byte, *HttpError)
|
||||
|
||||
type RequestContext struct {
|
||||
MetricsClientProvider MetricsClientProvider
|
||||
LogsAPIProvider CloudWatchLogsAPIProvider
|
||||
OAMAPIProvider OAMAPIProvider
|
||||
EC2APIProvider EC2APIProvider
|
||||
Settings CloudWatchSettings
|
||||
Logger log.Logger
|
||||
MetricsClientProvider MetricsClientProvider
|
||||
ListMetricsAPIProvider cloudwatch.ListMetricsAPIClient
|
||||
LogsAPIProvider CloudWatchLogsAPIProvider
|
||||
OAMAPIProvider OAMAPIProvider
|
||||
EC2APIProvider EC2APIProvider
|
||||
Settings CloudWatchSettings
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
// Services
|
||||
@@ -35,8 +36,8 @@ type ListMetricsProvider interface {
|
||||
}
|
||||
|
||||
type LogGroupsProvider interface {
|
||||
GetLogGroupsWithContext(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error)
|
||||
GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error)
|
||||
GetLogGroups(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error)
|
||||
GetLogGroupFields(ctx context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error)
|
||||
}
|
||||
|
||||
type AccountsProvider interface {
|
||||
@@ -54,20 +55,42 @@ type MetricsClientProvider interface {
|
||||
|
||||
// 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 {
|
||||
ListMetricsPagesWithContext(ctx context.Context, in *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error
|
||||
ListMetrics(ctx context.Context, in *cloudwatch.ListMetricsInput, optFns ...func(*cloudwatch.Options)) error
|
||||
}
|
||||
|
||||
type CloudWatchLogsAPIProvider interface {
|
||||
DescribeLogGroupsWithContext(ctx context.Context, in *cloudwatchlogs.DescribeLogGroupsInput, opts ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error)
|
||||
GetLogGroupFieldsWithContext(ctx context.Context, in *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error)
|
||||
cloudwatchlogs.DescribeLogGroupsAPIClient
|
||||
GetLogGroupFields(ctx context.Context, in *cloudwatchlogs.GetLogGroupFieldsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error)
|
||||
}
|
||||
|
||||
type OAMAPIProvider interface {
|
||||
ListSinksWithContext(ctx context.Context, in *oam.ListSinksInput, opts ...request.Option) (*oam.ListSinksOutput, error)
|
||||
ListAttachedLinksWithContext(ctx context.Context, in *oam.ListAttachedLinksInput, opts ...request.Option) (*oam.ListAttachedLinksOutput, error)
|
||||
ListSinks(ctx context.Context, in *oam.ListSinksInput, optFns ...func(options *oam.Options)) (*oam.ListSinksOutput, error)
|
||||
ListAttachedLinks(ctx context.Context, in *oam.ListAttachedLinksInput, optFns ...func(options *oam.Options)) (*oam.ListAttachedLinksOutput, error)
|
||||
}
|
||||
|
||||
type EC2APIProvider interface {
|
||||
DescribeRegionsWithContext(ctx context.Context, in *ec2.DescribeRegionsInput, opts ...request.Option) (*ec2.DescribeRegionsOutput, error)
|
||||
DescribeInstancesPagesWithContext(ctx context.Context, in *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error
|
||||
DescribeRegions(ctx context.Context, in *ec2.DescribeRegionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error)
|
||||
ec2.DescribeInstancesAPIClient
|
||||
}
|
||||
|
||||
type CWLogsClient interface {
|
||||
StartQuery(context.Context, *cloudwatchlogs.StartQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error)
|
||||
StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error)
|
||||
GetQueryResults(context.Context, *cloudwatchlogs.GetQueryResultsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error)
|
||||
|
||||
cloudwatchlogs.GetLogEventsAPIClient
|
||||
cloudwatchlogs.DescribeLogGroupsAPIClient
|
||||
}
|
||||
|
||||
type CWClient interface {
|
||||
AlarmsAPI
|
||||
cloudwatch.GetMetricDataAPIClient
|
||||
cloudwatch.ListMetricsAPIClient
|
||||
}
|
||||
|
||||
type AlarmsAPI interface {
|
||||
cloudwatch.DescribeAlarmsAPIClient
|
||||
cloudwatch.DescribeAlarmHistoryAPIClient
|
||||
|
||||
DescribeAlarmsForMetric(context.Context, *cloudwatch.DescribeAlarmsForMetricInput, ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsForMetricOutput, error)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
@@ -201,7 +202,11 @@ func (q *CloudWatchQuery) BuildDeepLink(startTime time.Time, endTime time.Time)
|
||||
return "", fmt.Errorf("could not marshal link: %w", err)
|
||||
}
|
||||
|
||||
url, err := url.Parse(fmt.Sprintf(`https://%s/cloudwatch/deeplink.js`, getEndpoint(q.Region)))
|
||||
endpoint, err := getEndpoint(q.Region)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
url, err := url.Parse(fmt.Sprintf(`https://%s/cloudwatch/deeplink.js`, endpoint))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to parse CloudWatch console deep link")
|
||||
}
|
||||
@@ -503,14 +508,18 @@ func parseDimensions(dimensions dataquery.Dimensions) (map[string][]string, erro
|
||||
return parsedDimensions, nil
|
||||
}
|
||||
|
||||
func getEndpoint(region string) string {
|
||||
partition, _ := endpoints.PartitionForRegion(endpoints.DefaultPartitions(), region)
|
||||
url := defaultConsoleURL
|
||||
if partition.ID() == endpoints.AwsUsGovPartitionID {
|
||||
url = usGovConsoleURL
|
||||
func getEndpoint(region string) (string, error) {
|
||||
resolver := cloudwatch.NewDefaultEndpointResolver()
|
||||
endpoint, err := resolver.ResolveEndpoint(region, cloudwatch.EndpointResolverOptions{})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolve endpoint failed: %w", err)
|
||||
}
|
||||
if partition.ID() == endpoints.AwsCnPartitionID {
|
||||
url = chinaConsoleURL
|
||||
consoleURL := defaultConsoleURL
|
||||
switch endpoint.PartitionID {
|
||||
case "aws-us-gov":
|
||||
consoleURL = usGovConsoleURL
|
||||
case "aws-cn":
|
||||
consoleURL = chinaConsoleURL
|
||||
}
|
||||
return fmt.Sprintf("%s.%s", region, url)
|
||||
return fmt.Sprintf("%s.%s", region, consoleURL), nil
|
||||
}
|
||||
|
||||
@@ -7,13 +7,15 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
|
||||
)
|
||||
|
||||
var logger = log.NewNullLogger()
|
||||
@@ -932,7 +934,6 @@ func Test_migrateAliasToDynamicLabel_single_query_preserves_old_alias_and_create
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
average := "Average"
|
||||
false := false
|
||||
|
||||
queryToMigrate := metricsDataQuery{
|
||||
CloudWatchMetricsQuery: dataquery.CloudWatchMetricsQuery{
|
||||
@@ -945,7 +946,7 @@ func Test_migrateAliasToDynamicLabel_single_query_preserves_old_alias_and_create
|
||||
},
|
||||
Statistic: &average,
|
||||
Period: utils.Pointer("600"),
|
||||
Hide: &false,
|
||||
Hide: aws.Bool(false),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1305,7 +1306,8 @@ func TestGetEndpoint(t *testing.T) {
|
||||
}
|
||||
for _, ts := range testcases {
|
||||
t.Run(fmt.Sprintf("should create correct endpoint for %s", ts), func(t *testing.T) {
|
||||
actual := getEndpoint(ts.region)
|
||||
actual, err := getEndpoint(ts.region)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, ts.expectedEndpoint, actual)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ type LogsQuery struct {
|
||||
dataquery.CloudWatchLogsQuery
|
||||
StartTime *int64
|
||||
EndTime *int64
|
||||
Limit *int64
|
||||
Limit *int32
|
||||
LogGroupName string
|
||||
LogStreamName string
|
||||
QueryId string
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
|
||||
)
|
||||
|
||||
// queryRowResponse represents the GetMetricData response for a query row in the query editor.
|
||||
// QueryRowResponse represents the GetMetricData response for a query row in the query editor.
|
||||
type QueryRowResponse struct {
|
||||
partialDataSet map[string]*cloudwatch.MetricDataResult
|
||||
partialDataSet map[string]*cloudwatchtypes.MetricDataResult
|
||||
ErrorCodes map[string]bool
|
||||
HasArithmeticError bool
|
||||
ArithmeticErrorMessage string
|
||||
HasPermissionError bool
|
||||
PermissionErrorMessage string
|
||||
Metrics []*cloudwatch.MetricDataResult
|
||||
StatusCode string
|
||||
Metrics []*cloudwatchtypes.MetricDataResult
|
||||
StatusCode cloudwatchtypes.StatusCode
|
||||
}
|
||||
|
||||
func NewQueryRowResponse(errors map[string]bool) QueryRowResponse {
|
||||
return QueryRowResponse{
|
||||
partialDataSet: make(map[string]*cloudwatch.MetricDataResult),
|
||||
partialDataSet: make(map[string]*cloudwatchtypes.MetricDataResult),
|
||||
ErrorCodes: errors,
|
||||
HasArithmeticError: false,
|
||||
ArithmeticErrorMessage: "",
|
||||
Metrics: []*cloudwatch.MetricDataResult{},
|
||||
Metrics: []*cloudwatchtypes.MetricDataResult{},
|
||||
}
|
||||
}
|
||||
|
||||
func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatch.MetricDataResult) {
|
||||
func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatchtypes.MetricDataResult) {
|
||||
if mdr.Label == nil {
|
||||
return
|
||||
}
|
||||
@@ -34,16 +34,16 @@ 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" {
|
||||
q.StatusCode = mdr.StatusCode
|
||||
if mdr.StatusCode != cloudwatchtypes.StatusCodePartialData {
|
||||
delete(q.partialDataSet, *mdr.Label)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
q.Metrics = append(q.Metrics, mdr)
|
||||
q.StatusCode = *mdr.StatusCode
|
||||
if *mdr.StatusCode == "PartialData" {
|
||||
q.StatusCode = mdr.StatusCode
|
||||
if mdr.StatusCode == cloudwatchtypes.StatusCodePartialData {
|
||||
q.partialDataSet[*mdr.Label] = mdr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const defaultLogGroupLimit = int64(50)
|
||||
const defaultLogGroupLimit = int32(50)
|
||||
|
||||
type LogGroupsRequest struct {
|
||||
ResourceRequest
|
||||
Limit int64
|
||||
Limit int32
|
||||
LogGroupNamePrefix, LogGroupNamePattern *string
|
||||
ListAllLogGroups bool
|
||||
}
|
||||
@@ -45,11 +45,11 @@ func setIfNotEmptyString(paramValue string) *string {
|
||||
return ¶mValue
|
||||
}
|
||||
|
||||
func getLimit(limit string) int64 {
|
||||
func getLimit(limit string) int32 {
|
||||
logGroupLimit := defaultLogGroupLimit
|
||||
intLimit, err := strconv.ParseInt(limit, 10, 64)
|
||||
if err == nil && intLimit > 0 {
|
||||
logGroupLimit = intLimit
|
||||
logGroupLimit = int32(intLimit)
|
||||
}
|
||||
return logGroupLimit
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package resources
|
||||
|
||||
import "github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
import (
|
||||
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
|
||||
)
|
||||
|
||||
type Dimension struct {
|
||||
Name string
|
||||
@@ -13,7 +15,7 @@ type ResourceResponse[T any] struct {
|
||||
}
|
||||
|
||||
type MetricResponse struct {
|
||||
*cloudwatch.Metric
|
||||
Metric cloudwatchtypes.Metric
|
||||
AccountId *string `json:"accountId,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user