2017-04-03 07:50:40 -05:00
|
|
|
package cloudwatch
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-04-25 15:48:20 -05:00
|
|
|
"fmt"
|
2017-04-03 07:50:40 -05:00
|
|
|
"regexp"
|
2020-10-12 10:58:58 -05:00
|
|
|
"strings"
|
2020-04-25 15:48:20 -05:00
|
|
|
"time"
|
2017-04-03 07:50:40 -05:00
|
|
|
|
2020-06-09 06:13:06 -05:00
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
|
|
|
2020-07-23 11:52:22 -05:00
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/client"
|
2020-10-12 10:58:58 -05:00
|
|
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
2020-07-14 01:23:23 -05:00
|
|
|
"github.com/aws/aws-sdk-go/aws/request"
|
2020-07-23 11:52:22 -05:00
|
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
2020-07-14 01:23:23 -05:00
|
|
|
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
2020-07-23 11:52:22 -05:00
|
|
|
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
|
2020-04-25 15:48:20 -05:00
|
|
|
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
2020-07-23 01:17:20 -05:00
|
|
|
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
|
2020-07-23 11:52:22 -05:00
|
|
|
"github.com/aws/aws-sdk-go/service/ec2"
|
2019-04-15 10:55:07 -05:00
|
|
|
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
2020-07-23 11:52:22 -05:00
|
|
|
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
|
2019-04-15 10:55:07 -05:00
|
|
|
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
|
2020-04-25 15:48:20 -05:00
|
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
2019-05-13 01:45:54 -05:00
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
2017-04-03 07:50:40 -05:00
|
|
|
"github.com/grafana/grafana/pkg/models"
|
2021-03-08 00:02:49 -06:00
|
|
|
"github.com/grafana/grafana/pkg/plugins"
|
2020-10-28 03:36:57 -05:00
|
|
|
"github.com/grafana/grafana/pkg/registry"
|
2020-07-14 01:23:23 -05:00
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2017-04-03 07:50:40 -05:00
|
|
|
)
|
|
|
|
|
2020-07-14 01:23:23 -05:00
|
|
|
type datasourceInfo struct {
|
2017-09-26 04:30:40 -05:00
|
|
|
Profile string
|
|
|
|
Region string
|
2020-10-12 10:58:58 -05:00
|
|
|
AuthType authType
|
|
|
|
AssumeRoleARN string
|
2020-07-02 08:24:36 -05:00
|
|
|
ExternalID string
|
2017-09-26 04:30:40 -05:00
|
|
|
Namespace string
|
2021-02-24 09:05:14 -06:00
|
|
|
Endpoint string
|
2017-09-26 04:30:40 -05:00
|
|
|
|
|
|
|
AccessKey string
|
|
|
|
SecretKey string
|
|
|
|
}
|
|
|
|
|
2020-05-13 14:17:06 -05:00
|
|
|
const cloudWatchTSFormat = "2006-01-02 15:04:05.000"
|
2020-07-14 01:23:23 -05:00
|
|
|
const defaultRegion = "default"
|
2020-04-25 15:48:20 -05:00
|
|
|
|
2020-05-13 08:34:23 -05:00
|
|
|
// Constants also defined in datasource/cloudwatch/datasource.ts
|
2020-05-13 14:17:06 -05:00
|
|
|
const logIdentifierInternal = "__log__grafana_internal__"
|
|
|
|
const logStreamIdentifierInternal = "__logstream__grafana_internal__"
|
2020-05-13 08:34:23 -05:00
|
|
|
|
2020-07-14 01:23:23 -05:00
|
|
|
var plog = log.New("tsdb.cloudwatch")
|
|
|
|
var aliasFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
|
2020-04-25 15:48:20 -05:00
|
|
|
|
2020-07-14 01:23:23 -05:00
|
|
|
func init() {
|
2020-10-28 03:36:57 -05:00
|
|
|
registry.Register(®istry.Descriptor{
|
|
|
|
Name: "CloudWatchService",
|
|
|
|
InitPriority: registry.Low,
|
|
|
|
Instance: &CloudWatchService{},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
type CloudWatchService struct {
|
|
|
|
LogsService *LogsService `inject:""`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *CloudWatchService) Init() error {
|
|
|
|
return nil
|
2020-07-14 01:23:23 -05:00
|
|
|
}
|
2020-04-25 15:48:20 -05:00
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
func (s *CloudWatchService) NewExecutor(*models.DataSource) (plugins.DataPlugin, error) {
|
|
|
|
return newExecutor(s.LogsService), nil
|
|
|
|
}
|
|
|
|
|
2020-10-28 03:36:57 -05:00
|
|
|
func newExecutor(logsService *LogsService) *cloudWatchExecutor {
|
2020-07-23 11:52:22 -05:00
|
|
|
return &cloudWatchExecutor{
|
2020-10-28 03:36:57 -05:00
|
|
|
logsService: logsService,
|
2020-07-14 01:23:23 -05:00
|
|
|
}
|
|
|
|
}
|
2020-04-25 15:48:20 -05:00
|
|
|
|
2020-07-14 01:23:23 -05:00
|
|
|
// cloudWatchExecutor executes CloudWatch requests.
|
|
|
|
type cloudWatchExecutor struct {
|
|
|
|
*models.DataSource
|
|
|
|
|
2020-10-28 03:36:57 -05:00
|
|
|
ec2Client ec2iface.EC2API
|
|
|
|
rgtaClient resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI
|
|
|
|
|
|
|
|
logsService *LogsService
|
2020-04-25 15:48:20 -05:00
|
|
|
}
|
|
|
|
|
2020-07-23 11:52:22 -05:00
|
|
|
func (e *cloudWatchExecutor) newSession(region string) (*session.Session, error) {
|
|
|
|
dsInfo := e.getDSInfo(region)
|
2020-10-12 10:58:58 -05:00
|
|
|
|
|
|
|
bldr := strings.Builder{}
|
|
|
|
for i, s := range []string{
|
2021-02-24 09:05:14 -06:00
|
|
|
dsInfo.AuthType.String(), dsInfo.AccessKey, dsInfo.Profile, dsInfo.AssumeRoleARN, region, dsInfo.Endpoint,
|
2020-10-12 10:58:58 -05:00
|
|
|
} {
|
|
|
|
if i != 0 {
|
|
|
|
bldr.WriteString(":")
|
|
|
|
}
|
|
|
|
bldr.WriteString(strings.ReplaceAll(s, ":", `\:`))
|
|
|
|
}
|
|
|
|
cacheKey := bldr.String()
|
|
|
|
|
|
|
|
sessCacheLock.RLock()
|
|
|
|
if env, ok := sessCache[cacheKey]; ok {
|
|
|
|
if env.expiration.After(time.Now().UTC()) {
|
|
|
|
sessCacheLock.RUnlock()
|
|
|
|
return env.session, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sessCacheLock.RUnlock()
|
|
|
|
|
|
|
|
cfgs := []*aws.Config{
|
|
|
|
{
|
|
|
|
CredentialsChainVerboseErrors: aws.Bool(true),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var regionCfg *aws.Config
|
|
|
|
if dsInfo.Region == defaultRegion {
|
|
|
|
plog.Warn("Region is set to \"default\", which is unsupported")
|
|
|
|
dsInfo.Region = ""
|
|
|
|
}
|
|
|
|
if dsInfo.Region != "" {
|
|
|
|
regionCfg = &aws.Config{Region: aws.String(dsInfo.Region)}
|
|
|
|
cfgs = append(cfgs, regionCfg)
|
|
|
|
}
|
|
|
|
|
2021-02-24 09:05:14 -06:00
|
|
|
if dsInfo.Endpoint != "" {
|
|
|
|
cfgs = append(cfgs, &aws.Config{Endpoint: aws.String(dsInfo.Endpoint)})
|
|
|
|
}
|
|
|
|
|
2020-10-12 10:58:58 -05:00
|
|
|
switch dsInfo.AuthType {
|
|
|
|
case authTypeSharedCreds:
|
|
|
|
plog.Debug("Authenticating towards AWS with shared credentials", "profile", dsInfo.Profile,
|
|
|
|
"region", dsInfo.Region)
|
|
|
|
cfgs = append(cfgs, &aws.Config{
|
|
|
|
Credentials: credentials.NewSharedCredentials("", dsInfo.Profile),
|
|
|
|
})
|
|
|
|
case authTypeKeys:
|
|
|
|
plog.Debug("Authenticating towards AWS with an access key pair", "region", dsInfo.Region)
|
|
|
|
cfgs = append(cfgs, &aws.Config{
|
|
|
|
Credentials: credentials.NewStaticCredentials(dsInfo.AccessKey, dsInfo.SecretKey, ""),
|
|
|
|
})
|
|
|
|
case authTypeDefault:
|
|
|
|
plog.Debug("Authenticating towards AWS with default SDK method", "region", dsInfo.Region)
|
2021-03-09 13:50:16 -06:00
|
|
|
case authTypeEC2IAMRole:
|
|
|
|
plog.Debug("Authenticating towards AWS with IAM Role", "region", dsInfo.Region)
|
|
|
|
sess, err := newSession(cfgs...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cfgs = append(cfgs, &aws.Config{Credentials: newEC2RoleCredentials(sess)})
|
2020-10-12 10:58:58 -05:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("Unrecognized authType: %d", dsInfo.AuthType))
|
|
|
|
}
|
|
|
|
sess, err := newSession(cfgs...)
|
2020-07-14 01:23:23 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-04-25 15:48:20 -05:00
|
|
|
|
2020-10-12 10:58:58 -05:00
|
|
|
duration := stscreds.DefaultDuration
|
2020-12-04 14:44:16 -06:00
|
|
|
expiration := time.Now().UTC().Add(duration)
|
2020-10-12 10:58:58 -05:00
|
|
|
if dsInfo.AssumeRoleARN != "" {
|
|
|
|
// We should assume a role in AWS
|
|
|
|
plog.Debug("Trying to assume role in AWS", "arn", dsInfo.AssumeRoleARN)
|
|
|
|
|
|
|
|
cfgs := []*aws.Config{
|
|
|
|
{
|
|
|
|
CredentialsChainVerboseErrors: aws.Bool(true),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Credentials: newSTSCredentials(sess, dsInfo.AssumeRoleARN, func(p *stscreds.AssumeRoleProvider) {
|
|
|
|
// Not sure if this is necessary, overlaps with p.Duration and is undocumented
|
|
|
|
p.Expiry.SetExpiration(expiration, 0)
|
|
|
|
p.Duration = duration
|
|
|
|
if dsInfo.ExternalID != "" {
|
|
|
|
p.ExternalID = aws.String(dsInfo.ExternalID)
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if regionCfg != nil {
|
|
|
|
cfgs = append(cfgs, regionCfg)
|
|
|
|
}
|
|
|
|
sess, err = newSession(cfgs...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-23 11:52:22 -05:00
|
|
|
}
|
2020-10-12 10:58:58 -05:00
|
|
|
|
|
|
|
plog.Debug("Successfully created AWS session")
|
|
|
|
|
|
|
|
sessCacheLock.Lock()
|
|
|
|
sessCache[cacheKey] = envelope{
|
|
|
|
session: sess,
|
|
|
|
expiration: expiration,
|
|
|
|
}
|
|
|
|
sessCacheLock.Unlock()
|
|
|
|
|
|
|
|
return sess, nil
|
2020-07-23 11:52:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *cloudWatchExecutor) getCWClient(region string) (cloudwatchiface.CloudWatchAPI, error) {
|
|
|
|
sess, err := e.newSession(region)
|
2020-04-25 15:48:20 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-07 04:36:13 -06:00
|
|
|
return NewCWClient(sess), nil
|
2017-04-03 07:50:40 -05:00
|
|
|
}
|
|
|
|
|
2020-07-23 11:52:22 -05:00
|
|
|
func (e *cloudWatchExecutor) getCWLogsClient(region string) (cloudwatchlogsiface.CloudWatchLogsAPI, error) {
|
|
|
|
sess, err := e.newSession(region)
|
2020-07-14 01:23:23 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-07 04:36:13 -06:00
|
|
|
logsClient := NewCWLogsClient(sess)
|
2020-07-23 11:52:22 -05:00
|
|
|
|
|
|
|
return logsClient, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *cloudWatchExecutor) getEC2Client(region string) (ec2iface.EC2API, error) {
|
|
|
|
if e.ec2Client != nil {
|
|
|
|
return e.ec2Client, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
sess, err := e.newSession(region)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
e.ec2Client = newEC2Client(sess)
|
2020-07-14 01:23:23 -05:00
|
|
|
|
2020-07-23 11:52:22 -05:00
|
|
|
return e.ec2Client, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *cloudWatchExecutor) getRGTAClient(region string) (resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI,
|
|
|
|
error) {
|
|
|
|
if e.rgtaClient != nil {
|
|
|
|
return e.rgtaClient, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
sess, err := e.newSession(region)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
e.rgtaClient = newRGTAClient(sess)
|
|
|
|
|
|
|
|
return e.rgtaClient, nil
|
2017-04-03 07:50:40 -05:00
|
|
|
}
|
|
|
|
|
2020-07-23 01:17:20 -05:00
|
|
|
func (e *cloudWatchExecutor) alertQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
|
2021-03-08 00:02:49 -06:00
|
|
|
queryContext plugins.DataQuery) (*cloudwatchlogs.GetQueryResultsOutput, error) {
|
2020-04-25 15:48:20 -05:00
|
|
|
const maxAttempts = 8
|
|
|
|
const pollPeriod = 1000 * time.Millisecond
|
|
|
|
|
|
|
|
queryParams := queryContext.Queries[0].Model
|
2021-03-08 00:02:49 -06:00
|
|
|
startQueryOutput, err := e.executeStartQuery(ctx, logsClient, queryParams, *queryContext.TimeRange)
|
2020-04-25 15:48:20 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
requestParams := simplejson.NewFromAny(map[string]interface{}{
|
|
|
|
"region": queryParams.Get("region").MustString(""),
|
|
|
|
"queryId": *startQueryOutput.QueryId,
|
|
|
|
})
|
|
|
|
|
|
|
|
ticker := time.NewTicker(pollPeriod)
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
attemptCount := 1
|
|
|
|
for range ticker.C {
|
2020-06-29 07:08:32 -05:00
|
|
|
res, err := e.executeGetQueryResults(ctx, logsClient, requestParams)
|
|
|
|
if err != nil {
|
2020-04-25 15:48:20 -05:00
|
|
|
return nil, err
|
2020-06-29 07:08:32 -05:00
|
|
|
}
|
|
|
|
if isTerminated(*res.Status) {
|
2020-04-25 15:48:20 -05:00
|
|
|
return res, err
|
2020-06-29 07:08:32 -05:00
|
|
|
}
|
|
|
|
if attemptCount >= maxAttempts {
|
2020-04-25 15:48:20 -05:00
|
|
|
return res, fmt.Errorf("fetching of query results exceeded max number of attempts")
|
|
|
|
}
|
|
|
|
|
|
|
|
attemptCount++
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
// DataQuery executes a CloudWatch query.
|
|
|
|
func (e *cloudWatchExecutor) DataQuery(ctx context.Context, dsInfo *models.DataSource,
|
|
|
|
queryContext plugins.DataQuery) (plugins.DataResponse, error) {
|
2017-09-22 04:07:10 -05:00
|
|
|
e.DataSource = dsInfo
|
2020-04-25 15:48:20 -05:00
|
|
|
|
|
|
|
/*
|
2021-03-08 00:02:49 -06:00
|
|
|
Unlike many other data sources, with Cloudwatch Logs query requests don't receive the results as the response
|
|
|
|
to the query, but rather an ID is first returned. Following this, a client is expected to send requests along
|
|
|
|
with the ID until the status of the query is complete, receiving (possibly partial) results each time. For
|
|
|
|
queries made via dashboards and Explore, the logic of making these repeated queries is handled on the
|
|
|
|
frontend, but because alerts are executed on the backend the logic needs to be reimplemented here.
|
2020-04-25 15:48:20 -05:00
|
|
|
*/
|
|
|
|
queryParams := queryContext.Queries[0].Model
|
|
|
|
_, fromAlert := queryContext.Headers["FromAlert"]
|
2020-05-21 09:18:09 -05:00
|
|
|
isLogAlertQuery := fromAlert && queryParams.Get("queryMode").MustString("") == "Logs"
|
2020-04-25 15:48:20 -05:00
|
|
|
|
|
|
|
if isLogAlertQuery {
|
|
|
|
return e.executeLogAlertQuery(ctx, queryContext)
|
|
|
|
}
|
|
|
|
|
|
|
|
queryType := queryParams.Get("type").MustString("")
|
2017-09-22 04:07:10 -05:00
|
|
|
|
2020-05-18 05:25:58 -05:00
|
|
|
var err error
|
2021-03-08 00:02:49 -06:00
|
|
|
var result plugins.DataResponse
|
2017-09-09 14:24:39 -05:00
|
|
|
switch queryType {
|
|
|
|
case "metricFindQuery":
|
2017-09-22 04:07:10 -05:00
|
|
|
result, err = e.executeMetricFindQuery(ctx, queryContext)
|
2017-09-25 04:16:40 -05:00
|
|
|
case "annotationQuery":
|
|
|
|
result, err = e.executeAnnotationQuery(ctx, queryContext)
|
2020-04-25 15:48:20 -05:00
|
|
|
case "logAction":
|
|
|
|
result, err = e.executeLogActions(ctx, queryContext)
|
2020-10-28 03:36:57 -05:00
|
|
|
case "liveLogAction":
|
|
|
|
result, err = e.executeLiveLogQuery(ctx, queryContext)
|
2017-09-23 22:30:34 -05:00
|
|
|
case "timeSeriesQuery":
|
|
|
|
fallthrough
|
2017-09-22 04:07:10 -05:00
|
|
|
default:
|
2017-09-23 22:30:34 -05:00
|
|
|
result, err = e.executeTimeSeriesQuery(ctx, queryContext)
|
2017-09-09 14:24:39 -05:00
|
|
|
}
|
2017-09-22 04:07:10 -05:00
|
|
|
|
|
|
|
return result, err
|
2017-09-09 14:24:39 -05:00
|
|
|
}
|
2020-04-25 15:48:20 -05:00
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
func (e *cloudWatchExecutor) executeLogAlertQuery(ctx context.Context, queryContext plugins.DataQuery) (
|
|
|
|
plugins.DataResponse, error) {
|
2020-04-25 15:48:20 -05:00
|
|
|
queryParams := queryContext.Queries[0].Model
|
|
|
|
queryParams.Set("subtype", "StartQuery")
|
|
|
|
queryParams.Set("queryString", queryParams.Get("expression").MustString(""))
|
|
|
|
|
2020-07-14 01:23:23 -05:00
|
|
|
region := queryParams.Get("region").MustString(defaultRegion)
|
|
|
|
if region == defaultRegion {
|
2020-04-25 15:48:20 -05:00
|
|
|
region = e.DataSource.JsonData.Get("defaultRegion").MustString()
|
|
|
|
queryParams.Set("region", region)
|
|
|
|
}
|
|
|
|
|
2020-07-14 01:23:23 -05:00
|
|
|
logsClient, err := e.getCWLogsClient(region)
|
2020-04-25 15:48:20 -05:00
|
|
|
if err != nil {
|
2021-03-08 00:02:49 -06:00
|
|
|
return plugins.DataResponse{}, err
|
2020-04-25 15:48:20 -05:00
|
|
|
}
|
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
result, err := e.executeStartQuery(ctx, logsClient, queryParams, *queryContext.TimeRange)
|
2020-04-25 15:48:20 -05:00
|
|
|
if err != nil {
|
2021-03-08 00:02:49 -06:00
|
|
|
return plugins.DataResponse{}, err
|
2020-04-25 15:48:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
queryParams.Set("queryId", *result.QueryId)
|
|
|
|
|
2020-05-18 05:25:58 -05:00
|
|
|
// Get query results
|
2020-04-25 15:48:20 -05:00
|
|
|
getQueryResultsOutput, err := e.alertQuery(ctx, logsClient, queryContext)
|
|
|
|
if err != nil {
|
2021-03-08 00:02:49 -06:00
|
|
|
return plugins.DataResponse{}, err
|
2020-04-25 15:48:20 -05:00
|
|
|
}
|
|
|
|
|
2020-05-21 09:18:09 -05:00
|
|
|
dataframe, err := logsResultsToDataframes(getQueryResultsOutput)
|
2020-04-25 15:48:20 -05:00
|
|
|
if err != nil {
|
2021-03-08 00:02:49 -06:00
|
|
|
return plugins.DataResponse{}, err
|
2020-04-25 15:48:20 -05:00
|
|
|
}
|
|
|
|
|
2020-05-21 09:18:09 -05:00
|
|
|
statsGroups := queryParams.Get("statsGroups").MustStringArray()
|
|
|
|
if len(statsGroups) > 0 && len(dataframe.Fields) > 0 {
|
|
|
|
groupedFrames, err := groupResults(dataframe, statsGroups)
|
|
|
|
if err != nil {
|
2021-03-08 00:02:49 -06:00
|
|
|
return plugins.DataResponse{}, err
|
2020-05-21 09:18:09 -05:00
|
|
|
}
|
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
response := plugins.DataResponse{
|
|
|
|
Results: make(map[string]plugins.DataQueryResult),
|
2020-05-21 09:18:09 -05:00
|
|
|
}
|
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
response.Results["A"] = plugins.DataQueryResult{
|
|
|
|
RefID: "A",
|
|
|
|
Dataframes: plugins.NewDecodedDataFrames(groupedFrames),
|
2020-05-21 09:18:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
}
|
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
response := plugins.DataResponse{
|
|
|
|
Results: map[string]plugins.DataQueryResult{
|
2020-05-18 05:25:58 -05:00
|
|
|
"A": {
|
2021-03-08 00:02:49 -06:00
|
|
|
RefID: "A",
|
|
|
|
Dataframes: plugins.NewDecodedDataFrames(data.Frames{dataframe}),
|
2020-05-18 05:25:58 -05:00
|
|
|
},
|
|
|
|
},
|
2020-04-25 15:48:20 -05:00
|
|
|
}
|
|
|
|
return response, nil
|
|
|
|
}
|
|
|
|
|
2020-10-12 10:58:58 -05:00
|
|
|
type authType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
authTypeDefault authType = iota
|
|
|
|
authTypeSharedCreds
|
|
|
|
authTypeKeys
|
2021-03-09 13:50:16 -06:00
|
|
|
authTypeEC2IAMRole
|
2020-10-12 10:58:58 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func (at authType) String() string {
|
|
|
|
switch at {
|
|
|
|
case authTypeDefault:
|
|
|
|
return "default"
|
|
|
|
case authTypeSharedCreds:
|
2021-03-09 13:50:16 -06:00
|
|
|
return "credentials"
|
2020-10-12 10:58:58 -05:00
|
|
|
case authTypeKeys:
|
|
|
|
return "keys"
|
2021-03-09 13:50:16 -06:00
|
|
|
case authTypeEC2IAMRole:
|
|
|
|
return "ec2_iam_role"
|
2020-10-12 10:58:58 -05:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("Unrecognized auth type %d", at))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-14 01:23:23 -05:00
|
|
|
func (e *cloudWatchExecutor) getDSInfo(region string) *datasourceInfo {
|
|
|
|
if region == defaultRegion {
|
|
|
|
region = e.DataSource.JsonData.Get("defaultRegion").MustString()
|
|
|
|
}
|
|
|
|
|
2020-10-12 10:58:58 -05:00
|
|
|
atStr := e.DataSource.JsonData.Get("authType").MustString()
|
|
|
|
assumeRoleARN := e.DataSource.JsonData.Get("assumeRoleArn").MustString()
|
2020-07-14 01:23:23 -05:00
|
|
|
externalID := e.DataSource.JsonData.Get("externalId").MustString()
|
2021-02-24 09:05:14 -06:00
|
|
|
endpoint := e.DataSource.JsonData.Get("endpoint").MustString()
|
2020-07-14 01:23:23 -05:00
|
|
|
decrypted := e.DataSource.DecryptedValues()
|
|
|
|
accessKey := decrypted["accessKey"]
|
|
|
|
secretKey := decrypted["secretKey"]
|
|
|
|
|
2020-10-12 10:58:58 -05:00
|
|
|
at := authTypeDefault
|
|
|
|
switch atStr {
|
|
|
|
case "credentials":
|
|
|
|
at = authTypeSharedCreds
|
|
|
|
case "keys":
|
|
|
|
at = authTypeKeys
|
|
|
|
case "default":
|
|
|
|
at = authTypeDefault
|
2021-03-09 13:50:16 -06:00
|
|
|
case "ec2_iam_role":
|
|
|
|
at = authTypeEC2IAMRole
|
2020-10-12 10:58:58 -05:00
|
|
|
case "arn":
|
|
|
|
at = authTypeDefault
|
|
|
|
plog.Warn("Authentication type \"arn\" is deprecated, falling back to default")
|
|
|
|
default:
|
|
|
|
plog.Warn("Unrecognized AWS authentication type", "type", atStr)
|
|
|
|
}
|
|
|
|
|
2020-09-24 11:21:17 -05:00
|
|
|
profile := e.DataSource.JsonData.Get("profile").MustString()
|
|
|
|
if profile == "" {
|
|
|
|
profile = e.DataSource.Database // legacy support
|
|
|
|
}
|
|
|
|
|
2020-07-14 01:23:23 -05:00
|
|
|
return &datasourceInfo{
|
|
|
|
Region: region,
|
2020-09-24 11:21:17 -05:00
|
|
|
Profile: profile,
|
2020-10-12 10:58:58 -05:00
|
|
|
AuthType: at,
|
|
|
|
AssumeRoleARN: assumeRoleARN,
|
2020-07-14 01:23:23 -05:00
|
|
|
ExternalID: externalID,
|
|
|
|
AccessKey: accessKey,
|
|
|
|
SecretKey: secretKey,
|
2021-02-24 09:05:14 -06:00
|
|
|
Endpoint: endpoint,
|
2020-07-14 01:23:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 11:52:22 -05:00
|
|
|
func isTerminated(queryStatus string) bool {
|
|
|
|
return queryStatus == "Complete" || queryStatus == "Cancelled" || queryStatus == "Failed" || queryStatus == "Timeout"
|
|
|
|
}
|
2020-07-14 01:23:23 -05:00
|
|
|
|
2021-01-07 04:36:13 -06:00
|
|
|
// NewCWClient is a CloudWatch client factory.
|
2020-07-23 11:52:22 -05:00
|
|
|
//
|
|
|
|
// Stubbable by tests.
|
2021-01-07 04:36:13 -06:00
|
|
|
var NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI {
|
2020-07-23 11:52:22 -05:00
|
|
|
client := cloudwatch.New(sess)
|
|
|
|
client.Handlers.Send.PushFront(func(r *request.Request) {
|
|
|
|
r.HTTPRequest.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
|
|
|
})
|
2020-07-14 01:23:23 -05:00
|
|
|
|
2020-07-23 11:52:22 -05:00
|
|
|
return client
|
|
|
|
}
|
2020-07-14 01:23:23 -05:00
|
|
|
|
2021-01-07 04:36:13 -06:00
|
|
|
// NewCWLogsClient is a CloudWatch logs client factory.
|
2020-07-23 11:52:22 -05:00
|
|
|
//
|
|
|
|
// Stubbable by tests.
|
2021-01-07 04:36:13 -06:00
|
|
|
var NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
|
2020-07-23 11:52:22 -05:00
|
|
|
client := cloudwatchlogs.New(sess)
|
2020-07-14 01:23:23 -05:00
|
|
|
client.Handlers.Send.PushFront(func(r *request.Request) {
|
|
|
|
r.HTTPRequest.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
|
|
|
})
|
|
|
|
|
2020-07-23 11:52:22 -05:00
|
|
|
return client
|
2020-07-14 01:23:23 -05:00
|
|
|
}
|
|
|
|
|
2020-07-23 11:52:22 -05:00
|
|
|
// EC2 client factory.
|
|
|
|
//
|
|
|
|
// Stubbable by tests.
|
|
|
|
var newEC2Client = func(provider client.ConfigProvider) ec2iface.EC2API {
|
|
|
|
return ec2.New(provider)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RGTA client factory.
|
|
|
|
//
|
|
|
|
// Stubbable by tests.
|
|
|
|
var newRGTAClient = func(provider client.ConfigProvider) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI {
|
|
|
|
return resourcegroupstaggingapi.New(provider)
|
2020-04-25 15:48:20 -05:00
|
|
|
}
|