Commit 07d35585 by Mitsuhiro Tanda Committed by Torkel Ödegaard

(cloudwatch) assume role support (#5065)

* (cloudwatch) assume role support

* save godep

* (cloudwatch) add assumeRoleArn field

* (cloudwatch) set cred provider for sts

* (cloudwatch) fix test
parent fc189132
...@@ -125,6 +125,11 @@ ...@@ -125,6 +125,11 @@
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f" "Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/service/sts",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/bmizerany/assert", "ImportPath": "github.com/bmizerany/assert",
"Comment": "release.r60-6-ge17e998", "Comment": "release.r60-6-ge17e998",
"Rev": "e17e99893cb6509f428e1728281c2ad60a6b31e3" "Rev": "e17e99893cb6509f428e1728281c2ad60a6b31e3"
......
package sts
import "github.com/aws/aws-sdk-go/aws/request"
func init() {
initRequest = func(r *request.Request) {
switch r.Operation.Name {
case opAssumeRoleWithSAML, opAssumeRoleWithWebIdentity:
r.Handlers.Sign.Clear() // these operations are unsigned
}
}
}
package sts_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/awstesting/unit"
"github.com/aws/aws-sdk-go/service/sts"
)
var svc = sts.New(unit.Session, &aws.Config{
Region: aws.String("mock-region"),
})
func TestUnsignedRequest_AssumeRoleWithSAML(t *testing.T) {
req, _ := svc.AssumeRoleWithSAMLRequest(&sts.AssumeRoleWithSAMLInput{
PrincipalArn: aws.String("ARN01234567890123456789"),
RoleArn: aws.String("ARN01234567890123456789"),
SAMLAssertion: aws.String("ASSERT"),
})
err := req.Sign()
assert.NoError(t, err)
assert.Equal(t, "", req.HTTPRequest.Header.Get("Authorization"))
}
func TestUnsignedRequest_AssumeRoleWithWebIdentity(t *testing.T) {
req, _ := svc.AssumeRoleWithWebIdentityRequest(&sts.AssumeRoleWithWebIdentityInput{
RoleArn: aws.String("ARN01234567890123456789"),
RoleSessionName: aws.String("SESSION"),
WebIdentityToken: aws.String("TOKEN"),
})
err := req.Sign()
assert.NoError(t, err)
assert.Equal(t, "", req.HTTPRequest.Header.Get("Authorization"))
}
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
package sts_test
import (
"bytes"
"fmt"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
)
var _ time.Duration
var _ bytes.Buffer
func ExampleSTS_AssumeRole() {
svc := sts.New(session.New())
params := &sts.AssumeRoleInput{
RoleArn: aws.String("arnType"), // Required
RoleSessionName: aws.String("roleSessionNameType"), // Required
DurationSeconds: aws.Int64(1),
ExternalId: aws.String("externalIdType"),
Policy: aws.String("sessionPolicyDocumentType"),
SerialNumber: aws.String("serialNumberType"),
TokenCode: aws.String("tokenCodeType"),
}
resp, err := svc.AssumeRole(params)
if err != nil {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
fmt.Println(err.Error())
return
}
// Pretty-print the response data.
fmt.Println(resp)
}
func ExampleSTS_AssumeRoleWithSAML() {
svc := sts.New(session.New())
params := &sts.AssumeRoleWithSAMLInput{
PrincipalArn: aws.String("arnType"), // Required
RoleArn: aws.String("arnType"), // Required
SAMLAssertion: aws.String("SAMLAssertionType"), // Required
DurationSeconds: aws.Int64(1),
Policy: aws.String("sessionPolicyDocumentType"),
}
resp, err := svc.AssumeRoleWithSAML(params)
if err != nil {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
fmt.Println(err.Error())
return
}
// Pretty-print the response data.
fmt.Println(resp)
}
func ExampleSTS_AssumeRoleWithWebIdentity() {
svc := sts.New(session.New())
params := &sts.AssumeRoleWithWebIdentityInput{
RoleArn: aws.String("arnType"), // Required
RoleSessionName: aws.String("roleSessionNameType"), // Required
WebIdentityToken: aws.String("clientTokenType"), // Required
DurationSeconds: aws.Int64(1),
Policy: aws.String("sessionPolicyDocumentType"),
ProviderId: aws.String("urlType"),
}
resp, err := svc.AssumeRoleWithWebIdentity(params)
if err != nil {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
fmt.Println(err.Error())
return
}
// Pretty-print the response data.
fmt.Println(resp)
}
func ExampleSTS_DecodeAuthorizationMessage() {
svc := sts.New(session.New())
params := &sts.DecodeAuthorizationMessageInput{
EncodedMessage: aws.String("encodedMessageType"), // Required
}
resp, err := svc.DecodeAuthorizationMessage(params)
if err != nil {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
fmt.Println(err.Error())
return
}
// Pretty-print the response data.
fmt.Println(resp)
}
func ExampleSTS_GetFederationToken() {
svc := sts.New(session.New())
params := &sts.GetFederationTokenInput{
Name: aws.String("userNameType"), // Required
DurationSeconds: aws.Int64(1),
Policy: aws.String("sessionPolicyDocumentType"),
}
resp, err := svc.GetFederationToken(params)
if err != nil {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
fmt.Println(err.Error())
return
}
// Pretty-print the response data.
fmt.Println(resp)
}
func ExampleSTS_GetSessionToken() {
svc := sts.New(session.New())
params := &sts.GetSessionTokenInput{
DurationSeconds: aws.Int64(1),
SerialNumber: aws.String("serialNumberType"),
TokenCode: aws.String("tokenCodeType"),
}
resp, err := svc.GetSessionToken(params)
if err != nil {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
fmt.Println(err.Error())
return
}
// Pretty-print the response data.
fmt.Println(resp)
}
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
package sts
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/protocol/query"
"github.com/aws/aws-sdk-go/private/signer/v4"
)
// The AWS Security Token Service (STS) is a web service that enables you to
// request temporary, limited-privilege credentials for AWS Identity and Access
// Management (IAM) users or for users that you authenticate (federated users).
// This guide provides descriptions of the STS API. For more detailed information
// about using this service, go to Temporary Security Credentials (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html).
//
// As an alternative to using the API, you can use one of the AWS SDKs, which
// consist of libraries and sample code for various programming languages and
// platforms (Java, Ruby, .NET, iOS, Android, etc.). The SDKs provide a convenient
// way to create programmatic access to STS. For example, the SDKs take care
// of cryptographically signing requests, managing errors, and retrying requests
// automatically. For information about the AWS SDKs, including how to download
// and install them, see the Tools for Amazon Web Services page (http://aws.amazon.com/tools/).
// For information about setting up signatures and authorization through the
// API, go to Signing AWS API Requests (http://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html"
// target="_blank) in the AWS General Reference. For general information about
// the Query API, go to Making Query Requests (http://docs.aws.amazon.com/IAM/latest/UserGuide/IAM_UsingQueryAPI.html"
// target="_blank) in Using IAM. For information about using security tokens
// with other AWS products, go to AWS Services That Work with IAM (http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html)
// in the Using IAM.
//
// If you're new to AWS and need additional technical information about a specific
// AWS product, you can find the product's technical documentation at http://aws.amazon.com/documentation/
// (http://aws.amazon.com/documentation/" target="_blank).
//
// Endpoints
//
// The AWS Security Token Service (STS) has a default endpoint of https://sts.amazonaws.com
// that maps to the US East (N. Virginia) region. Additional regions are available,
// but must first be activated in the AWS Management Console before you can
// use a different region's endpoint. For more information about activating
// a region for STS see Activating STS in a New Region (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html)
// in the Using IAM.
//
// For information about STS endpoints, see Regions and Endpoints (http://docs.aws.amazon.com/general/latest/gr/rande.html#sts_region)
// in the AWS General Reference.
//
// Recording API requests
//
// STS supports AWS CloudTrail, which is a service that records AWS calls for
// your AWS account and delivers log files to an Amazon S3 bucket. By using
// information collected by CloudTrail, you can determine what requests were
// successfully made to STS, who made the request, when it was made, and so
// on. To learn more about CloudTrail, including how to turn it on and find
// your log files, see the AWS CloudTrail User Guide (http://docs.aws.amazon.com/awscloudtrail/latest/userguide/what_is_cloud_trail_top_level.html).
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type STS struct {
*client.Client
}
// Used for custom client initialization logic
var initClient func(*client.Client)
// Used for custom request initialization logic
var initRequest func(*request.Request)
// A ServiceName is the name of the service the client will make API calls to.
const ServiceName = "sts"
// New creates a new instance of the STS client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a STS client from just a session.
// svc := sts.New(mySession)
//
// // Create a STS client with additional configuration
// svc := sts.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *STS {
c := p.ClientConfig(ServiceName, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *STS {
svc := &STS{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2011-06-15",
},
handlers,
),
}
// Handlers
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(query.Build)
svc.Handlers.Unmarshal.PushBack(query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(query.UnmarshalError)
// Run custom client initialization if present
if initClient != nil {
initClient(svc.Client)
}
return svc
}
// newRequest creates a new request for a STS operation and runs any
// custom request initialization.
func (c *STS) newRequest(op *request.Operation, params, data interface{}) *request.Request {
req := c.NewRequest(op, params, data)
// Run custom request initialization if present
if initRequest != nil {
initRequest(req)
}
return req
}
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
// Package stsiface provides an interface for the AWS Security Token Service.
package stsiface
import (
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/sts"
)
// STSAPI is the interface type for sts.STS.
type STSAPI interface {
AssumeRoleRequest(*sts.AssumeRoleInput) (*request.Request, *sts.AssumeRoleOutput)
AssumeRole(*sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
AssumeRoleWithSAMLRequest(*sts.AssumeRoleWithSAMLInput) (*request.Request, *sts.AssumeRoleWithSAMLOutput)
AssumeRoleWithSAML(*sts.AssumeRoleWithSAMLInput) (*sts.AssumeRoleWithSAMLOutput, error)
AssumeRoleWithWebIdentityRequest(*sts.AssumeRoleWithWebIdentityInput) (*request.Request, *sts.AssumeRoleWithWebIdentityOutput)
AssumeRoleWithWebIdentity(*sts.AssumeRoleWithWebIdentityInput) (*sts.AssumeRoleWithWebIdentityOutput, error)
DecodeAuthorizationMessageRequest(*sts.DecodeAuthorizationMessageInput) (*request.Request, *sts.DecodeAuthorizationMessageOutput)
DecodeAuthorizationMessage(*sts.DecodeAuthorizationMessageInput) (*sts.DecodeAuthorizationMessageOutput, error)
GetFederationTokenRequest(*sts.GetFederationTokenInput) (*request.Request, *sts.GetFederationTokenOutput)
GetFederationToken(*sts.GetFederationTokenInput) (*sts.GetFederationTokenOutput, error)
GetSessionTokenRequest(*sts.GetSessionTokenInput) (*request.Request, *sts.GetSessionTokenOutput)
GetSessionToken(*sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error)
}
var _ STSAPI = (*sts.STS)(nil)
...@@ -4,6 +4,8 @@ import ( ...@@ -4,6 +4,8 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"io/ioutil" "io/ioutil"
"strings"
"sync"
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
...@@ -14,6 +16,8 @@ import ( ...@@ -14,6 +16,8 @@ import (
"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/cloudwatch"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
) )
...@@ -44,31 +48,96 @@ func init() { ...@@ -44,31 +48,96 @@ func init() {
} }
} }
var awsCredentials map[string]*credentials.Credentials = make(map[string]*credentials.Credentials) type cache struct {
credential *credentials.Credentials
expiration *time.Time
}
func getCredentials(profile string) *credentials.Credentials { var awsCredentialCache map[string]cache = make(map[string]cache)
if _, ok := awsCredentials[profile]; ok { var credentialCacheLock sync.RWMutex
return awsCredentials[profile]
func getCredentials(profile string, region string, assumeRoleArn string) *credentials.Credentials {
credentialCacheLock.RLock()
if _, ok := awsCredentialCache[profile]; ok {
if awsCredentialCache[profile].expiration != nil &&
(*awsCredentialCache[profile].expiration).After(time.Now().UTC()) {
result := awsCredentialCache[profile].credential
credentialCacheLock.RUnlock()
return result
}
}
credentialCacheLock.RUnlock()
accessKeyId := ""
secretAccessKey := ""
sessionToken := ""
var expiration *time.Time
expiration = nil
if strings.Index(assumeRoleArn, "arn:aws:iam:") == 0 {
params := &sts.AssumeRoleInput{
RoleArn: aws.String(assumeRoleArn),
RoleSessionName: aws.String("GrafanaSession"),
DurationSeconds: aws.Int64(900),
}
stsSess := session.New()
stsCreds := credentials.NewChainCredentials(
[]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: profile},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(stsSess), ExpiryWindow: 5 * time.Minute},
})
stsConfig := &aws.Config{
Region: aws.String(region),
Credentials: stsCreds,
}
svc := sts.New(session.New(stsConfig), stsConfig)
resp, err := svc.AssumeRole(params)
if err != nil {
// ignore
log.Error(3, "CloudWatch: Failed to assume role", err)
}
if resp.Credentials != nil {
accessKeyId = *resp.Credentials.AccessKeyId
secretAccessKey = *resp.Credentials.SecretAccessKey
sessionToken = *resp.Credentials.SessionToken
expiration = resp.Credentials.Expiration
}
} }
sess := session.New() sess := session.New()
creds := credentials.NewChainCredentials( creds := credentials.NewChainCredentials(
[]credentials.Provider{ []credentials.Provider{
&credentials.StaticProvider{Value: credentials.Value{
AccessKeyID: accessKeyId,
SecretAccessKey: secretAccessKey,
SessionToken: sessionToken,
}},
&credentials.EnvProvider{}, &credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: profile}, &credentials.SharedCredentialsProvider{Filename: "", Profile: profile},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute}, &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
}) })
awsCredentials[profile] = creds credentialCacheLock.Lock()
awsCredentialCache[profile] = cache{
credential: creds,
expiration: expiration,
}
credentialCacheLock.Unlock()
return creds return creds
} }
func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) { func getAwsConfig(req *cwRequest) *aws.Config {
assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString()
cfg := &aws.Config{ cfg := &aws.Config{
Region: aws.String(req.Region), Region: aws.String(req.Region),
Credentials: getCredentials(req.DataSource.Database), Credentials: getCredentials(req.DataSource.Database, req.Region, assumeRoleArn),
} }
return cfg
}
func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
cfg := getAwsConfig(req)
svc := cloudwatch.New(session.New(cfg), cfg) svc := cloudwatch.New(session.New(cfg), cfg)
reqParam := &struct { reqParam := &struct {
...@@ -104,11 +173,7 @@ func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) { ...@@ -104,11 +173,7 @@ func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
} }
func handleListMetrics(req *cwRequest, c *middleware.Context) { func handleListMetrics(req *cwRequest, c *middleware.Context) {
cfg := &aws.Config{ cfg := getAwsConfig(req)
Region: aws.String(req.Region),
Credentials: getCredentials(req.DataSource.Database),
}
svc := cloudwatch.New(session.New(cfg), cfg) svc := cloudwatch.New(session.New(cfg), cfg)
reqParam := &struct { reqParam := &struct {
...@@ -144,11 +209,7 @@ func handleListMetrics(req *cwRequest, c *middleware.Context) { ...@@ -144,11 +209,7 @@ func handleListMetrics(req *cwRequest, c *middleware.Context) {
} }
func handleDescribeAlarms(req *cwRequest, c *middleware.Context) { func handleDescribeAlarms(req *cwRequest, c *middleware.Context) {
cfg := &aws.Config{ cfg := getAwsConfig(req)
Region: aws.String(req.Region),
Credentials: getCredentials(req.DataSource.Database),
}
svc := cloudwatch.New(session.New(cfg), cfg) svc := cloudwatch.New(session.New(cfg), cfg)
reqParam := &struct { reqParam := &struct {
...@@ -187,11 +248,7 @@ func handleDescribeAlarms(req *cwRequest, c *middleware.Context) { ...@@ -187,11 +248,7 @@ func handleDescribeAlarms(req *cwRequest, c *middleware.Context) {
} }
func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) { func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
cfg := &aws.Config{ cfg := getAwsConfig(req)
Region: aws.String(req.Region),
Credentials: getCredentials(req.DataSource.Database),
}
svc := cloudwatch.New(session.New(cfg), cfg) svc := cloudwatch.New(session.New(cfg), cfg)
reqParam := &struct { reqParam := &struct {
...@@ -227,11 +284,7 @@ func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) { ...@@ -227,11 +284,7 @@ func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
} }
func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) { func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) {
cfg := &aws.Config{ cfg := getAwsConfig(req)
Region: aws.String(req.Region),
Credentials: getCredentials(req.DataSource.Database),
}
svc := cloudwatch.New(session.New(cfg), cfg) svc := cloudwatch.New(session.New(cfg), cfg)
reqParam := &struct { reqParam := &struct {
...@@ -263,11 +316,7 @@ func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) { ...@@ -263,11 +316,7 @@ func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) {
} }
func handleDescribeInstances(req *cwRequest, c *middleware.Context) { func handleDescribeInstances(req *cwRequest, c *middleware.Context) {
cfg := &aws.Config{ cfg := getAwsConfig(req)
Region: aws.String(req.Region),
Credentials: getCredentials(req.DataSource.Database),
}
svc := ec2.New(session.New(cfg), cfg) svc := ec2.New(session.New(cfg), cfg)
reqParam := &struct { reqParam := &struct {
......
...@@ -166,7 +166,8 @@ func handleGetMetrics(req *cwRequest, c *middleware.Context) { ...@@ -166,7 +166,8 @@ func handleGetMetrics(req *cwRequest, c *middleware.Context) {
} }
} else { } else {
var err error var err error
if namespaceMetrics, err = getMetricsForCustomMetrics(req.Region, reqParam.Parameters.Namespace, req.DataSource.Database, getAllMetrics); err != nil { assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString()
if namespaceMetrics, err = getMetricsForCustomMetrics(req.Region, reqParam.Parameters.Namespace, req.DataSource.Database, assumeRoleArn, getAllMetrics); err != nil {
c.JsonApiErr(500, "Unable to call AWS API", err) c.JsonApiErr(500, "Unable to call AWS API", err)
return return
} }
...@@ -199,7 +200,8 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { ...@@ -199,7 +200,8 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) {
} }
} else { } else {
var err error var err error
if dimensionValues, err = getDimensionsForCustomMetrics(req.Region, reqParam.Parameters.Namespace, req.DataSource.Database, getAllMetrics); err != nil { assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString()
if dimensionValues, err = getDimensionsForCustomMetrics(req.Region, reqParam.Parameters.Namespace, req.DataSource.Database, assumeRoleArn, getAllMetrics); err != nil {
c.JsonApiErr(500, "Unable to call AWS API", err) c.JsonApiErr(500, "Unable to call AWS API", err)
return return
} }
...@@ -214,10 +216,10 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { ...@@ -214,10 +216,10 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) {
c.JSON(200, result) c.JSON(200, result)
} }
func getAllMetrics(region string, namespace string, database string) (cloudwatch.ListMetricsOutput, error) { func getAllMetrics(region string, namespace string, database string, assumeRoleArn string) (cloudwatch.ListMetricsOutput, error) {
cfg := &aws.Config{ cfg := &aws.Config{
Region: aws.String(region), Region: aws.String(region),
Credentials: getCredentials(database), Credentials: getCredentials(database, region, assumeRoleArn),
} }
svc := cloudwatch.New(session.New(cfg), cfg) svc := cloudwatch.New(session.New(cfg), cfg)
...@@ -244,8 +246,8 @@ func getAllMetrics(region string, namespace string, database string) (cloudwatch ...@@ -244,8 +246,8 @@ func getAllMetrics(region string, namespace string, database string) (cloudwatch
var metricsCacheLock sync.Mutex var metricsCacheLock sync.Mutex
func getMetricsForCustomMetrics(region string, namespace string, database string, getAllMetrics func(string, string, string) (cloudwatch.ListMetricsOutput, error)) ([]string, error) { func getMetricsForCustomMetrics(region string, namespace string, database string, assumeRoleArn string, getAllMetrics func(string, string, string, string) (cloudwatch.ListMetricsOutput, error)) ([]string, error) {
result, err := getAllMetrics(region, namespace, database) result, err := getAllMetrics(region, namespace, database, assumeRoleArn)
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
...@@ -282,8 +284,8 @@ func getMetricsForCustomMetrics(region string, namespace string, database string ...@@ -282,8 +284,8 @@ func getMetricsForCustomMetrics(region string, namespace string, database string
var dimensionsCacheLock sync.Mutex var dimensionsCacheLock sync.Mutex
func getDimensionsForCustomMetrics(region string, namespace string, database string, getAllMetrics func(string, string, string) (cloudwatch.ListMetricsOutput, error)) ([]string, error) { func getDimensionsForCustomMetrics(region string, namespace string, database string, assumeRoleArn string, getAllMetrics func(string, string, string, string) (cloudwatch.ListMetricsOutput, error)) ([]string, error) {
result, err := getAllMetrics(region, namespace, database) result, err := getAllMetrics(region, namespace, database, assumeRoleArn)
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
......
...@@ -14,7 +14,8 @@ func TestCloudWatchMetrics(t *testing.T) { ...@@ -14,7 +14,8 @@ func TestCloudWatchMetrics(t *testing.T) {
region := "us-east-1" region := "us-east-1"
namespace := "Foo" namespace := "Foo"
database := "default" database := "default"
f := func(region string, namespace string, database string) (cloudwatch.ListMetricsOutput, error) { assumeRoleArn := ""
f := func(region string, namespace string, database string, assumeRoleArn string) (cloudwatch.ListMetricsOutput, error) {
return cloudwatch.ListMetricsOutput{ return cloudwatch.ListMetricsOutput{
Metrics: []*cloudwatch.Metric{ Metrics: []*cloudwatch.Metric{
{ {
...@@ -28,7 +29,7 @@ func TestCloudWatchMetrics(t *testing.T) { ...@@ -28,7 +29,7 @@ func TestCloudWatchMetrics(t *testing.T) {
}, },
}, nil }, nil
} }
metrics, _ := getMetricsForCustomMetrics(region, namespace, database, f) metrics, _ := getMetricsForCustomMetrics(region, namespace, database, assumeRoleArn, f)
Convey("Should contain Test_MetricName", func() { Convey("Should contain Test_MetricName", func() {
So(metrics, ShouldContain, "Test_MetricName") So(metrics, ShouldContain, "Test_MetricName")
...@@ -39,7 +40,8 @@ func TestCloudWatchMetrics(t *testing.T) { ...@@ -39,7 +40,8 @@ func TestCloudWatchMetrics(t *testing.T) {
region := "us-east-1" region := "us-east-1"
namespace := "Foo" namespace := "Foo"
database := "default" database := "default"
f := func(region string, namespace string, database string) (cloudwatch.ListMetricsOutput, error) { assumeRoleArn := ""
f := func(region string, namespace string, database string, assumeRoleArn string) (cloudwatch.ListMetricsOutput, error) {
return cloudwatch.ListMetricsOutput{ return cloudwatch.ListMetricsOutput{
Metrics: []*cloudwatch.Metric{ Metrics: []*cloudwatch.Metric{
{ {
...@@ -53,7 +55,7 @@ func TestCloudWatchMetrics(t *testing.T) { ...@@ -53,7 +55,7 @@ func TestCloudWatchMetrics(t *testing.T) {
}, },
}, nil }, nil
} }
dimensionKeys, _ := getDimensionsForCustomMetrics(region, namespace, database, f) dimensionKeys, _ := getDimensionsForCustomMetrics(region, namespace, database, assumeRoleArn, f)
Convey("Should contain Test_DimensionName", func() { Convey("Should contain Test_DimensionName", func() {
So(dimensionKeys, ShouldContain, "Test_DimensionName") So(dimensionKeys, ShouldContain, "Test_DimensionName")
......
...@@ -24,4 +24,11 @@ ...@@ -24,4 +24,11 @@
Namespaces of Custom Metrics Namespaces of Custom Metrics
</info-popover> </info-popover>
</div> </div>
<div class="gf-form">
<label class="gf-form-label width-13">Assume Role ARN</label>
<input type="text" class="gf-form-input max-width-18" ng-model='ctrl.current.jsonData.assumeRoleArn' placeholder="arn:aws:iam:*"></input>
<info-popover mode="right-absolute">
ARN of Assume Role
</info-popover>
</div>
</div> </div>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment