diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go index 0369a078569..57f06efc1e0 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch.go +++ b/pkg/tsdb/cloudwatch/cloudwatch.go @@ -164,7 +164,13 @@ func (e *cloudWatchExecutor) checkHealthMetrics(pluginCtx backend.PluginContext) Namespace: &namespace, MetricName: &metric, } - _, err := e.listMetrics(pluginCtx, defaultRegion, params) + + session, err := e.newSession(pluginCtx, defaultRegion) + if err != nil { + return err + } + metricClient := clients.NewMetricsClient(NewMetricsAPI(session), e.cfg) + _, err = metricClient.ListMetricsWithPageLimit(params) return err } diff --git a/pkg/tsdb/cloudwatch/cloudwatch_test.go b/pkg/tsdb/cloudwatch/cloudwatch_test.go index 08e95fef1d8..8db4144d865 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch_test.go +++ b/pkg/tsdb/cloudwatch/cloudwatch_test.go @@ -11,7 +11,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/google/go-cmp/cmp" @@ -95,15 +94,15 @@ func TestNewInstanceSettings(t *testing.T) { } func Test_CheckHealth(t *testing.T) { - origNewCWClient := NewCWClient + origNewMetricsAPI := NewMetricsAPI origNewCWLogsClient := NewCWLogsClient t.Cleanup(func() { - NewCWClient = origNewCWClient + NewMetricsAPI = origNewMetricsAPI NewCWLogsClient = origNewCWLogsClient }) var client fakeCheckHealthClient - NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { + NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { return client } NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index 4ed640e7fd8..a5781796531 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -13,13 +13,10 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awsutil" - "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/resourcegroupstaggingapi" "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/constants" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/cwlog" ) @@ -30,12 +27,6 @@ type suggestData struct { Label string `json:"label,omitempty"` } -type customMetricsCache struct { - Expire time.Time - Cache []string -} - -var customMetricsMetricsMap = make(map[string]map[string]map[string]*customMetricsCache) var regionCache sync.Map func parseMultiSelectValue(input string) []string { @@ -128,44 +119,6 @@ func (e *cloudWatchExecutor) handleGetNamespaces(pluginCtx backend.PluginContext return result, nil } -func (e *cloudWatchExecutor) handleGetMetrics(pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) { - region := parameters.Get("region") - namespace := parameters.Get("namespace") - - var namespaceMetrics []string - if !isCustomMetrics(namespace) { - var exists bool - if namespaceMetrics, exists = constants.NamespaceMetricsMap[namespace]; !exists { - return nil, fmt.Errorf("unable to find namespace %q", namespace) - } - } else { - var err error - if namespaceMetrics, err = e.getMetricsForCustomMetrics(region, namespace, pluginCtx); err != nil { - return nil, fmt.Errorf("%v: %w", "unable to call AWS API", err) - } - } - sort.Strings(namespaceMetrics) - - result := make([]suggestData, 0) - for _, name := range namespaceMetrics { - result = append(result, suggestData{Text: name, Value: name, Label: name}) - } - - return result, nil -} - -// handleGetAllMetrics returns a slice of suggestData structs with metric and its namespace -func (e *cloudWatchExecutor) handleGetAllMetrics(pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) { - result := make([]suggestData, 0) - for namespace, metrics := range constants.NamespaceMetricsMap { - for _, metric := range metrics { - result = append(result, suggestData{Text: namespace, Value: metric, Label: namespace}) - } - } - - return result, nil -} - func (e *cloudWatchExecutor) handleGetEbsVolumeIds(pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) { region := parameters.Get("region") instanceId := parameters.Get("instanceId") @@ -317,31 +270,6 @@ func (e *cloudWatchExecutor) handleGetResourceArns(pluginCtx backend.PluginConte return result, nil } -func (e *cloudWatchExecutor) listMetrics(pluginCtx backend.PluginContext, region string, params *cloudwatch.ListMetricsInput) ([]*cloudwatch.Metric, error) { - client, err := e.getCWClient(pluginCtx, region) - if err != nil { - return nil, err - } - - cwlog.Debug("Listing metrics pages") - var cloudWatchMetrics []*cloudwatch.Metric - - pageNum := 0 - err = client.ListMetricsPages(params, func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool { - pageNum++ - metrics.MAwsCloudWatchListMetrics.Inc() - metrics, err := awsutil.ValuesAtPath(page, "Metrics") - if err == nil { - for _, metric := range metrics { - cloudWatchMetrics = append(cloudWatchMetrics, metric.(*cloudwatch.Metric)) - } - } - return !lastPage && pageNum < e.cfg.AWSListMetricsPageLimit - }) - - return cloudWatchMetrics, err -} - func (e *cloudWatchExecutor) ec2DescribeInstances(pluginCtx backend.PluginContext, region string, filters []*ec2.Filter, instanceIds []*string) (*ec2.DescribeInstancesOutput, error) { params := &ec2.DescribeInstancesInput{ Filters: filters, @@ -388,53 +316,6 @@ func (e *cloudWatchExecutor) resourceGroupsGetResources(pluginCtx backend.Plugin return &resp, nil } -var metricsCacheLock sync.Mutex - -func (e *cloudWatchExecutor) getMetricsForCustomMetrics(region, namespace string, pluginCtx backend.PluginContext) ([]string, error) { - cwlog.Debug("Getting metrics for custom metrics", "region", region, "namespace", namespace) - metricsCacheLock.Lock() - defer metricsCacheLock.Unlock() - - instance, err := e.getInstance(pluginCtx) - if err != nil { - return nil, err - } - - if _, ok := customMetricsMetricsMap[instance.Settings.Profile]; !ok { - customMetricsMetricsMap[instance.Settings.Profile] = make(map[string]map[string]*customMetricsCache) - } - if _, ok := customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region]; !ok { - customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region] = make(map[string]*customMetricsCache) - } - if _, ok := customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace]; !ok { - customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace] = &customMetricsCache{} - customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace].Cache = make([]string, 0) - } - - if customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace].Expire.After(time.Now()) { - return customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace].Cache, nil - } - metrics, err := e.listMetrics(pluginCtx, region, &cloudwatch.ListMetricsInput{ - Namespace: aws.String(namespace), - }) - if err != nil { - return []string{}, err - } - - customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace].Cache = make([]string, 0) - customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace].Expire = time.Now().Add(5 * time.Minute) - - for _, metric := range metrics { - if isDuplicate(customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace].Cache, *metric.MetricName) { - continue - } - customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace].Cache = append( - customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace].Cache, *metric.MetricName) - } - - return customMetricsMetricsMap[instance.Settings.Profile][instance.Settings.Region][namespace].Cache, nil -} - func (e *cloudWatchExecutor) handleGetLogGroups(pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) { region := parameters.Get("region") limit := parameters.Get("limit") @@ -505,19 +386,3 @@ func (e *cloudWatchExecutor) handleGetAllLogGroups(pluginCtx backend.PluginConte return result, nil } - -func isDuplicate(nameList []string, target string) bool { - for _, name := range nameList { - if name == target { - return true - } - } - return false -} - -func isCustomMetrics(namespace string) bool { - if _, ok := constants.NamespaceMetricsMap[namespace]; ok { - return false - } - return true -} diff --git a/pkg/tsdb/cloudwatch/metric_find_query_test.go b/pkg/tsdb/cloudwatch/metric_find_query_test.go index f20389e169e..89e04c9eb07 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query_test.go +++ b/pkg/tsdb/cloudwatch/metric_find_query_test.go @@ -7,9 +7,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2/ec2iface" "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" @@ -20,60 +17,11 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/services/featuremgmt" "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/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestQuery_Metrics(t *testing.T) { - origNewCWClient := NewCWClient - t.Cleanup(func() { - NewCWClient = origNewCWClient - }) - - var cwClient mocks.FakeMetricsAPI - - NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { - return &cwClient - } - - t.Run("Custom metrics", func(t *testing.T) { - cwClient = mocks.FakeMetricsAPI{ - Metrics: []*cloudwatch.Metric{ - { - MetricName: aws.String("Test_MetricName"), - Dimensions: []*cloudwatch.Dimension{ - { - Name: aws.String("Test_DimensionName"), - }, - }, - }, - }, - } - - im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: &models.CloudWatchSettings{}}, nil - }) - - executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures()) - resp, err := executor.handleGetMetrics( - backend.PluginContext{ - DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}, - }, url.Values{ - "region": []string{"us-east-1"}, - "namespace": []string{"custom"}, - }, - ) - require.NoError(t, err) - - expResponse := []suggestData{ - {Text: "Test_MetricName", Value: "Test_MetricName", Label: "Test_MetricName"}, - } - assert.Equal(t, expResponse, resp) - }) -} - func TestQuery_Regions(t *testing.T) { origNewEC2Client := newEC2Client t.Cleanup(func() { @@ -335,29 +283,3 @@ func TestQuery_ResourceARNs(t *testing.T) { assert.Equal(t, expResponse, resp) }) } - -func TestQuery_GetAllMetrics(t *testing.T) { - t.Run("all metrics in all namespaces are being returned", func(t *testing.T) { - im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{Settings: &models.CloudWatchSettings{}}, nil - }) - - executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures()) - resp, err := executor.handleGetAllMetrics( - backend.PluginContext{ - DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}, - }, - url.Values{ - "region": []string{"us-east-1"}, - }, - ) - require.NoError(t, err) - - metricCount := 0 - for _, metrics := range constants.NamespaceMetricsMap { - metricCount += len(metrics) - } - - assert.Equal(t, metricCount, len(resp)) - }) -}