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:
Nathan Vērzemnieks
2025-04-01 10:03:06 +02:00
committed by GitHub
parent 3e15459d20
commit a65cc0df93
60 changed files with 1497 additions and 1607 deletions
+43 -20
View File
@@ -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)
}
+19 -10
View File
@@ -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)
})
}
+1 -1
View File
@@ -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 &paramValue
}
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"`
}