|
|
|
|
@ -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 |
|
|
|
|
} |
|
|
|
|
|