mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
The datasource uses the default region in the query if the region is "" in the settings. However setting the region to an empty string is almost impossible and rendered incorrectly. This commit introduces a special value "default" for region which is shown in the drop down and is translated to the default region of the data source when performing queries.
202 lines
5.2 KiB
Go
202 lines
5.2 KiB
Go
package cloudwatch
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
|
|
"github.com/aws/aws-sdk-go/aws/credentials/endpointcreds"
|
|
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
|
"github.com/aws/aws-sdk-go/service/sts"
|
|
)
|
|
|
|
type cache struct {
|
|
credential *credentials.Credentials
|
|
expiration *time.Time
|
|
}
|
|
|
|
var awsCredentialCache map[string]cache = make(map[string]cache)
|
|
var credentialCacheLock sync.RWMutex
|
|
|
|
func GetCredentials(dsInfo *DatasourceInfo) (*credentials.Credentials, error) {
|
|
cacheKey := dsInfo.AccessKey + ":" + dsInfo.Profile + ":" + dsInfo.AssumeRoleArn
|
|
credentialCacheLock.RLock()
|
|
if _, ok := awsCredentialCache[cacheKey]; ok {
|
|
if awsCredentialCache[cacheKey].expiration != nil &&
|
|
(*awsCredentialCache[cacheKey].expiration).After(time.Now().UTC()) {
|
|
result := awsCredentialCache[cacheKey].credential
|
|
credentialCacheLock.RUnlock()
|
|
return result, nil
|
|
}
|
|
}
|
|
credentialCacheLock.RUnlock()
|
|
|
|
accessKeyId := ""
|
|
secretAccessKey := ""
|
|
sessionToken := ""
|
|
var expiration *time.Time
|
|
expiration = nil
|
|
if dsInfo.AuthType == "arn" && strings.Index(dsInfo.AssumeRoleArn, "arn:aws:iam:") == 0 {
|
|
params := &sts.AssumeRoleInput{
|
|
RoleArn: aws.String(dsInfo.AssumeRoleArn),
|
|
RoleSessionName: aws.String("GrafanaSession"),
|
|
DurationSeconds: aws.Int64(900),
|
|
}
|
|
|
|
stsSess, err := session.NewSession()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stsCreds := credentials.NewChainCredentials(
|
|
[]credentials.Provider{
|
|
&credentials.EnvProvider{},
|
|
&credentials.SharedCredentialsProvider{Filename: "", Profile: dsInfo.Profile},
|
|
remoteCredProvider(stsSess),
|
|
})
|
|
stsConfig := &aws.Config{
|
|
Region: aws.String(dsInfo.Region),
|
|
Credentials: stsCreds,
|
|
}
|
|
|
|
sess, err := session.NewSession(stsConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
svc := sts.New(sess, stsConfig)
|
|
resp, err := svc.AssumeRole(params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.Credentials != nil {
|
|
accessKeyId = *resp.Credentials.AccessKeyId
|
|
secretAccessKey = *resp.Credentials.SecretAccessKey
|
|
sessionToken = *resp.Credentials.SessionToken
|
|
expiration = resp.Credentials.Expiration
|
|
}
|
|
} else {
|
|
now := time.Now()
|
|
e := now.Add(5 * time.Minute)
|
|
expiration = &e
|
|
}
|
|
|
|
sess, err := session.NewSession()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
creds := credentials.NewChainCredentials(
|
|
[]credentials.Provider{
|
|
&credentials.StaticProvider{Value: credentials.Value{
|
|
AccessKeyID: accessKeyId,
|
|
SecretAccessKey: secretAccessKey,
|
|
SessionToken: sessionToken,
|
|
}},
|
|
&credentials.EnvProvider{},
|
|
&credentials.StaticProvider{Value: credentials.Value{
|
|
AccessKeyID: dsInfo.AccessKey,
|
|
SecretAccessKey: dsInfo.SecretKey,
|
|
}},
|
|
&credentials.SharedCredentialsProvider{Filename: "", Profile: dsInfo.Profile},
|
|
remoteCredProvider(sess),
|
|
})
|
|
|
|
credentialCacheLock.Lock()
|
|
awsCredentialCache[cacheKey] = cache{
|
|
credential: creds,
|
|
expiration: expiration,
|
|
}
|
|
credentialCacheLock.Unlock()
|
|
|
|
return creds, nil
|
|
}
|
|
|
|
func remoteCredProvider(sess *session.Session) credentials.Provider {
|
|
ecsCredURI := os.Getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
|
|
|
|
if len(ecsCredURI) > 0 {
|
|
return ecsCredProvider(sess, ecsCredURI)
|
|
}
|
|
return ec2RoleProvider(sess)
|
|
}
|
|
|
|
func ecsCredProvider(sess *session.Session, uri string) credentials.Provider {
|
|
const host = `169.254.170.2`
|
|
|
|
c := ec2metadata.New(sess)
|
|
return endpointcreds.NewProviderClient(
|
|
c.Client.Config,
|
|
c.Client.Handlers,
|
|
fmt.Sprintf("http://%s%s", host, uri),
|
|
func(p *endpointcreds.Provider) { p.ExpiryWindow = 5 * time.Minute })
|
|
}
|
|
|
|
func ec2RoleProvider(sess *session.Session) credentials.Provider {
|
|
return &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute}
|
|
}
|
|
|
|
func (e *CloudWatchExecutor) getDsInfo(region string) *DatasourceInfo {
|
|
defaultRegion := e.DataSource.JsonData.Get("defaultRegion").MustString()
|
|
if region == "default" {
|
|
region = defaultRegion
|
|
}
|
|
|
|
authType := e.DataSource.JsonData.Get("authType").MustString()
|
|
assumeRoleArn := e.DataSource.JsonData.Get("assumeRoleArn").MustString()
|
|
accessKey := ""
|
|
secretKey := ""
|
|
for key, value := range e.DataSource.SecureJsonData.Decrypt() {
|
|
if key == "accessKey" {
|
|
accessKey = value
|
|
}
|
|
if key == "secretKey" {
|
|
secretKey = value
|
|
}
|
|
}
|
|
|
|
datasourceInfo := &DatasourceInfo{
|
|
Region: region,
|
|
Profile: e.DataSource.Database,
|
|
AuthType: authType,
|
|
AssumeRoleArn: assumeRoleArn,
|
|
AccessKey: accessKey,
|
|
SecretKey: secretKey,
|
|
}
|
|
|
|
return datasourceInfo
|
|
}
|
|
|
|
func (e *CloudWatchExecutor) getAwsConfig(dsInfo *DatasourceInfo) (*aws.Config, error) {
|
|
creds, err := GetCredentials(dsInfo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cfg := &aws.Config{
|
|
Region: aws.String(dsInfo.Region),
|
|
Credentials: creds,
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
func (e *CloudWatchExecutor) getClient(region string) (*cloudwatch.CloudWatch, error) {
|
|
datasourceInfo := e.getDsInfo(region)
|
|
cfg, err := e.getAwsConfig(datasourceInfo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sess, err := session.NewSession(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client := cloudwatch.New(sess, cfg)
|
|
return client, nil
|
|
}
|