diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go index 735c65d2177..2d89697eb4d 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch.go +++ b/pkg/tsdb/cloudwatch/cloudwatch.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "net/http" - "sync" "time" "github.com/aws/aws-sdk-go/aws" @@ -124,10 +123,9 @@ func NewInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins // cloudWatchExecutor executes CloudWatch requests type cloudWatchExecutor struct { - im instancemgmt.InstanceManager - sessions SessionCache - regionCache sync.Map - logger log.Logger + im instancemgmt.InstanceManager + sessions SessionCache + logger log.Logger resourceHandler backend.CallResourceHandler } diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index cf1edd524a3..8acddfbcbd0 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -17,7 +17,6 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/constants" ) type suggestData struct { @@ -40,54 +39,6 @@ func parseMultiSelectValue(input string) []string { return []string{trimmedInput} } -// Whenever this list is updated, the frontend list should also be updated. -// Please update the region list in public/app/plugins/datasource/cloudwatch/partials/config.html -func (e *cloudWatchExecutor) handleGetRegions(ctx context.Context, pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) { - instance, err := e.getInstance(ctx, pluginCtx) - if err != nil { - return nil, err - } - - profile := instance.Settings.Profile - if cache, ok := e.regionCache.Load(profile); ok { - if cache2, ok2 := cache.([]suggestData); ok2 { - return cache2, nil - } - } - - client, err := e.getEC2Client(ctx, pluginCtx, defaultRegion) - if err != nil { - return nil, err - } - regions := constants.Regions() - ec2Regions, err := client.DescribeRegionsWithContext(ctx, &ec2.DescribeRegionsInput{}) - if err != nil { - // ignore error for backward compatibility - e.logger.FromContext(ctx).Error("Failed to get regions", "error", err) - } else { - mergeEC2RegionsAndConstantRegions(regions, ec2Regions.Regions) - } - - result := make([]suggestData, 0) - for region := range regions { - result = append(result, suggestData{Text: region, Value: region, Label: region}) - } - sort.Slice(result, func(i, j int) bool { - return result[i].Text < result[j].Text - }) - e.regionCache.Store(profile, result) - - return result, nil -} - -func mergeEC2RegionsAndConstantRegions(regions map[string]struct{}, ec2Regions []*ec2.Region) { - for _, region := range ec2Regions { - if _, ok := regions[*region.RegionName]; !ok { - regions[*region.RegionName] = struct{}{} - } - } -} - func (e *cloudWatchExecutor) handleGetEbsVolumeIds(ctx context.Context, pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) { region := parameters.Get("region") instanceId := parameters.Get("instanceId") diff --git a/pkg/tsdb/cloudwatch/metric_find_query_test.go b/pkg/tsdb/cloudwatch/metric_find_query_test.go index a9172c1690d..f8c73ac8f59 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query_test.go +++ b/pkg/tsdb/cloudwatch/metric_find_query_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "net/url" - "sort" "testing" "github.com/aws/aws-sdk-go/aws" @@ -12,124 +11,15 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" - "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" - "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/constants" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) -func TestQuery_Regions(t *testing.T) { - origNewEC2Client := NewEC2Client - t.Cleanup(func() { - NewEC2Client = origNewEC2Client - }) - - ec2Mock := &mocks.EC2Mock{} - NewEC2Client = func(provider client.ConfigProvider) models.EC2APIProvider { - return ec2Mock - } - t.Run("An extra region", func(t *testing.T) { - const regionName = "xtra-region" - ec2Mock.On("DescribeRegionsWithContext", mock.Anything, mock.Anything).Return(&ec2.DescribeRegionsOutput{ - Regions: []*ec2.Region{ - { - RegionName: utils.Pointer(regionName), - }, - }, - }, nil) - - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-2"}, - GrafanaSettings: awsds.AuthSettings{ListMetricsPageLimit: 1000}, - }}, nil - }) - - executor := newExecutor(im, &fakeSessionCache{}, log.NewNullLogger()) - resp, err := executor.handleGetRegions( - context.Background(), - backend.PluginContext{ - DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}, - }, url.Values{ - "region": []string{"us-east-1"}, - "namespace": []string{"custom"}, - }, - ) - require.NoError(t, err) - - expRegions := buildSortedSliceOfDefaultAndExtraRegions(t, regionName) - expFrame := data.NewFrame( - "", - data.NewField("text", nil, expRegions), - data.NewField("value", nil, expRegions), - ) - expFrame.Meta = &data.FrameMeta{ - Custom: map[string]any{ - "rowCount": len(constants.Regions()) + 1, - }, - } - - expResponse := []suggestData{} - for _, region := range expRegions { - expResponse = append(expResponse, suggestData{Text: region, Value: region, Label: region}) - } - assert.Equal(t, expResponse, resp) - }) -} - -func buildSortedSliceOfDefaultAndExtraRegions(t *testing.T, regionName string) []string { - t.Helper() - regions := constants.Regions() - regions[regionName] = struct{}{} - var expRegions []string - for region := range regions { - expRegions = append(expRegions, region) - } - sort.Strings(expRegions) - return expRegions -} - -func Test_handleGetRegions_regionCache(t *testing.T) { - origNewEC2Client := NewEC2Client - t.Cleanup(func() { - NewEC2Client = origNewEC2Client - }) - cli := mockEC2Client{} - NewEC2Client = func(client.ConfigProvider) models.EC2APIProvider { - return &cli - } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-2"}, - GrafanaSettings: awsds.AuthSettings{ListMetricsPageLimit: 1000}, - }}, nil - }) - - t.Run("AWS only called once for multiple calls to handleGetRegions", func(t *testing.T) { - cli.On("DescribeRegionsWithContext", mock.Anything, mock.Anything).Return(&ec2.DescribeRegionsOutput{}, nil) - executor := newExecutor(im, &fakeSessionCache{}, log.NewNullLogger()) - _, err := executor.handleGetRegions( - context.Background(), - backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, nil) - require.NoError(t, err) - - _, err = executor.handleGetRegions( - context.Background(), - backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, nil) - require.NoError(t, err) - - cli.AssertNumberOfCalls(t, "DescribeRegionsWithContext", 1) - }) -} func TestQuery_InstanceAttributes(t *testing.T) { origNewEC2Client := NewEC2Client t.Cleanup(func() { diff --git a/pkg/tsdb/cloudwatch/test_utils.go b/pkg/tsdb/cloudwatch/test_utils.go index 698d3c60b86..0d2683b19d4 100644 --- a/pkg/tsdb/cloudwatch/test_utils.go +++ b/pkg/tsdb/cloudwatch/test_utils.go @@ -119,20 +119,6 @@ func (c *fakeCWAnnotationsClient) DescribeAlarms(params *cloudwatch.DescribeAlar return c.describeAlarmsOutput, nil } -type mockEC2Client struct { - mock.Mock -} - -func (c *mockEC2Client) DescribeRegionsWithContext(ctx aws.Context, in *ec2.DescribeRegionsInput, option ...request.Option) (*ec2.DescribeRegionsOutput, error) { - args := c.Called(in) - return args.Get(0).(*ec2.DescribeRegionsOutput), args.Error(1) -} - -func (c *mockEC2Client) DescribeInstancesPagesWithContext(ctx aws.Context, in *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error { - args := c.Called(in, fn) - return args.Error(0) -} - // Please use mockEC2Client above, we are slowly migrating towards using testify's mocks only type oldEC2Client struct { ec2iface.EC2API