mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
parent
747bdaf2e7
commit
c863fd3da9
@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) executeAnnotationQuery(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||||
result := &tsdb.Response{
|
result := &tsdb.Response{
|
||||||
Results: make(map[string]*tsdb.QueryResult),
|
Results: make(map[string]*tsdb.QueryResult),
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ func (e *CloudWatchExecutor) executeAnnotationQuery(ctx context.Context, queryCo
|
|||||||
actionPrefix := parameters.Get("actionPrefix").MustString("")
|
actionPrefix := parameters.Get("actionPrefix").MustString("")
|
||||||
alarmNamePrefix := parameters.Get("alarmNamePrefix").MustString("")
|
alarmNamePrefix := parameters.Get("alarmNamePrefix").MustString("")
|
||||||
|
|
||||||
svc, err := e.getClient(region)
|
cli, err := e.getCWClient(region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ func (e *CloudWatchExecutor) executeAnnotationQuery(ctx context.Context, queryCo
|
|||||||
ActionPrefix: aws.String(actionPrefix),
|
ActionPrefix: aws.String(actionPrefix),
|
||||||
AlarmNamePrefix: aws.String(alarmNamePrefix),
|
AlarmNamePrefix: aws.String(alarmNamePrefix),
|
||||||
}
|
}
|
||||||
resp, err := svc.DescribeAlarms(params)
|
resp, err := cli.DescribeAlarms(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errutil.Wrap("failed to call cloudwatch:DescribeAlarms", err)
|
return nil, errutil.Wrap("failed to call cloudwatch:DescribeAlarms", err)
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ func (e *CloudWatchExecutor) executeAnnotationQuery(ctx context.Context, queryCo
|
|||||||
Statistic: aws.String(s),
|
Statistic: aws.String(s),
|
||||||
Period: aws.Int64(period),
|
Period: aws.Int64(period),
|
||||||
}
|
}
|
||||||
resp, err := svc.DescribeAlarmsForMetric(params)
|
resp, err := cli.DescribeAlarmsForMetric(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errutil.Wrap("failed to call cloudwatch:DescribeAlarmsForMetric", err)
|
return nil, errutil.Wrap("failed to call cloudwatch:DescribeAlarmsForMetric", err)
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ func (e *CloudWatchExecutor) executeAnnotationQuery(ctx context.Context, queryCo
|
|||||||
EndDate: aws.Time(endTime),
|
EndDate: aws.Time(endTime),
|
||||||
MaxRecords: aws.Int64(100),
|
MaxRecords: aws.Int64(100),
|
||||||
}
|
}
|
||||||
resp, err := svc.DescribeAlarmHistory(params)
|
resp, err := cli.DescribeAlarmHistory(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errutil.Wrap("failed to call cloudwatch:DescribeAlarmHistory", err)
|
return nil, errutil.Wrap("failed to call cloudwatch:DescribeAlarmHistory", err)
|
||||||
}
|
}
|
||||||
|
@ -9,25 +9,19 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
|
||||||
|
"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/cloudwatchlogs"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||||
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
|
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
"github.com/grafana/grafana/pkg/tsdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CloudWatchExecutor struct {
|
type datasourceInfo struct {
|
||||||
*models.DataSource
|
|
||||||
ec2Svc ec2iface.EC2API
|
|
||||||
rgtaSvc resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI
|
|
||||||
|
|
||||||
logsClientsByRegion map[string](*cloudwatchlogs.CloudWatchLogs)
|
|
||||||
mux sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
type DatasourceInfo struct {
|
|
||||||
Profile string
|
Profile string
|
||||||
Region string
|
Region string
|
||||||
AuthType string
|
AuthType string
|
||||||
@ -40,12 +34,69 @@ type DatasourceInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cloudWatchTSFormat = "2006-01-02 15:04:05.000"
|
const cloudWatchTSFormat = "2006-01-02 15:04:05.000"
|
||||||
|
const defaultRegion = "default"
|
||||||
|
|
||||||
// Constants also defined in datasource/cloudwatch/datasource.ts
|
// Constants also defined in datasource/cloudwatch/datasource.ts
|
||||||
const logIdentifierInternal = "__log__grafana_internal__"
|
const logIdentifierInternal = "__log__grafana_internal__"
|
||||||
const logStreamIdentifierInternal = "__logstream__grafana_internal__"
|
const logStreamIdentifierInternal = "__logstream__grafana_internal__"
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) getLogsClient(region string) (*cloudwatchlogs.CloudWatchLogs, error) {
|
var plog = log.New("tsdb.cloudwatch")
|
||||||
|
var aliasFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tsdb.RegisterTsdbQueryEndpoint("cloudwatch", newcloudWatchExecutor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newcloudWatchExecutor(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
|
||||||
|
e := &cloudWatchExecutor{
|
||||||
|
DataSource: datasource,
|
||||||
|
}
|
||||||
|
|
||||||
|
dsInfo := e.getDSInfo(defaultRegion)
|
||||||
|
defaultLogsClient, err := retrieveLogsClient(dsInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
e.logsClientsByRegion = map[string]*cloudwatchlogs.CloudWatchLogs{
|
||||||
|
dsInfo.Region: defaultLogsClient,
|
||||||
|
defaultRegion: defaultLogsClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cloudWatchExecutor executes CloudWatch requests.
|
||||||
|
type cloudWatchExecutor struct {
|
||||||
|
*models.DataSource
|
||||||
|
ec2Svc ec2iface.EC2API
|
||||||
|
rgtaSvc resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI
|
||||||
|
|
||||||
|
logsClientsByRegion map[string](*cloudwatchlogs.CloudWatchLogs)
|
||||||
|
mux sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cloudWatchExecutor) getCWClient(region string) (*cloudwatch.CloudWatch, error) {
|
||||||
|
datasourceInfo := e.getDSInfo(region)
|
||||||
|
cfg, err := getAwsConfig(datasourceInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sess, err := newSession(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := cloudwatch.New(sess, cfg)
|
||||||
|
|
||||||
|
client.Handlers.Send.PushFront(func(r *request.Request) {
|
||||||
|
r.HTTPRequest.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
||||||
|
})
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cloudWatchExecutor) getCWLogsClient(region string) (*cloudwatchlogs.CloudWatchLogs, error) {
|
||||||
e.mux.Lock()
|
e.mux.Lock()
|
||||||
defer e.mux.Unlock()
|
defer e.mux.Unlock()
|
||||||
|
|
||||||
@ -53,9 +104,8 @@ func (e *CloudWatchExecutor) getLogsClient(region string) (*cloudwatchlogs.Cloud
|
|||||||
return logsClient, nil
|
return logsClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dsInfo := retrieveDsInfo(e.DataSource, region)
|
dsInfo := e.getDSInfo(region)
|
||||||
newLogsClient, err := retrieveLogsClient(dsInfo)
|
newLogsClient, err := retrieveLogsClient(dsInfo)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -65,31 +115,7 @@ func (e *CloudWatchExecutor) getLogsClient(region string) (*cloudwatchlogs.Cloud
|
|||||||
return newLogsClient, nil
|
return newLogsClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCloudWatchExecutor(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
|
func (e *cloudWatchExecutor) alertQuery(ctx context.Context, logsClient *cloudwatchlogs.CloudWatchLogs, queryContext *tsdb.TsdbQuery) (*cloudwatchlogs.GetQueryResultsOutput, error) {
|
||||||
dsInfo := retrieveDsInfo(datasource, "default")
|
|
||||||
defaultLogsClient, err := retrieveLogsClient(dsInfo)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
logsClientsByRegion := make(map[string](*cloudwatchlogs.CloudWatchLogs))
|
|
||||||
logsClientsByRegion[dsInfo.Region] = defaultLogsClient
|
|
||||||
logsClientsByRegion["default"] = defaultLogsClient
|
|
||||||
|
|
||||||
return &CloudWatchExecutor{
|
|
||||||
logsClientsByRegion: logsClientsByRegion,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var plog = log.New("tsdb.cloudwatch")
|
|
||||||
var aliasFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
tsdb.RegisterTsdbQueryEndpoint("cloudwatch", NewCloudWatchExecutor)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) alertQuery(ctx context.Context, logsClient *cloudwatchlogs.CloudWatchLogs, queryContext *tsdb.TsdbQuery) (*cloudwatchlogs.GetQueryResultsOutput, error) {
|
|
||||||
const maxAttempts = 8
|
const maxAttempts = 8
|
||||||
const pollPeriod = 1000 * time.Millisecond
|
const pollPeriod = 1000 * time.Millisecond
|
||||||
|
|
||||||
@ -126,7 +152,8 @@ func (e *CloudWatchExecutor) alertQuery(ctx context.Context, logsClient *cloudwa
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) Query(ctx context.Context, dsInfo *models.DataSource, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
// Query executes a CloudWatch query.
|
||||||
|
func (e *cloudWatchExecutor) Query(ctx context.Context, dsInfo *models.DataSource, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||||
e.DataSource = dsInfo
|
e.DataSource = dsInfo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -163,18 +190,18 @@ func (e *CloudWatchExecutor) Query(ctx context.Context, dsInfo *models.DataSourc
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) executeLogAlertQuery(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
func (e *cloudWatchExecutor) executeLogAlertQuery(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||||
queryParams := queryContext.Queries[0].Model
|
queryParams := queryContext.Queries[0].Model
|
||||||
queryParams.Set("subtype", "StartQuery")
|
queryParams.Set("subtype", "StartQuery")
|
||||||
queryParams.Set("queryString", queryParams.Get("expression").MustString(""))
|
queryParams.Set("queryString", queryParams.Get("expression").MustString(""))
|
||||||
|
|
||||||
region := queryParams.Get("region").MustString("default")
|
region := queryParams.Get("region").MustString(defaultRegion)
|
||||||
if region == "default" {
|
if region == defaultRegion {
|
||||||
region = e.DataSource.JsonData.Get("defaultRegion").MustString()
|
region = e.DataSource.JsonData.Get("defaultRegion").MustString()
|
||||||
queryParams.Set("region", region)
|
queryParams.Set("region", region)
|
||||||
}
|
}
|
||||||
|
|
||||||
logsClient, err := e.getLogsClient(region)
|
logsClient, err := e.getCWLogsClient(region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -227,6 +254,49 @@ func (e *CloudWatchExecutor) executeLogAlertQuery(ctx context.Context, queryCont
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *cloudWatchExecutor) getDSInfo(region string) *datasourceInfo {
|
||||||
|
if region == defaultRegion {
|
||||||
|
region = e.DataSource.JsonData.Get("defaultRegion").MustString()
|
||||||
|
}
|
||||||
|
|
||||||
|
authType := e.DataSource.JsonData.Get("authType").MustString()
|
||||||
|
assumeRoleArn := e.DataSource.JsonData.Get("assumeRoleArn").MustString()
|
||||||
|
externalID := e.DataSource.JsonData.Get("externalId").MustString()
|
||||||
|
decrypted := e.DataSource.DecryptedValues()
|
||||||
|
accessKey := decrypted["accessKey"]
|
||||||
|
secretKey := decrypted["secretKey"]
|
||||||
|
|
||||||
|
return &datasourceInfo{
|
||||||
|
Region: region,
|
||||||
|
Profile: e.DataSource.Database,
|
||||||
|
AuthType: authType,
|
||||||
|
AssumeRoleArn: assumeRoleArn,
|
||||||
|
ExternalID: externalID,
|
||||||
|
AccessKey: accessKey,
|
||||||
|
SecretKey: secretKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func retrieveLogsClient(dsInfo *datasourceInfo) (*cloudwatchlogs.CloudWatchLogs, error) {
|
||||||
|
cfg, err := getAwsConfig(dsInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sess, err := newSession(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := cloudwatchlogs.New(sess, cfg)
|
||||||
|
|
||||||
|
client.Handlers.Send.PushFront(func(r *request.Request) {
|
||||||
|
r.HTTPRequest.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
||||||
|
})
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
func isTerminated(queryStatus string) bool {
|
func isTerminated(queryStatus string) bool {
|
||||||
return queryStatus == "Complete" || queryStatus == "Cancelled" || queryStatus == "Failed" || queryStatus == "Timeout"
|
return queryStatus == "Complete" || queryStatus == "Cancelled" || queryStatus == "Failed" || queryStatus == "Timeout"
|
||||||
}
|
}
|
||||||
|
@ -14,23 +14,18 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||||
"github.com/aws/aws-sdk-go/aws/defaults"
|
"github.com/aws/aws-sdk-go/aws/defaults"
|
||||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
|
||||||
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
|
||||||
"github.com/aws/aws-sdk-go/service/sts"
|
"github.com/aws/aws-sdk-go/service/sts"
|
||||||
"github.com/aws/aws-sdk-go/service/sts/stsiface"
|
"github.com/aws/aws-sdk-go/service/sts/stsiface"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type cache struct {
|
type envelope struct {
|
||||||
credential *credentials.Credentials
|
credentials *credentials.Credentials
|
||||||
expiration *time.Time
|
expiration *time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
var awsCredentialCache = make(map[string]cache)
|
var awsCredsCache = map[string]envelope{}
|
||||||
var credentialCacheLock sync.RWMutex
|
var credsCacheLock sync.RWMutex
|
||||||
|
|
||||||
// Session factory.
|
// Session factory.
|
||||||
// Stubbable by tests.
|
// Stubbable by tests.
|
||||||
@ -50,18 +45,17 @@ var newEC2Metadata = func(p client.ConfigProvider, cfgs ...*aws.Config) *ec2meta
|
|||||||
return ec2metadata.New(p, cfgs...)
|
return ec2metadata.New(p, cfgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCredentials(dsInfo *DatasourceInfo) (*credentials.Credentials, error) {
|
func getCredentials(dsInfo *datasourceInfo) (*credentials.Credentials, error) {
|
||||||
cacheKey := fmt.Sprintf("%s:%s:%s:%s", dsInfo.AuthType, dsInfo.AccessKey, dsInfo.Profile, dsInfo.AssumeRoleArn)
|
cacheKey := fmt.Sprintf("%s:%s:%s:%s", dsInfo.AuthType, dsInfo.AccessKey, dsInfo.Profile, dsInfo.AssumeRoleArn)
|
||||||
credentialCacheLock.RLock()
|
credsCacheLock.RLock()
|
||||||
if _, ok := awsCredentialCache[cacheKey]; ok {
|
if env, ok := awsCredsCache[cacheKey]; ok {
|
||||||
if awsCredentialCache[cacheKey].expiration != nil &&
|
if env.expiration != nil && env.expiration.After(time.Now().UTC()) {
|
||||||
awsCredentialCache[cacheKey].expiration.After(time.Now().UTC()) {
|
result := env.credentials
|
||||||
result := awsCredentialCache[cacheKey].credential
|
credsCacheLock.RUnlock()
|
||||||
credentialCacheLock.RUnlock()
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
credentialCacheLock.RUnlock()
|
credsCacheLock.RUnlock()
|
||||||
|
|
||||||
accessKeyID := ""
|
accessKeyID := ""
|
||||||
secretAccessKey := ""
|
secretAccessKey := ""
|
||||||
@ -135,12 +129,12 @@ func getCredentials(dsInfo *DatasourceInfo) (*credentials.Credentials, error) {
|
|||||||
remoteCredProvider(sess),
|
remoteCredProvider(sess),
|
||||||
})
|
})
|
||||||
|
|
||||||
credentialCacheLock.Lock()
|
credsCacheLock.Lock()
|
||||||
awsCredentialCache[cacheKey] = cache{
|
awsCredsCache[cacheKey] = envelope{
|
||||||
credential: creds,
|
credentials: creds,
|
||||||
expiration: expiration,
|
expiration: expiration,
|
||||||
}
|
}
|
||||||
credentialCacheLock.Unlock()
|
credsCacheLock.Unlock()
|
||||||
|
|
||||||
return creds, nil
|
return creds, nil
|
||||||
}
|
}
|
||||||
@ -178,37 +172,7 @@ func ec2RoleProvider(sess *session.Session) credentials.Provider {
|
|||||||
return &ec2rolecreds.EC2RoleProvider{Client: newEC2Metadata(sess), ExpiryWindow: 5 * time.Minute}
|
return &ec2rolecreds.EC2RoleProvider{Client: newEC2Metadata(sess), ExpiryWindow: 5 * time.Minute}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) getDsInfo(region string) *DatasourceInfo {
|
func getAwsConfig(dsInfo *datasourceInfo) (*aws.Config, error) {
|
||||||
return retrieveDsInfo(e.DataSource, region)
|
|
||||||
}
|
|
||||||
|
|
||||||
func retrieveDsInfo(datasource *models.DataSource, region string) *DatasourceInfo {
|
|
||||||
defaultRegion := datasource.JsonData.Get("defaultRegion").MustString()
|
|
||||||
if region == "default" {
|
|
||||||
region = defaultRegion
|
|
||||||
}
|
|
||||||
|
|
||||||
authType := datasource.JsonData.Get("authType").MustString()
|
|
||||||
assumeRoleArn := datasource.JsonData.Get("assumeRoleArn").MustString()
|
|
||||||
externalID := datasource.JsonData.Get("externalId").MustString()
|
|
||||||
decrypted := datasource.DecryptedValues()
|
|
||||||
accessKey := decrypted["accessKey"]
|
|
||||||
secretKey := decrypted["secretKey"]
|
|
||||||
|
|
||||||
datasourceInfo := &DatasourceInfo{
|
|
||||||
Region: region,
|
|
||||||
Profile: datasource.Database,
|
|
||||||
AuthType: authType,
|
|
||||||
AssumeRoleArn: assumeRoleArn,
|
|
||||||
ExternalID: externalID,
|
|
||||||
AccessKey: accessKey,
|
|
||||||
SecretKey: secretKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
return datasourceInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAwsConfig(dsInfo *DatasourceInfo) (*aws.Config, error) {
|
|
||||||
creds, err := getCredentials(dsInfo)
|
creds, err := getCredentials(dsInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -221,44 +185,3 @@ func getAwsConfig(dsInfo *DatasourceInfo) (*aws.Config, error) {
|
|||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) getClient(region string) (*cloudwatch.CloudWatch, error) {
|
|
||||||
datasourceInfo := e.getDsInfo(region)
|
|
||||||
cfg, err := getAwsConfig(datasourceInfo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sess, err := newSession(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
client := cloudwatch.New(sess, cfg)
|
|
||||||
|
|
||||||
client.Handlers.Send.PushFront(func(r *request.Request) {
|
|
||||||
r.HTTPRequest.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
|
||||||
})
|
|
||||||
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func retrieveLogsClient(datasourceInfo *DatasourceInfo) (*cloudwatchlogs.CloudWatchLogs, error) {
|
|
||||||
cfg, err := getAwsConfig(datasourceInfo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sess, err := newSession(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
client := cloudwatchlogs.New(sess, cfg)
|
|
||||||
|
|
||||||
client.Handlers.Send.PushFront(func(r *request.Request) {
|
|
||||||
r.HTTPRequest.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
|
||||||
})
|
|
||||||
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
|
@ -87,7 +87,7 @@ func TestGetCredentials_ARNAuthType(t *testing.T) {
|
|||||||
}, nil).
|
}, nil).
|
||||||
Times(1)
|
Times(1)
|
||||||
|
|
||||||
creds, err := getCredentials(&DatasourceInfo{
|
creds, err := getCredentials(&datasourceInfo{
|
||||||
AuthType: "arn",
|
AuthType: "arn",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -113,7 +113,7 @@ func TestGetCredentials_ARNAuthType(t *testing.T) {
|
|||||||
}, nil).
|
}, nil).
|
||||||
Times(1)
|
Times(1)
|
||||||
|
|
||||||
creds, err := getCredentials(&DatasourceInfo{
|
creds, err := getCredentials(&datasourceInfo{
|
||||||
AuthType: "arn",
|
AuthType: "arn",
|
||||||
ExternalID: "external-id",
|
ExternalID: "external-id",
|
||||||
})
|
})
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) executeRequest(ctx context.Context, client cloudWatchClient, metricDataInput *cloudwatch.GetMetricDataInput) ([]*cloudwatch.GetMetricDataOutput, error) {
|
func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client cloudWatchClient, metricDataInput *cloudwatch.GetMetricDataInput) ([]*cloudwatch.GetMetricDataOutput, error) {
|
||||||
mdo := make([]*cloudwatch.GetMetricDataOutput, 0)
|
mdo := make([]*cloudwatch.GetMetricDataOutput, 0)
|
||||||
|
|
||||||
nextToken := ""
|
nextToken := ""
|
||||||
|
@ -35,7 +35,7 @@ func (client *cloudWatchFakeClient) GetMetricDataWithContext(ctx aws.Context, in
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetMetricDataExecutorTest(t *testing.T) {
|
func TestGetMetricDataExecutorTest(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{}
|
executor := &cloudWatchExecutor{}
|
||||||
inputs := &cloudwatch.GetMetricDataInput{MetricDataQueries: []*cloudwatch.MetricDataQuery{}}
|
inputs := &cloudwatch.GetMetricDataInput{MetricDataQueries: []*cloudwatch.MetricDataQuery{}}
|
||||||
res, err := executor.executeRequest(context.Background(), &cloudWatchFakeClient{}, inputs)
|
res, err := executor.executeRequest(context.Background(), &cloudWatchFakeClient{}, inputs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) executeLogActions(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
func (e *cloudWatchExecutor) executeLogActions(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||||
resultChan := make(chan *tsdb.QueryResult, len(queryContext.Queries))
|
resultChan := make(chan *tsdb.QueryResult, len(queryContext.Queries))
|
||||||
eg, ectx := errgroup.WithContext(ctx)
|
eg, ectx := errgroup.WithContext(ctx)
|
||||||
|
|
||||||
@ -73,13 +73,13 @@ func (e *CloudWatchExecutor) executeLogActions(ctx context.Context, queryContext
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) executeLogAction(ctx context.Context, queryContext *tsdb.TsdbQuery, query *tsdb.Query) (*data.Frame, error) {
|
func (e *cloudWatchExecutor) executeLogAction(ctx context.Context, queryContext *tsdb.TsdbQuery, query *tsdb.Query) (*data.Frame, error) {
|
||||||
parameters := query.Model
|
parameters := query.Model
|
||||||
subType := query.Model.Get("subtype").MustString()
|
subType := query.Model.Get("subtype").MustString()
|
||||||
|
|
||||||
defaultRegion := e.DataSource.JsonData.Get("defaultRegion").MustString()
|
defaultRegion := e.DataSource.JsonData.Get("defaultRegion").MustString()
|
||||||
region := parameters.Get("region").MustString(defaultRegion)
|
region := parameters.Get("region").MustString(defaultRegion)
|
||||||
logsClient, err := e.getLogsClient(region)
|
logsClient, err := e.getCWLogsClient(region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -100,7 +100,6 @@ func (e *CloudWatchExecutor) executeLogAction(ctx context.Context, queryContext
|
|||||||
case "GetLogEvents":
|
case "GetLogEvents":
|
||||||
data, err = e.handleGetLogEvents(ctx, logsClient, parameters)
|
data, err = e.handleGetLogEvents(ctx, logsClient, parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -108,7 +107,8 @@ func (e *CloudWatchExecutor) executeLogAction(ctx context.Context, queryContext
|
|||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters *simplejson.Json) (*data.Frame, error) {
|
func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
|
||||||
|
parameters *simplejson.Json) (*data.Frame, error) {
|
||||||
queryRequest := &cloudwatchlogs.GetLogEventsInput{
|
queryRequest := &cloudwatchlogs.GetLogEventsInput{
|
||||||
Limit: aws.Int64(parameters.Get("limit").MustInt64(10)),
|
Limit: aws.Int64(parameters.Get("limit").MustInt64(10)),
|
||||||
StartFromHead: aws.Bool(parameters.Get("startFromHead").MustBool(false)),
|
StartFromHead: aws.Bool(parameters.Get("startFromHead").MustBool(false)),
|
||||||
@ -159,7 +159,8 @@ func (e *CloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient
|
|||||||
return data.NewFrame("logEvents", timestampField, messageField), nil
|
return data.NewFrame("logEvents", timestampField, messageField), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleDescribeLogGroups(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters *simplejson.Json) (*data.Frame, error) {
|
func (e *cloudWatchExecutor) handleDescribeLogGroups(ctx context.Context,
|
||||||
|
logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters *simplejson.Json) (*data.Frame, error) {
|
||||||
logGroupNamePrefix := parameters.Get("logGroupNamePrefix").MustString("")
|
logGroupNamePrefix := parameters.Get("logGroupNamePrefix").MustString("")
|
||||||
|
|
||||||
var response *cloudwatchlogs.DescribeLogGroupsOutput = nil
|
var response *cloudwatchlogs.DescribeLogGroupsOutput = nil
|
||||||
@ -189,7 +190,8 @@ func (e *CloudWatchExecutor) handleDescribeLogGroups(ctx context.Context, logsCl
|
|||||||
return frame, nil
|
return frame, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters *simplejson.Json, timeRange *tsdb.TimeRange) (*cloudwatchlogs.StartQueryOutput, error) {
|
func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
|
||||||
|
parameters *simplejson.Json, timeRange *tsdb.TimeRange) (*cloudwatchlogs.StartQueryOutput, error) {
|
||||||
startTime, err := timeRange.ParseFrom()
|
startTime, err := timeRange.ParseFrom()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -224,7 +226,8 @@ func (e *CloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient c
|
|||||||
return logsClient.StartQueryWithContext(ctx, startQueryInput)
|
return logsClient.StartQueryWithContext(ctx, startQueryInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters *simplejson.Json, timeRange *tsdb.TimeRange, refID string) (*data.Frame, error) {
|
func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
|
||||||
|
parameters *simplejson.Json, timeRange *tsdb.TimeRange, refID string) (*data.Frame, error) {
|
||||||
startQueryResponse, err := e.executeStartQuery(ctx, logsClient, parameters, timeRange)
|
startQueryResponse, err := e.executeStartQuery(ctx, logsClient, parameters, timeRange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -244,7 +247,8 @@ func (e *CloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient cl
|
|||||||
return dataFrame, nil
|
return dataFrame, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters *simplejson.Json) (*cloudwatchlogs.StopQueryOutput, error) {
|
func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
|
||||||
|
parameters *simplejson.Json) (*cloudwatchlogs.StopQueryOutput, error) {
|
||||||
queryInput := &cloudwatchlogs.StopQueryInput{
|
queryInput := &cloudwatchlogs.StopQueryInput{
|
||||||
QueryId: aws.String(parameters.Get("queryId").MustString()),
|
QueryId: aws.String(parameters.Get("queryId").MustString()),
|
||||||
}
|
}
|
||||||
@ -264,7 +268,8 @@ func (e *CloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient cl
|
|||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters *simplejson.Json) (*data.Frame, error) {
|
func (e *cloudWatchExecutor) handleStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
|
||||||
|
parameters *simplejson.Json) (*data.Frame, error) {
|
||||||
response, err := e.executeStopQuery(ctx, logsClient, parameters)
|
response, err := e.executeStopQuery(ctx, logsClient, parameters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -274,7 +279,8 @@ func (e *CloudWatchExecutor) handleStopQuery(ctx context.Context, logsClient clo
|
|||||||
return dataFrame, nil
|
return dataFrame, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) executeGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters *simplejson.Json) (*cloudwatchlogs.GetQueryResultsOutput, error) {
|
func (e *cloudWatchExecutor) executeGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
|
||||||
|
parameters *simplejson.Json) (*cloudwatchlogs.GetQueryResultsOutput, error) {
|
||||||
queryInput := &cloudwatchlogs.GetQueryResultsInput{
|
queryInput := &cloudwatchlogs.GetQueryResultsInput{
|
||||||
QueryId: aws.String(parameters.Get("queryId").MustString()),
|
QueryId: aws.String(parameters.Get("queryId").MustString()),
|
||||||
}
|
}
|
||||||
@ -282,7 +288,8 @@ func (e *CloudWatchExecutor) executeGetQueryResults(ctx context.Context, logsCli
|
|||||||
return logsClient.GetQueryResultsWithContext(ctx, queryInput)
|
return logsClient.GetQueryResultsWithContext(ctx, queryInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters *simplejson.Json, refID string) (*data.Frame, error) {
|
func (e *cloudWatchExecutor) handleGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
|
||||||
|
parameters *simplejson.Json, refID string) (*data.Frame, error) {
|
||||||
getQueryResultsOutput, err := e.executeGetQueryResults(ctx, logsClient, parameters)
|
getQueryResultsOutput, err := e.executeGetQueryResults(ctx, logsClient, parameters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -299,7 +306,8 @@ func (e *CloudWatchExecutor) handleGetQueryResults(ctx context.Context, logsClie
|
|||||||
return dataFrame, nil
|
return dataFrame, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleGetLogGroupFields(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, parameters *simplejson.Json, refID string) (*data.Frame, error) {
|
func (e *cloudWatchExecutor) handleGetLogGroupFields(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
|
||||||
|
parameters *simplejson.Json, refID string) (*data.Frame, error) {
|
||||||
queryInput := &cloudwatchlogs.GetLogGroupFieldsInput{
|
queryInput := &cloudwatchlogs.GetLogGroupFieldsInput{
|
||||||
LogGroupName: aws.String(parameters.Get("logGroupName").MustString()),
|
LogGroupName: aws.String(parameters.Get("logGroupName").MustString()),
|
||||||
Time: aws.Int64(parameters.Get("time").MustInt64()),
|
Time: aws.Int64(parameters.Get("time").MustInt64()),
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
//***
|
//***
|
||||||
|
|
||||||
func TestHandleDescribeLogGroups_WhenLogGroupNamePrefixIsEmpty(t *testing.T) {
|
func TestHandleDescribeLogGroups_WhenLogGroupNamePrefixIsEmpty(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{}
|
executor := &cloudWatchExecutor{}
|
||||||
|
|
||||||
logsClient := &FakeLogsClient{
|
logsClient := &FakeLogsClient{
|
||||||
Config: aws.Config{
|
Config: aws.Config{
|
||||||
@ -43,7 +43,7 @@ func TestHandleDescribeLogGroups_WhenLogGroupNamePrefixIsEmpty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleDescribeLogGroups_WhenLogGroupNamePrefixIsNotEmpty(t *testing.T) {
|
func TestHandleDescribeLogGroups_WhenLogGroupNamePrefixIsNotEmpty(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{}
|
executor := &cloudWatchExecutor{}
|
||||||
|
|
||||||
logsClient := &FakeLogsClient{
|
logsClient := &FakeLogsClient{
|
||||||
Config: aws.Config{
|
Config: aws.Config{
|
||||||
@ -64,7 +64,7 @@ func TestHandleDescribeLogGroups_WhenLogGroupNamePrefixIsNotEmpty(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleGetLogGroupFields_WhenLogGroupNamePrefixIsNotEmpty(t *testing.T) {
|
func TestHandleGetLogGroupFields_WhenLogGroupNamePrefixIsNotEmpty(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{}
|
executor := &cloudWatchExecutor{}
|
||||||
|
|
||||||
logsClient := &FakeLogsClient{
|
logsClient := &FakeLogsClient{
|
||||||
Config: aws.Config{
|
Config: aws.Config{
|
||||||
@ -89,7 +89,7 @@ func TestHandleGetLogGroupFields_WhenLogGroupNamePrefixIsNotEmpty(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestExecuteStartQuery(t *testing.T) {
|
func TestExecuteStartQuery(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{}
|
executor := &cloudWatchExecutor{}
|
||||||
|
|
||||||
logsClient := &FakeLogsClient{
|
logsClient := &FakeLogsClient{
|
||||||
Config: aws.Config{
|
Config: aws.Config{
|
||||||
@ -117,7 +117,7 @@ func TestExecuteStartQuery(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleStartQuery(t *testing.T) {
|
func TestHandleStartQuery(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{}
|
executor := &cloudWatchExecutor{}
|
||||||
|
|
||||||
logsClient := &FakeLogsClient{
|
logsClient := &FakeLogsClient{
|
||||||
Config: aws.Config{
|
Config: aws.Config{
|
||||||
@ -152,7 +152,7 @@ func TestHandleStartQuery(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleStopQuery(t *testing.T) {
|
func TestHandleStopQuery(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{}
|
executor := &cloudWatchExecutor{}
|
||||||
|
|
||||||
logsClient := &FakeLogsClient{
|
logsClient := &FakeLogsClient{
|
||||||
Config: aws.Config{
|
Config: aws.Config{
|
||||||
@ -174,7 +174,7 @@ func TestHandleStopQuery(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleGetQueryResults(t *testing.T) {
|
func TestHandleGetQueryResults(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{}
|
executor := &cloudWatchExecutor{}
|
||||||
|
|
||||||
logsClient := &FakeLogsClient{
|
logsClient := &FakeLogsClient{
|
||||||
Config: aws.Config{
|
Config: aws.Config{
|
||||||
|
@ -7,7 +7,8 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) buildMetricDataInput(startTime time.Time, endTime time.Time, queries map[string]*cloudWatchQuery) (*cloudwatch.GetMetricDataInput, error) {
|
func (e *cloudWatchExecutor) buildMetricDataInput(startTime time.Time, endTime time.Time,
|
||||||
|
queries map[string]*cloudWatchQuery) (*cloudwatch.GetMetricDataInput, error) {
|
||||||
metricDataInput := &cloudwatch.GetMetricDataInput{
|
metricDataInput := &cloudwatch.GetMetricDataInput{
|
||||||
StartTime: aws.Time(startTime),
|
StartTime: aws.Time(startTime),
|
||||||
EndTime: aws.Time(endTime),
|
EndTime: aws.Time(endTime),
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) buildMetricDataQuery(query *cloudWatchQuery) (*cloudwatch.MetricDataQuery, error) {
|
func (e *cloudWatchExecutor) buildMetricDataQuery(query *cloudWatchQuery) (*cloudwatch.MetricDataQuery, error) {
|
||||||
mdq := &cloudwatch.MetricDataQuery{
|
mdq := &cloudwatch.MetricDataQuery{
|
||||||
Id: aws.String(query.Id),
|
Id: aws.String(query.Id),
|
||||||
ReturnData: aws.Bool(query.ReturnData),
|
ReturnData: aws.Bool(query.ReturnData),
|
||||||
|
@ -21,18 +21,26 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/tsdb"
|
"github.com/grafana/grafana/pkg/tsdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Known AWS regions.
|
||||||
|
var knownRegions = []string{
|
||||||
|
"ap-east-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-south-1", "ap-southeast-1",
|
||||||
|
"ap-southeast-2", "ca-central-1", "cn-north-1", "cn-northwest-1", "eu-central-1", "eu-north-1", "eu-west-1",
|
||||||
|
"eu-west-2", "eu-west-3", "me-south-1", "sa-east-1", "us-east-1", "us-east-2", "us-gov-east-1", "us-gov-west-1",
|
||||||
|
"us-iso-east-1", "us-isob-east-1", "us-west-1", "us-west-2",
|
||||||
|
}
|
||||||
|
|
||||||
type suggestData struct {
|
type suggestData struct {
|
||||||
Text string
|
Text string
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CustomMetricsCache struct {
|
type customMetricsCache struct {
|
||||||
Expire time.Time
|
Expire time.Time
|
||||||
Cache []string
|
Cache []string
|
||||||
}
|
}
|
||||||
|
|
||||||
var customMetricsMetricsMap = make(map[string]map[string]map[string]*CustomMetricsCache)
|
var customMetricsMetricsMap = make(map[string]map[string]map[string]*customMetricsCache)
|
||||||
var customMetricsDimensionsMap = make(map[string]map[string]map[string]*CustomMetricsCache)
|
var customMetricsDimensionsMap = make(map[string]map[string]map[string]*customMetricsCache)
|
||||||
var metricsMap = map[string][]string{
|
var metricsMap = map[string][]string{
|
||||||
"AWS/ACMPrivateCA": {"CRLGenerated", "Failure", "MisconfiguredCRLBucket", "Success", "Time"},
|
"AWS/ACMPrivateCA": {"CRLGenerated", "Failure", "MisconfiguredCRLBucket", "Success", "Time"},
|
||||||
"AWS/AmazonMQ": {"ConsumerCount", "CpuCreditBalance", "CpuUtilization", "CurrentConnectionsCount", "DequeueCount", "DispatchCount", "EnqueueCount", "EnqueueTime", "ExpiredCount", "HeapUsage", "InflightCount", "JournalFilesForFastRecovery", "JournalFilesForFullRecovery", "MemoryUsage", "NetworkIn", "NetworkOut", "OpenTransactionsCount", "ProducerCount", "QueueSize", "StorePercentUsage", "TotalConsumerCount", "TotalMessageCount", "TotalProducerCount"},
|
"AWS/AmazonMQ": {"ConsumerCount", "CpuCreditBalance", "CpuUtilization", "CurrentConnectionsCount", "DequeueCount", "DispatchCount", "EnqueueCount", "EnqueueTime", "ExpiredCount", "HeapUsage", "InflightCount", "JournalFilesForFastRecovery", "JournalFilesForFullRecovery", "MemoryUsage", "NetworkIn", "NetworkOut", "OpenTransactionsCount", "ProducerCount", "QueueSize", "StorePercentUsage", "TotalConsumerCount", "TotalMessageCount", "TotalProducerCount"},
|
||||||
@ -222,7 +230,7 @@ var dimensionsMap = map[string][]string{
|
|||||||
|
|
||||||
var regionCache sync.Map
|
var regionCache sync.Map
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) executeMetricFindQuery(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
func (e *cloudWatchExecutor) executeMetricFindQuery(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||||
firstQuery := queryContext.Queries[0]
|
firstQuery := queryContext.Queries[0]
|
||||||
|
|
||||||
parameters := firstQuery.Model
|
parameters := firstQuery.Model
|
||||||
@ -301,8 +309,9 @@ func parseMultiSelectValue(input string) []string {
|
|||||||
|
|
||||||
// Whenever this list is updated, the frontend list should also be updated.
|
// 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
|
// Please update the region list in public/app/plugins/datasource/cloudwatch/partials/config.html
|
||||||
func (e *CloudWatchExecutor) handleGetRegions(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
func (e *cloudWatchExecutor) handleGetRegions(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
||||||
dsInfo := e.getDsInfo("default")
|
const region = "default"
|
||||||
|
dsInfo := e.getDSInfo(region)
|
||||||
profile := dsInfo.Profile
|
profile := dsInfo.Profile
|
||||||
if cache, ok := regionCache.Load(profile); ok {
|
if cache, ok := regionCache.Load(profile); ok {
|
||||||
if cache2, ok2 := cache.([]suggestData); ok2 {
|
if cache2, ok2 := cache.([]suggestData); ok2 {
|
||||||
@ -310,15 +319,11 @@ func (e *CloudWatchExecutor) handleGetRegions(ctx context.Context, parameters *s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
regions := []string{
|
|
||||||
"ap-east-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-south-1", "ap-southeast-1", "ap-southeast-2", "ca-central-1",
|
|
||||||
"eu-central-1", "eu-north-1", "eu-west-1", "eu-west-2", "eu-west-3", "me-south-1", "sa-east-1", "us-east-1", "us-east-2", "us-west-1", "us-west-2",
|
|
||||||
"cn-north-1", "cn-northwest-1", "us-gov-east-1", "us-gov-west-1", "us-isob-east-1", "us-iso-east-1",
|
|
||||||
}
|
|
||||||
err := e.ensureClientSession("default")
|
err := e.ensureClientSession("default")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
regions := knownRegions
|
||||||
r, err := e.ec2Svc.DescribeRegions(&ec2.DescribeRegionsInput{})
|
r, err := e.ec2Svc.DescribeRegions(&ec2.DescribeRegionsInput{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// ignore error for backward compatibility
|
// ignore error for backward compatibility
|
||||||
@ -350,7 +355,7 @@ func (e *CloudWatchExecutor) handleGetRegions(ctx context.Context, parameters *s
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleGetNamespaces(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
func (e *cloudWatchExecutor) handleGetNamespaces(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
||||||
keys := []string{}
|
keys := []string{}
|
||||||
for key := range metricsMap {
|
for key := range metricsMap {
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
@ -369,7 +374,7 @@ func (e *CloudWatchExecutor) handleGetNamespaces(ctx context.Context, parameters
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleGetMetrics(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
func (e *cloudWatchExecutor) handleGetMetrics(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
||||||
region := parameters.Get("region").MustString()
|
region := parameters.Get("region").MustString()
|
||||||
namespace := parameters.Get("namespace").MustString()
|
namespace := parameters.Get("namespace").MustString()
|
||||||
|
|
||||||
@ -381,10 +386,10 @@ func (e *CloudWatchExecutor) handleGetMetrics(ctx context.Context, parameters *s
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
dsInfo := e.getDsInfo(region)
|
dsInfo := e.getDSInfo(region)
|
||||||
dsInfo.Namespace = namespace
|
dsInfo.Namespace = namespace
|
||||||
|
|
||||||
if namespaceMetrics, err = getMetricsForCustomMetrics(dsInfo, getAllMetrics); err != nil {
|
if namespaceMetrics, err = e.getMetricsForCustomMetrics(region, e.getAllMetrics); err != nil {
|
||||||
return nil, errors.New("Unable to call AWS API")
|
return nil, errors.New("Unable to call AWS API")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -398,7 +403,7 @@ func (e *CloudWatchExecutor) handleGetMetrics(ctx context.Context, parameters *s
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleGetDimensions(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
func (e *cloudWatchExecutor) handleGetDimensions(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
||||||
region := parameters.Get("region").MustString()
|
region := parameters.Get("region").MustString()
|
||||||
namespace := parameters.Get("namespace").MustString()
|
namespace := parameters.Get("namespace").MustString()
|
||||||
|
|
||||||
@ -410,10 +415,10 @@ func (e *CloudWatchExecutor) handleGetDimensions(ctx context.Context, parameters
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
dsInfo := e.getDsInfo(region)
|
dsInfo := e.getDSInfo(region)
|
||||||
dsInfo.Namespace = namespace
|
dsInfo.Namespace = namespace
|
||||||
|
|
||||||
if dimensionValues, err = getDimensionsForCustomMetrics(dsInfo, getAllMetrics); err != nil {
|
if dimensionValues, err = e.getDimensionsForCustomMetrics(region, e.getAllMetrics); err != nil {
|
||||||
return nil, errors.New("Unable to call AWS API")
|
return nil, errors.New("Unable to call AWS API")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -427,7 +432,7 @@ func (e *CloudWatchExecutor) handleGetDimensions(ctx context.Context, parameters
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleGetDimensionValues(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
func (e *cloudWatchExecutor) handleGetDimensionValues(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
||||||
region := parameters.Get("region").MustString()
|
region := parameters.Get("region").MustString()
|
||||||
namespace := parameters.Get("namespace").MustString()
|
namespace := parameters.Get("namespace").MustString()
|
||||||
metricName := parameters.Get("metricName").MustString()
|
metricName := parameters.Get("metricName").MustString()
|
||||||
@ -478,9 +483,9 @@ func (e *CloudWatchExecutor) handleGetDimensionValues(ctx context.Context, param
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) ensureClientSession(region string) error {
|
func (e *cloudWatchExecutor) ensureClientSession(region string) error {
|
||||||
if e.ec2Svc == nil {
|
if e.ec2Svc == nil {
|
||||||
dsInfo := e.getDsInfo(region)
|
dsInfo := e.getDSInfo(region)
|
||||||
cfg, err := getAwsConfig(dsInfo)
|
cfg, err := getAwsConfig(dsInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to call ec2:getAwsConfig, %w", err)
|
return fmt.Errorf("Failed to call ec2:getAwsConfig, %w", err)
|
||||||
@ -494,7 +499,7 @@ func (e *CloudWatchExecutor) ensureClientSession(region string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleGetEbsVolumeIds(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
func (e *cloudWatchExecutor) handleGetEbsVolumeIds(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
||||||
region := parameters.Get("region").MustString()
|
region := parameters.Get("region").MustString()
|
||||||
instanceId := parameters.Get("instanceId").MustString()
|
instanceId := parameters.Get("instanceId").MustString()
|
||||||
|
|
||||||
@ -521,7 +526,7 @@ func (e *CloudWatchExecutor) handleGetEbsVolumeIds(ctx context.Context, paramete
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
||||||
region := parameters.Get("region").MustString()
|
region := parameters.Get("region").MustString()
|
||||||
attributeName := parameters.Get("attributeName").MustString()
|
attributeName := parameters.Get("attributeName").MustString()
|
||||||
filterJson := parameters.Get("filters").MustMap()
|
filterJson := parameters.Get("filters").MustMap()
|
||||||
@ -529,15 +534,15 @@ func (e *CloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context,
|
|||||||
var filters []*ec2.Filter
|
var filters []*ec2.Filter
|
||||||
for k, v := range filterJson {
|
for k, v := range filterJson {
|
||||||
if vv, ok := v.([]interface{}); ok {
|
if vv, ok := v.([]interface{}); ok {
|
||||||
var vvvvv []*string
|
var values []*string
|
||||||
for _, vvv := range vv {
|
for _, vvv := range vv {
|
||||||
if vvvv, ok := vvv.(string); ok {
|
if vvvv, ok := vvv.(string); ok {
|
||||||
vvvvv = append(vvvvv, &vvvv)
|
values = append(values, &vvvv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filters = append(filters, &ec2.Filter{
|
filters = append(filters, &ec2.Filter{
|
||||||
Name: aws.String(k),
|
Name: aws.String(k),
|
||||||
Values: vvvvv,
|
Values: values,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -605,9 +610,9 @@ func (e *CloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context,
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) ensureRGTAClientSession(region string) error {
|
func (e *cloudWatchExecutor) ensureRGTAClientSession(region string) error {
|
||||||
if e.rgtaSvc == nil {
|
if e.rgtaSvc == nil {
|
||||||
dsInfo := e.getDsInfo(region)
|
dsInfo := e.getDSInfo(region)
|
||||||
cfg, err := getAwsConfig(dsInfo)
|
cfg, err := getAwsConfig(dsInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to call ec2:getAwsConfig, %w", err)
|
return fmt.Errorf("Failed to call ec2:getAwsConfig, %w", err)
|
||||||
@ -621,7 +626,7 @@ func (e *CloudWatchExecutor) ensureRGTAClientSession(region string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) handleGetResourceArns(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
||||||
region := parameters.Get("region").MustString()
|
region := parameters.Get("region").MustString()
|
||||||
resourceType := parameters.Get("resourceType").MustString()
|
resourceType := parameters.Get("resourceType").MustString()
|
||||||
filterJson := parameters.Get("tags").MustMap()
|
filterJson := parameters.Get("tags").MustMap()
|
||||||
@ -634,15 +639,15 @@ func (e *CloudWatchExecutor) handleGetResourceArns(ctx context.Context, paramete
|
|||||||
var filters []*resourcegroupstaggingapi.TagFilter
|
var filters []*resourcegroupstaggingapi.TagFilter
|
||||||
for k, v := range filterJson {
|
for k, v := range filterJson {
|
||||||
if vv, ok := v.([]interface{}); ok {
|
if vv, ok := v.([]interface{}); ok {
|
||||||
var vvvvv []*string
|
var values []*string
|
||||||
for _, vvv := range vv {
|
for _, vvv := range vv {
|
||||||
if vvvv, ok := vvv.(string); ok {
|
if vvvv, ok := vvv.(string); ok {
|
||||||
vvvvv = append(vvvvv, &vvvv)
|
values = append(values, &vvvv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filters = append(filters, &resourcegroupstaggingapi.TagFilter{
|
filters = append(filters, &resourcegroupstaggingapi.TagFilter{
|
||||||
Key: aws.String(k),
|
Key: aws.String(k),
|
||||||
Values: vvvvv,
|
Values: values,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -664,8 +669,8 @@ func (e *CloudWatchExecutor) handleGetResourceArns(ctx context.Context, paramete
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) cloudwatchListMetrics(region string, namespace string, metricName string, dimensions []*cloudwatch.DimensionFilter) (*cloudwatch.ListMetricsOutput, error) {
|
func (e *cloudWatchExecutor) cloudwatchListMetrics(region string, namespace string, metricName string, dimensions []*cloudwatch.DimensionFilter) (*cloudwatch.ListMetricsOutput, error) {
|
||||||
svc, err := e.getClient(region)
|
svc, err := e.getCWClient(region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -695,57 +700,50 @@ func (e *CloudWatchExecutor) cloudwatchListMetrics(region string, namespace stri
|
|||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) ec2DescribeInstances(region string, filters []*ec2.Filter, instanceIds []*string) (*ec2.DescribeInstancesOutput, error) {
|
func (e *cloudWatchExecutor) ec2DescribeInstances(region string, filters []*ec2.Filter, instanceIds []*string) (*ec2.DescribeInstancesOutput, error) {
|
||||||
params := &ec2.DescribeInstancesInput{
|
params := &ec2.DescribeInstancesInput{
|
||||||
Filters: filters,
|
Filters: filters,
|
||||||
InstanceIds: instanceIds,
|
InstanceIds: instanceIds,
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp ec2.DescribeInstancesOutput
|
var resp ec2.DescribeInstancesOutput
|
||||||
err := e.ec2Svc.DescribeInstancesPages(params,
|
if err := e.ec2Svc.DescribeInstancesPages(params, func(page *ec2.DescribeInstancesOutput, lastPage bool) bool {
|
||||||
func(page *ec2.DescribeInstancesOutput, lastPage bool) bool {
|
resp.Reservations = append(resp.Reservations, page.Reservations...)
|
||||||
reservations, _ := awsutil.ValuesAtPath(page, "Reservations")
|
return !lastPage
|
||||||
for _, reservation := range reservations {
|
}); err != nil {
|
||||||
resp.Reservations = append(resp.Reservations, reservation.(*ec2.Reservation))
|
return nil, fmt.Errorf("failed to call ec2:DescribeInstances, %w", err)
|
||||||
}
|
|
||||||
return !lastPage
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to call ec2:DescribeInstances, %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) resourceGroupsGetResources(region string, filters []*resourcegroupstaggingapi.TagFilter, resourceTypes []*string) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
|
func (e *cloudWatchExecutor) resourceGroupsGetResources(region string, filters []*resourcegroupstaggingapi.TagFilter,
|
||||||
|
resourceTypes []*string) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
|
||||||
params := &resourcegroupstaggingapi.GetResourcesInput{
|
params := &resourcegroupstaggingapi.GetResourcesInput{
|
||||||
ResourceTypeFilters: resourceTypes,
|
ResourceTypeFilters: resourceTypes,
|
||||||
TagFilters: filters,
|
TagFilters: filters,
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp resourcegroupstaggingapi.GetResourcesOutput
|
var resp resourcegroupstaggingapi.GetResourcesOutput
|
||||||
err := e.rgtaSvc.GetResourcesPages(params,
|
if err := e.rgtaSvc.GetResourcesPages(params,
|
||||||
func(page *resourcegroupstaggingapi.GetResourcesOutput, lastPage bool) bool {
|
func(page *resourcegroupstaggingapi.GetResourcesOutput, lastPage bool) bool {
|
||||||
resources, _ := awsutil.ValuesAtPath(page, "ResourceTagMappingList")
|
resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, page.ResourceTagMappingList...)
|
||||||
for _, resource := range resources {
|
|
||||||
resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, resource.(*resourcegroupstaggingapi.ResourceTagMapping))
|
|
||||||
}
|
|
||||||
return !lastPage
|
return !lastPage
|
||||||
})
|
}); err != nil {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("failed to call tags:GetResources, %w", err)
|
||||||
return nil, fmt.Errorf("Failed to call tags:GetResources, %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllMetrics(cwData *DatasourceInfo) (cloudwatch.ListMetricsOutput, error) {
|
func (e *cloudWatchExecutor) getAllMetrics(region string) (cloudwatch.ListMetricsOutput, error) {
|
||||||
creds, err := getCredentials(cwData)
|
dsInfo := e.getDSInfo(region)
|
||||||
|
creds, err := getCredentials(dsInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cloudwatch.ListMetricsOutput{}, err
|
return cloudwatch.ListMetricsOutput{}, err
|
||||||
}
|
}
|
||||||
cfg := &aws.Config{
|
cfg := &aws.Config{
|
||||||
Region: aws.String(cwData.Region),
|
Region: aws.String(dsInfo.Region),
|
||||||
Credentials: creds,
|
Credentials: creds,
|
||||||
}
|
}
|
||||||
sess, err := session.NewSession(cfg)
|
sess, err := session.NewSession(cfg)
|
||||||
@ -755,7 +753,7 @@ func getAllMetrics(cwData *DatasourceInfo) (cloudwatch.ListMetricsOutput, error)
|
|||||||
svc := cloudwatch.New(sess, cfg)
|
svc := cloudwatch.New(sess, cfg)
|
||||||
|
|
||||||
params := &cloudwatch.ListMetricsInput{
|
params := &cloudwatch.ListMetricsInput{
|
||||||
Namespace: aws.String(cwData.Namespace),
|
Namespace: aws.String(dsInfo.Namespace),
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp cloudwatch.ListMetricsOutput
|
var resp cloudwatch.ListMetricsOutput
|
||||||
@ -776,25 +774,27 @@ func getAllMetrics(cwData *DatasourceInfo) (cloudwatch.ListMetricsOutput, error)
|
|||||||
|
|
||||||
var metricsCacheLock sync.Mutex
|
var metricsCacheLock sync.Mutex
|
||||||
|
|
||||||
func getMetricsForCustomMetrics(dsInfo *DatasourceInfo, getAllMetrics func(*DatasourceInfo) (cloudwatch.ListMetricsOutput, error)) ([]string, error) {
|
func (e *cloudWatchExecutor) getMetricsForCustomMetrics(region string, getAllMetrics func(string) (cloudwatch.ListMetricsOutput, error)) ([]string, error) {
|
||||||
metricsCacheLock.Lock()
|
metricsCacheLock.Lock()
|
||||||
defer metricsCacheLock.Unlock()
|
defer metricsCacheLock.Unlock()
|
||||||
|
|
||||||
|
dsInfo := e.getDSInfo(region)
|
||||||
|
|
||||||
if _, ok := customMetricsMetricsMap[dsInfo.Profile]; !ok {
|
if _, ok := customMetricsMetricsMap[dsInfo.Profile]; !ok {
|
||||||
customMetricsMetricsMap[dsInfo.Profile] = make(map[string]map[string]*CustomMetricsCache)
|
customMetricsMetricsMap[dsInfo.Profile] = make(map[string]map[string]*customMetricsCache)
|
||||||
}
|
}
|
||||||
if _, ok := customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region]; !ok {
|
if _, ok := customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region]; !ok {
|
||||||
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region] = make(map[string]*CustomMetricsCache)
|
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region] = make(map[string]*customMetricsCache)
|
||||||
}
|
}
|
||||||
if _, ok := customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace]; !ok {
|
if _, ok := customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace]; !ok {
|
||||||
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace] = &CustomMetricsCache{}
|
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace] = &customMetricsCache{}
|
||||||
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = make([]string, 0)
|
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = make([]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire.After(time.Now()) {
|
if customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire.After(time.Now()) {
|
||||||
return customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil
|
return customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil
|
||||||
}
|
}
|
||||||
result, err := getAllMetrics(dsInfo)
|
result, err := getAllMetrics(region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, err
|
return []string{}, err
|
||||||
}
|
}
|
||||||
@ -813,25 +813,27 @@ func getMetricsForCustomMetrics(dsInfo *DatasourceInfo, getAllMetrics func(*Data
|
|||||||
|
|
||||||
var dimensionsCacheLock sync.Mutex
|
var dimensionsCacheLock sync.Mutex
|
||||||
|
|
||||||
func getDimensionsForCustomMetrics(dsInfo *DatasourceInfo, getAllMetrics func(*DatasourceInfo) (cloudwatch.ListMetricsOutput, error)) ([]string, error) {
|
func (e *cloudWatchExecutor) getDimensionsForCustomMetrics(region string, getAllMetrics func(string) (cloudwatch.ListMetricsOutput, error)) ([]string, error) {
|
||||||
dimensionsCacheLock.Lock()
|
dimensionsCacheLock.Lock()
|
||||||
defer dimensionsCacheLock.Unlock()
|
defer dimensionsCacheLock.Unlock()
|
||||||
|
|
||||||
|
dsInfo := e.getDSInfo(region)
|
||||||
|
|
||||||
if _, ok := customMetricsDimensionsMap[dsInfo.Profile]; !ok {
|
if _, ok := customMetricsDimensionsMap[dsInfo.Profile]; !ok {
|
||||||
customMetricsDimensionsMap[dsInfo.Profile] = make(map[string]map[string]*CustomMetricsCache)
|
customMetricsDimensionsMap[dsInfo.Profile] = make(map[string]map[string]*customMetricsCache)
|
||||||
}
|
}
|
||||||
if _, ok := customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region]; !ok {
|
if _, ok := customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region]; !ok {
|
||||||
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region] = make(map[string]*CustomMetricsCache)
|
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region] = make(map[string]*customMetricsCache)
|
||||||
}
|
}
|
||||||
if _, ok := customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace]; !ok {
|
if _, ok := customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace]; !ok {
|
||||||
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace] = &CustomMetricsCache{}
|
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace] = &customMetricsCache{}
|
||||||
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = make([]string, 0)
|
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = make([]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire.After(time.Now()) {
|
if customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire.After(time.Now()) {
|
||||||
return customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil
|
return customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil
|
||||||
}
|
}
|
||||||
result, err := getAllMetrics(dsInfo)
|
result, err := getAllMetrics(region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, err
|
return []string{}, err
|
||||||
}
|
}
|
||||||
|
@ -44,13 +44,16 @@ func (m mockedRGTA) GetResourcesPages(in *resourcegroupstaggingapi.GetResourcesI
|
|||||||
|
|
||||||
func TestCloudWatchMetrics(t *testing.T) {
|
func TestCloudWatchMetrics(t *testing.T) {
|
||||||
t.Run("When calling getMetricsForCustomMetrics", func(t *testing.T) {
|
t.Run("When calling getMetricsForCustomMetrics", func(t *testing.T) {
|
||||||
dsInfo := &DatasourceInfo{
|
const region = "us-east-1"
|
||||||
Region: "us-east-1",
|
e := &cloudWatchExecutor{
|
||||||
Namespace: "Foo",
|
DataSource: &models.DataSource{
|
||||||
Profile: "default",
|
Database: "default",
|
||||||
AssumeRoleArn: "",
|
JsonData: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"Region": region,
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
f := func(dsInfo *DatasourceInfo) (cloudwatch.ListMetricsOutput, error) {
|
f := func(region string) (cloudwatch.ListMetricsOutput, error) {
|
||||||
return cloudwatch.ListMetricsOutput{
|
return cloudwatch.ListMetricsOutput{
|
||||||
Metrics: []*cloudwatch.Metric{
|
Metrics: []*cloudwatch.Metric{
|
||||||
{
|
{
|
||||||
@ -64,20 +67,23 @@ func TestCloudWatchMetrics(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
metrics, err := getMetricsForCustomMetrics(dsInfo, f)
|
metrics, err := e.getMetricsForCustomMetrics(region, f)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Contains(t, metrics, "Test_MetricName")
|
assert.Contains(t, metrics, "Test_MetricName")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When calling getDimensionsForCustomMetrics", func(t *testing.T) {
|
t.Run("When calling getDimensionsForCustomMetrics", func(t *testing.T) {
|
||||||
dsInfo := &DatasourceInfo{
|
const region = "us-east-1"
|
||||||
Region: "us-east-1",
|
e := &cloudWatchExecutor{
|
||||||
Namespace: "Foo",
|
DataSource: &models.DataSource{
|
||||||
Profile: "default",
|
Database: "default",
|
||||||
AssumeRoleArn: "",
|
JsonData: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"Region": region,
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
f := func(dsInfo *DatasourceInfo) (cloudwatch.ListMetricsOutput, error) {
|
f := func(region string) (cloudwatch.ListMetricsOutput, error) {
|
||||||
return cloudwatch.ListMetricsOutput{
|
return cloudwatch.ListMetricsOutput{
|
||||||
Metrics: []*cloudwatch.Metric{
|
Metrics: []*cloudwatch.Metric{
|
||||||
{
|
{
|
||||||
@ -91,14 +97,14 @@ func TestCloudWatchMetrics(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
dimensionKeys, err := getDimensionsForCustomMetrics(dsInfo, f)
|
dimensionKeys, err := e.getDimensionsForCustomMetrics(region, f)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Contains(t, dimensionKeys, "Test_DimensionName")
|
assert.Contains(t, dimensionKeys, "Test_DimensionName")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When calling handleGetRegions", func(t *testing.T) {
|
t.Run("When calling handleGetRegions", func(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{
|
executor := &cloudWatchExecutor{
|
||||||
ec2Svc: mockedEc2{RespRegions: ec2.DescribeRegionsOutput{
|
ec2Svc: mockedEc2{RespRegions: ec2.DescribeRegionsOutput{
|
||||||
Regions: []*ec2.Region{
|
Regions: []*ec2.Region{
|
||||||
{
|
{
|
||||||
@ -123,7 +129,7 @@ func TestCloudWatchMetrics(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When calling handleGetEc2InstanceAttribute", func(t *testing.T) {
|
t.Run("When calling handleGetEc2InstanceAttribute", func(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{
|
executor := &cloudWatchExecutor{
|
||||||
ec2Svc: mockedEc2{Resp: ec2.DescribeInstancesOutput{
|
ec2Svc: mockedEc2{Resp: ec2.DescribeInstancesOutput{
|
||||||
Reservations: []*ec2.Reservation{
|
Reservations: []*ec2.Reservation{
|
||||||
{
|
{
|
||||||
@ -156,7 +162,7 @@ func TestCloudWatchMetrics(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When calling handleGetEbsVolumeIds", func(t *testing.T) {
|
t.Run("When calling handleGetEbsVolumeIds", func(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{
|
executor := &cloudWatchExecutor{
|
||||||
ec2Svc: mockedEc2{Resp: ec2.DescribeInstancesOutput{
|
ec2Svc: mockedEc2{Resp: ec2.DescribeInstancesOutput{
|
||||||
Reservations: []*ec2.Reservation{
|
Reservations: []*ec2.Reservation{
|
||||||
{
|
{
|
||||||
@ -217,7 +223,7 @@ func TestCloudWatchMetrics(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When calling handleGetResourceArns", func(t *testing.T) {
|
t.Run("When calling handleGetResourceArns", func(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{
|
executor := &cloudWatchExecutor{
|
||||||
rgtaSvc: mockedRGTA{
|
rgtaSvc: mockedRGTA{
|
||||||
Resp: resourcegroupstaggingapi.GetResourcesOutput{
|
Resp: resourcegroupstaggingapi.GetResourcesOutput{
|
||||||
ResourceTagMappingList: []*resourcegroupstaggingapi.ResourceTagMapping{
|
ResourceTagMappingList: []*resourcegroupstaggingapi.ResourceTagMapping{
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
// has more than one statistic defined, one cloudwatchQuery will be created for each statistic.
|
// has more than one statistic defined, one cloudwatchQuery will be created for each statistic.
|
||||||
// If the query doesn't have an Id defined by the user, we'll give it an with format `query[RefId]`. In the case
|
// If the query doesn't have an Id defined by the user, we'll give it an with format `query[RefId]`. In the case
|
||||||
// the incoming query had more than one stat, it will ge an id like `query[RefId]_[StatName]`, eg queryC_Average
|
// the incoming query had more than one stat, it will ge an id like `query[RefId]_[StatName]`, eg queryC_Average
|
||||||
func (e *CloudWatchExecutor) transformRequestQueriesToCloudWatchQueries(requestQueries []*requestQuery) (map[string]*cloudWatchQuery, error) {
|
func (e *cloudWatchExecutor) transformRequestQueriesToCloudWatchQueries(requestQueries []*requestQuery) (map[string]*cloudWatchQuery, error) {
|
||||||
cloudwatchQueries := make(map[string]*cloudWatchQuery)
|
cloudwatchQueries := make(map[string]*cloudWatchQuery)
|
||||||
for _, requestQuery := range requestQueries {
|
for _, requestQuery := range requestQueries {
|
||||||
for _, stat := range requestQuery.Statistics {
|
for _, stat := range requestQuery.Statistics {
|
||||||
@ -50,7 +50,7 @@ func (e *CloudWatchExecutor) transformRequestQueriesToCloudWatchQueries(requestQ
|
|||||||
return cloudwatchQueries, nil
|
return cloudwatchQueries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) transformQueryResponseToQueryResult(cloudwatchResponses []*cloudwatchResponse) map[string]*tsdb.QueryResult {
|
func (e *cloudWatchExecutor) transformQueryResponseToQueryResult(cloudwatchResponses []*cloudwatchResponse) map[string]*tsdb.QueryResult {
|
||||||
responsesByRefID := make(map[string][]*cloudwatchResponse)
|
responsesByRefID := make(map[string][]*cloudwatchResponse)
|
||||||
for _, res := range cloudwatchResponses {
|
for _, res := range cloudwatchResponses {
|
||||||
responsesByRefID[res.RefId] = append(responsesByRefID[res.RefId], res)
|
responsesByRefID[res.RefId] = append(responsesByRefID[res.RefId], res)
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
func TestQueryTransformer(t *testing.T) {
|
func TestQueryTransformer(t *testing.T) {
|
||||||
Convey("TestQueryTransformer", t, func() {
|
Convey("TestQueryTransformer", t, func() {
|
||||||
Convey("when transforming queries", func() {
|
Convey("when transforming queries", func() {
|
||||||
executor := &CloudWatchExecutor{}
|
executor := &cloudWatchExecutor{}
|
||||||
Convey("one cloudwatchQuery is generated when its request query has one stat", func() {
|
Convey("one cloudwatchQuery is generated when its request query has one stat", func() {
|
||||||
requestQueries := []*requestQuery{
|
requestQueries := []*requestQuery{
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Parses the json queries and returns a requestQuery. The requestQuery has a 1 to 1 mapping to a query editor row
|
// Parses the json queries and returns a requestQuery. The requestQuery has a 1 to 1 mapping to a query editor row
|
||||||
func (e *CloudWatchExecutor) parseQueries(queryContext *tsdb.TsdbQuery, startTime time.Time, endTime time.Time) (map[string][]*requestQuery, error) {
|
func (e *cloudWatchExecutor) parseQueries(queryContext *tsdb.TsdbQuery, startTime time.Time, endTime time.Time) (map[string][]*requestQuery, error) {
|
||||||
requestQueries := make(map[string][]*requestQuery)
|
requestQueries := make(map[string][]*requestQuery)
|
||||||
for i, model := range queryContext.Queries {
|
for i, model := range queryContext.Queries {
|
||||||
queryType := model.Model.Get("type").MustString()
|
queryType := model.Model.Get("type").MustString()
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/tsdb"
|
"github.com/grafana/grafana/pkg/tsdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) parseResponse(metricDataOutputs []*cloudwatch.GetMetricDataOutput, queries map[string]*cloudWatchQuery) ([]*cloudwatchResponse, error) {
|
func (e *cloudWatchExecutor) parseResponse(metricDataOutputs []*cloudwatch.GetMetricDataOutput, queries map[string]*cloudWatchQuery) ([]*cloudwatchResponse, error) {
|
||||||
mdr := make(map[string]map[string]*cloudwatch.MetricDataResult)
|
mdr := make(map[string]map[string]*cloudwatch.MetricDataResult)
|
||||||
for _, mdo := range metricDataOutputs {
|
for _, mdo := range metricDataOutputs {
|
||||||
requestExceededMaxLimit := false
|
requestExceededMaxLimit := false
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
func (e *cloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||||
startTime, err := queryContext.TimeRange.ParseFrom()
|
startTime, err := queryContext.TimeRange.ParseFrom()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -50,7 +50,7 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
client, err := e.getClient(region)
|
client, err := e.getCWClient(region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestTimeSeriesQuery(t *testing.T) {
|
func TestTimeSeriesQuery(t *testing.T) {
|
||||||
executor := &CloudWatchExecutor{}
|
executor := &cloudWatchExecutor{}
|
||||||
|
|
||||||
t.Run("End time before start time should result in error", func(t *testing.T) {
|
t.Run("End time before start time should result in error", func(t *testing.T) {
|
||||||
_, err := executor.executeTimeSeriesQuery(context.TODO(), &tsdb.TsdbQuery{TimeRange: tsdb.NewTimeRange("now-1h", "now-2h")})
|
_, err := executor.executeTimeSeriesQuery(context.TODO(), &tsdb.TsdbQuery{TimeRange: tsdb.NewTimeRange("now-1h", "now-2h")})
|
||||||
|
Loading…
Reference in New Issue
Block a user