From 64784db87018f146fa7c59acde649e90e5789276 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 23 Nov 2016 16:21:51 +0100 Subject: [PATCH 1/6] feat(cloudwatch): adds access and secret key to edit config page --- pkg/api/cloudwatch/cloudwatch.go | 43 ++++++-- pkg/api/cloudwatch/metrics.go | 99 ++++++++++++------- .../cloudwatch/partials/config.html | 74 ++++++++------ 3 files changed, 139 insertions(+), 77 deletions(-) diff --git a/pkg/api/cloudwatch/cloudwatch.go b/pkg/api/cloudwatch/cloudwatch.go index 4e9c6cc9064..c04f6452dc7 100644 --- a/pkg/api/cloudwatch/cloudwatch.go +++ b/pkg/api/cloudwatch/cloudwatch.go @@ -53,11 +53,21 @@ type cache struct { expiration *time.Time } +type CloudwatchDatasource struct { + Profile string + Region string + AssumeRoleArn string + Namespace string + + AccessKey string + SecretKey string +} + var awsCredentialCache map[string]cache = make(map[string]cache) var credentialCacheLock sync.RWMutex -func getCredentials(profile string, region string, assumeRoleArn string) *credentials.Credentials { - cacheKey := profile + ":" + assumeRoleArn +func getCredentials(cwDatasource *CloudwatchDatasource) *credentials.Credentials { + cacheKey := cwDatasource.Profile + ":" + cwDatasource.AssumeRoleArn credentialCacheLock.RLock() if _, ok := awsCredentialCache[cacheKey]; ok { if awsCredentialCache[cacheKey].expiration != nil && @@ -74,9 +84,9 @@ func getCredentials(profile string, region string, assumeRoleArn string) *creden sessionToken := "" var expiration *time.Time expiration = nil - if strings.Index(assumeRoleArn, "arn:aws:iam:") == 0 { + if strings.Index(cwDatasource.AssumeRoleArn, "arn:aws:iam:") == 0 { params := &sts.AssumeRoleInput{ - RoleArn: aws.String(assumeRoleArn), + RoleArn: aws.String(cwDatasource.AssumeRoleArn), RoleSessionName: aws.String("GrafanaSession"), DurationSeconds: aws.Int64(900), } @@ -85,13 +95,14 @@ func getCredentials(profile string, region string, assumeRoleArn string) *creden stsCreds := credentials.NewChainCredentials( []credentials.Provider{ &credentials.EnvProvider{}, - &credentials.SharedCredentialsProvider{Filename: "", Profile: profile}, + &credentials.SharedCredentialsProvider{Filename: "", Profile: cwDatasource.Profile}, &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(stsSess), ExpiryWindow: 5 * time.Minute}, }) stsConfig := &aws.Config{ - Region: aws.String(region), + Region: aws.String(cwDatasource.Region), Credentials: stsCreds, } + svc := sts.New(session.New(stsConfig), stsConfig) resp, err := svc.AssumeRole(params) if err != nil { @@ -115,9 +126,14 @@ func getCredentials(profile string, region string, assumeRoleArn string) *creden SessionToken: sessionToken, }}, &credentials.EnvProvider{}, - &credentials.SharedCredentialsProvider{Filename: "", Profile: profile}, + &credentials.StaticProvider{Value: credentials.Value{ + AccessKeyID: cwDatasource.AccessKey, + SecretAccessKey: cwDatasource.SecretKey, + }}, + &credentials.SharedCredentialsProvider{Filename: "", Profile: cwDatasource.Profile}, &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute}, }) + credentialCacheLock.Lock() awsCredentialCache[cacheKey] = cache{ credential: creds, @@ -130,9 +146,18 @@ func getCredentials(profile string, region string, assumeRoleArn string) *creden func getAwsConfig(req *cwRequest) *aws.Config { assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString() + accessKey := req.DataSource.JsonData.Get("accessKey").MustString() + secretKey := req.DataSource.JsonData.Get("secretKey").MustString() + cfg := &aws.Config{ - Region: aws.String(req.Region), - Credentials: getCredentials(req.DataSource.Database, req.Region, assumeRoleArn), + Region: aws.String(req.Region), + Credentials: getCredentials(&CloudwatchDatasource{ + AccessKey: accessKey, + SecretKey: secretKey, + Region: req.Region, + Profile: req.DataSource.Database, + AssumeRoleArn: assumeRoleArn, + }), } return cfg } diff --git a/pkg/api/cloudwatch/metrics.go b/pkg/api/cloudwatch/metrics.go index 68843ba5774..d8b3ba423c4 100644 --- a/pkg/api/cloudwatch/metrics.go +++ b/pkg/api/cloudwatch/metrics.go @@ -193,7 +193,19 @@ func handleGetMetrics(req *cwRequest, c *middleware.Context) { } else { var err error assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString() - if namespaceMetrics, err = getMetricsForCustomMetrics(req.Region, reqParam.Parameters.Namespace, req.DataSource.Database, assumeRoleArn, getAllMetrics); err != nil { + accessKey := req.DataSource.JsonData.Get("accessKey").MustString() + secretKey := req.DataSource.JsonData.Get("secretKey").MustString() + + cwData := &CloudwatchDatasource{ + AssumeRoleArn: assumeRoleArn, + Region: req.Region, + Namespace: reqParam.Parameters.Namespace, + Profile: req.DataSource.Database, + AccessKey: accessKey, + SecretKey: secretKey, + } + + if namespaceMetrics, err = getMetricsForCustomMetrics(cwData, getAllMetrics); err != nil { c.JsonApiErr(500, "Unable to call AWS API", err) return } @@ -227,7 +239,18 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { } else { var err error assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString() - if dimensionValues, err = getDimensionsForCustomMetrics(req.Region, reqParam.Parameters.Namespace, req.DataSource.Database, assumeRoleArn, getAllMetrics); err != nil { + accessKey := req.DataSource.JsonData.Get("accessKey").MustString() + secretKey := req.DataSource.JsonData.Get("secretKey").MustString() + + cwDatasource := &CloudwatchDatasource{ + Region: req.Region, + Namespace: reqParam.Parameters.Namespace, + Profile: req.DataSource.Database, + AssumeRoleArn: assumeRoleArn, + AccessKey: accessKey, + SecretKey: secretKey, + } + if dimensionValues, err = getDimensionsForCustomMetrics(cwDatasource, getAllMetrics); err != nil { c.JsonApiErr(500, "Unable to call AWS API", err) return } @@ -242,16 +265,16 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { c.JSON(200, result) } -func getAllMetrics(region string, namespace string, database string, assumeRoleArn string) (cloudwatch.ListMetricsOutput, error) { +func getAllMetrics(cwData *CloudwatchDatasource) (cloudwatch.ListMetricsOutput, error) { cfg := &aws.Config{ - Region: aws.String(region), - Credentials: getCredentials(database, region, assumeRoleArn), + Region: aws.String(cwData.Region), + Credentials: getCredentials(cwData), } svc := cloudwatch.New(session.New(cfg), cfg) params := &cloudwatch.ListMetricsInput{ - Namespace: aws.String(namespace), + Namespace: aws.String(cwData.Namespace), } var resp cloudwatch.ListMetricsOutput @@ -272,8 +295,8 @@ func getAllMetrics(region string, namespace string, database string, assumeRoleA var metricsCacheLock sync.Mutex -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, assumeRoleArn) +func getMetricsForCustomMetrics(cwDatasource *CloudwatchDatasource, getAllMetrics func(*CloudwatchDatasource) (cloudwatch.ListMetricsOutput, error)) ([]string, error) { + result, err := getAllMetrics(cwDatasource) if err != nil { return []string{}, err } @@ -281,37 +304,37 @@ func getMetricsForCustomMetrics(region string, namespace string, database string metricsCacheLock.Lock() defer metricsCacheLock.Unlock() - if _, ok := customMetricsMetricsMap[database]; !ok { - customMetricsMetricsMap[database] = make(map[string]map[string]*CustomMetricsCache) + if _, ok := customMetricsMetricsMap[cwDatasource.Profile]; !ok { + customMetricsMetricsMap[cwDatasource.Profile] = make(map[string]map[string]*CustomMetricsCache) } - if _, ok := customMetricsMetricsMap[database][region]; !ok { - customMetricsMetricsMap[database][region] = make(map[string]*CustomMetricsCache) + if _, ok := customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region]; !ok { + customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region] = make(map[string]*CustomMetricsCache) } - if _, ok := customMetricsMetricsMap[database][region][namespace]; !ok { - customMetricsMetricsMap[database][region][namespace] = &CustomMetricsCache{} - customMetricsMetricsMap[database][region][namespace].Cache = make([]string, 0) + if _, ok := customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace]; !ok { + customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace] = &CustomMetricsCache{} + customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = make([]string, 0) } - if customMetricsMetricsMap[database][region][namespace].Expire.After(time.Now()) { - return customMetricsMetricsMap[database][region][namespace].Cache, nil + if customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Expire.After(time.Now()) { + return customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, nil } - customMetricsMetricsMap[database][region][namespace].Cache = make([]string, 0) - customMetricsMetricsMap[database][region][namespace].Expire = time.Now().Add(5 * time.Minute) + customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = make([]string, 0) + customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Expire = time.Now().Add(5 * time.Minute) for _, metric := range result.Metrics { - if isDuplicate(customMetricsMetricsMap[database][region][namespace].Cache, *metric.MetricName) { + if isDuplicate(customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, *metric.MetricName) { continue } - customMetricsMetricsMap[database][region][namespace].Cache = append(customMetricsMetricsMap[database][region][namespace].Cache, *metric.MetricName) + customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = append(customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, *metric.MetricName) } - return customMetricsMetricsMap[database][region][namespace].Cache, nil + return customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, nil } var dimensionsCacheLock sync.Mutex -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, assumeRoleArn) +func getDimensionsForCustomMetrics(cwDatasource *CloudwatchDatasource, getAllMetrics func(*CloudwatchDatasource) (cloudwatch.ListMetricsOutput, error)) ([]string, error) { + result, err := getAllMetrics(cwDatasource) if err != nil { return []string{}, err } @@ -319,33 +342,33 @@ func getDimensionsForCustomMetrics(region string, namespace string, database str dimensionsCacheLock.Lock() defer dimensionsCacheLock.Unlock() - if _, ok := customMetricsDimensionsMap[database]; !ok { - customMetricsDimensionsMap[database] = make(map[string]map[string]*CustomMetricsCache) + if _, ok := customMetricsDimensionsMap[cwDatasource.Profile]; !ok { + customMetricsDimensionsMap[cwDatasource.Profile] = make(map[string]map[string]*CustomMetricsCache) } - if _, ok := customMetricsDimensionsMap[database][region]; !ok { - customMetricsDimensionsMap[database][region] = make(map[string]*CustomMetricsCache) + if _, ok := customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region]; !ok { + customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region] = make(map[string]*CustomMetricsCache) } - if _, ok := customMetricsDimensionsMap[database][region][namespace]; !ok { - customMetricsDimensionsMap[database][region][namespace] = &CustomMetricsCache{} - customMetricsDimensionsMap[database][region][namespace].Cache = make([]string, 0) + if _, ok := customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace]; !ok { + customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace] = &CustomMetricsCache{} + customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = make([]string, 0) } - if customMetricsDimensionsMap[database][region][namespace].Expire.After(time.Now()) { - return customMetricsDimensionsMap[database][region][namespace].Cache, nil + if customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Expire.After(time.Now()) { + return customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, nil } - customMetricsDimensionsMap[database][region][namespace].Cache = make([]string, 0) - customMetricsDimensionsMap[database][region][namespace].Expire = time.Now().Add(5 * time.Minute) + customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = make([]string, 0) + customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Expire = time.Now().Add(5 * time.Minute) for _, metric := range result.Metrics { for _, dimension := range metric.Dimensions { - if isDuplicate(customMetricsDimensionsMap[database][region][namespace].Cache, *dimension.Name) { + if isDuplicate(customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, *dimension.Name) { continue } - customMetricsDimensionsMap[database][region][namespace].Cache = append(customMetricsDimensionsMap[database][region][namespace].Cache, *dimension.Name) + customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = append(customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, *dimension.Name) } } - return customMetricsDimensionsMap[database][region][namespace].Cache, nil + return customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, nil } func isDuplicate(nameList []string, target string) bool { diff --git a/public/app/plugins/datasource/cloudwatch/partials/config.html b/public/app/plugins/datasource/cloudwatch/partials/config.html index 89e52e76ace..94c495d8fbf 100644 --- a/public/app/plugins/datasource/cloudwatch/partials/config.html +++ b/public/app/plugins/datasource/cloudwatch/partials/config.html @@ -1,34 +1,48 @@

CloudWatch details

-
- - - - Credentials profile name, as specified in ~/.aws/credentials, leave blank for default - -
-
- -
- - - Specify the region, such as for US West (Oregon) use ` us-west-2 ` as the region. - -
-
-
- - - - Namespaces of Custom Metrics - -
-
- - - - ARN of Assume Role - -
+
+ + + + Credentials profile name, as specified in ~/.aws/credentials, leave blank for default + +
+
+ + + + Accesskey + +
+
+ + + + Secret key + +
+
+ +
+ + + Specify the region, such as for US West (Oregon) use ` us-west-2 ` as the region. + +
+
+
+ + + + Namespaces of Custom Metrics + +
+
+ + + + ARN of Assume Role + +
From 1695aece0c59f58375079aa13b8157845781b9e8 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 24 Nov 2016 08:39:36 +0100 Subject: [PATCH 2/6] feat(cloudwatch): add authtype dropdown to config page --- .../datasource/cloudwatch/config_ctrl.ts | 31 +++++++++++++++++++ .../plugins/datasource/cloudwatch/module.ts | 5 +-- .../cloudwatch/partials/config.html | 31 +++++++++++-------- 3 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 public/app/plugins/datasource/cloudwatch/config_ctrl.ts diff --git a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts new file mode 100644 index 00000000000..8f9a8a970ba --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts @@ -0,0 +1,31 @@ +/// + +import angular from 'angular'; +import _ from 'lodash'; + +export class CloudWatchConfigCtrl { + static templateUrl = 'partials/config.html'; + current: any; + + /** @ngInject */ + constructor($scope) { + this.current.jsonData.timeField = this.current.jsonData.timeField || '@timestamp'; + this.current.jsonData.authType = this.current.jsonData.authType || 'credentials'; + } + + authTypes = [ + {name: 'Access & secret key', value: 'keys'}, + {name: 'Credentials file', value: 'credentials'}, + {name: 'ARN', value: 'arn'}, + ]; + + indexPatternTypes = [ + {name: 'No pattern', value: undefined}, + {name: 'Hourly', value: 'Hourly', example: '[logstash-]YYYY.MM.DD.HH'}, + {name: 'Daily', value: 'Daily', example: '[logstash-]YYYY.MM.DD'}, + {name: 'Weekly', value: 'Weekly', example: '[logstash-]GGGG.WW'}, + {name: 'Monthly', value: 'Monthly', example: '[logstash-]YYYY.MM'}, + {name: 'Yearly', value: 'Yearly', example: '[logstash-]YYYY'}, + ]; +} + diff --git a/public/app/plugins/datasource/cloudwatch/module.ts b/public/app/plugins/datasource/cloudwatch/module.ts index 1d81c429d86..e2e70ca5f7c 100644 --- a/public/app/plugins/datasource/cloudwatch/module.ts +++ b/public/app/plugins/datasource/cloudwatch/module.ts @@ -2,10 +2,7 @@ import './query_parameter_ctrl'; import {CloudWatchDatasource} from './datasource'; import {CloudWatchQueryCtrl} from './query_ctrl'; - -class CloudWatchConfigCtrl { - static templateUrl = 'partials/config.html'; -} +import {CloudWatchConfigCtrl} from './config_ctrl'; class CloudWatchAnnotationsQueryCtrl { static templateUrl = 'partials/annotations.editor.html'; diff --git a/public/app/plugins/datasource/cloudwatch/partials/config.html b/public/app/plugins/datasource/cloudwatch/partials/config.html index 94c495d8fbf..c877ef470b0 100644 --- a/public/app/plugins/datasource/cloudwatch/partials/config.html +++ b/public/app/plugins/datasource/cloudwatch/partials/config.html @@ -2,24 +2,36 @@
+ + +
+ +
Credentials profile name, as specified in ~/.aws/credentials, leave blank for default
-
- +
+ - Accesskey + AWS Access key id
-
- +
+ - Secret key + AWS Secret key + +
+
+ + + + ARN of Assume Role
@@ -38,11 +50,4 @@ Namespaces of Custom Metrics
-
- - - - ARN of Assume Role - -
From f7e12e5f93ffeab58bbe1f0d627c4709d1b35d55 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 24 Nov 2016 09:05:02 +0100 Subject: [PATCH 3/6] test(cloudwatch): fixes failing tests --- pkg/api/cloudwatch/metrics_test.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/pkg/api/cloudwatch/metrics_test.go b/pkg/api/cloudwatch/metrics_test.go index 6dd68037613..1595114329a 100644 --- a/pkg/api/cloudwatch/metrics_test.go +++ b/pkg/api/cloudwatch/metrics_test.go @@ -11,11 +11,13 @@ import ( func TestCloudWatchMetrics(t *testing.T) { Convey("When calling getMetricsForCustomMetrics", t, func() { - region := "us-east-1" - namespace := "Foo" - database := "default" - assumeRoleArn := "" - f := func(region string, namespace string, database string, assumeRoleArn string) (cloudwatch.ListMetricsOutput, error) { + dsInfo := &CloudwatchDatasource{ + Region: "us-east-1", + Namespace: "Foo", + Profile: "default", + AssumeRoleArn: "", + } + f := func(dsInfo *CloudwatchDatasource) (cloudwatch.ListMetricsOutput, error) { return cloudwatch.ListMetricsOutput{ Metrics: []*cloudwatch.Metric{ { @@ -29,7 +31,7 @@ func TestCloudWatchMetrics(t *testing.T) { }, }, nil } - metrics, _ := getMetricsForCustomMetrics(region, namespace, database, assumeRoleArn, f) + metrics, _ := getMetricsForCustomMetrics(dsInfo, f) Convey("Should contain Test_MetricName", func() { So(metrics, ShouldContain, "Test_MetricName") @@ -37,11 +39,13 @@ func TestCloudWatchMetrics(t *testing.T) { }) Convey("When calling getDimensionsForCustomMetrics", t, func() { - region := "us-east-1" - namespace := "Foo" - database := "default" - assumeRoleArn := "" - f := func(region string, namespace string, database string, assumeRoleArn string) (cloudwatch.ListMetricsOutput, error) { + dsInfo := &CloudwatchDatasource{ + Region: "us-east-1", + Namespace: "Foo", + Profile: "default", + AssumeRoleArn: "", + } + f := func(dsInfo *CloudwatchDatasource) (cloudwatch.ListMetricsOutput, error) { return cloudwatch.ListMetricsOutput{ Metrics: []*cloudwatch.Metric{ { @@ -55,7 +59,7 @@ func TestCloudWatchMetrics(t *testing.T) { }, }, nil } - dimensionKeys, _ := getDimensionsForCustomMetrics(region, namespace, database, assumeRoleArn, f) + dimensionKeys, _ := getDimensionsForCustomMetrics(dsInfo, f) Convey("Should contain Test_DimensionName", func() { So(dimensionKeys, ShouldContain, "Test_DimensionName") From 0ea653791676eca7147cba94b7e20180b3af3753 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 24 Nov 2016 09:21:57 +0100 Subject: [PATCH 4/6] tech(cloudwatch): refactoring --- pkg/api/cloudwatch/cloudwatch.go | 66 +++++++++++---------- pkg/api/cloudwatch/metrics.go | 95 ++++++++++++++++-------------- pkg/api/cloudwatch/metrics_test.go | 8 +-- 3 files changed, 89 insertions(+), 80 deletions(-) diff --git a/pkg/api/cloudwatch/cloudwatch.go b/pkg/api/cloudwatch/cloudwatch.go index c04f6452dc7..c19df76f585 100644 --- a/pkg/api/cloudwatch/cloudwatch.go +++ b/pkg/api/cloudwatch/cloudwatch.go @@ -33,6 +33,30 @@ type cwRequest struct { DataSource *m.DataSource } +type datasourceInfo struct { + Profile string + Region string + AssumeRoleArn string + Namespace string + + AccessKey string + SecretKey string +} + +func (req *cwRequest) GetDatasourceInfo() *datasourceInfo { + assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString() + accessKey := req.DataSource.JsonData.Get("accessKey").MustString() + secretKey := req.DataSource.JsonData.Get("secretKey").MustString() + + return &datasourceInfo{ + AssumeRoleArn: assumeRoleArn, + Region: req.Region, + Profile: req.DataSource.Database, + AccessKey: accessKey, + SecretKey: secretKey, + } +} + func init() { actionHandlers = map[string]actionHandler{ "GetMetricStatistics": handleGetMetricStatistics, @@ -53,21 +77,11 @@ type cache struct { expiration *time.Time } -type CloudwatchDatasource struct { - Profile string - Region string - AssumeRoleArn string - Namespace string - - AccessKey string - SecretKey string -} - var awsCredentialCache map[string]cache = make(map[string]cache) var credentialCacheLock sync.RWMutex -func getCredentials(cwDatasource *CloudwatchDatasource) *credentials.Credentials { - cacheKey := cwDatasource.Profile + ":" + cwDatasource.AssumeRoleArn +func getCredentials(dsInfo *datasourceInfo) *credentials.Credentials { + cacheKey := dsInfo.Profile + ":" + dsInfo.AssumeRoleArn credentialCacheLock.RLock() if _, ok := awsCredentialCache[cacheKey]; ok { if awsCredentialCache[cacheKey].expiration != nil && @@ -84,9 +98,9 @@ func getCredentials(cwDatasource *CloudwatchDatasource) *credentials.Credentials sessionToken := "" var expiration *time.Time expiration = nil - if strings.Index(cwDatasource.AssumeRoleArn, "arn:aws:iam:") == 0 { + if strings.Index(dsInfo.AssumeRoleArn, "arn:aws:iam:") == 0 { params := &sts.AssumeRoleInput{ - RoleArn: aws.String(cwDatasource.AssumeRoleArn), + RoleArn: aws.String(dsInfo.AssumeRoleArn), RoleSessionName: aws.String("GrafanaSession"), DurationSeconds: aws.Int64(900), } @@ -95,11 +109,11 @@ func getCredentials(cwDatasource *CloudwatchDatasource) *credentials.Credentials stsCreds := credentials.NewChainCredentials( []credentials.Provider{ &credentials.EnvProvider{}, - &credentials.SharedCredentialsProvider{Filename: "", Profile: cwDatasource.Profile}, + &credentials.SharedCredentialsProvider{Filename: "", Profile: dsInfo.Profile}, &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(stsSess), ExpiryWindow: 5 * time.Minute}, }) stsConfig := &aws.Config{ - Region: aws.String(cwDatasource.Region), + Region: aws.String(dsInfo.Region), Credentials: stsCreds, } @@ -127,10 +141,10 @@ func getCredentials(cwDatasource *CloudwatchDatasource) *credentials.Credentials }}, &credentials.EnvProvider{}, &credentials.StaticProvider{Value: credentials.Value{ - AccessKeyID: cwDatasource.AccessKey, - SecretAccessKey: cwDatasource.SecretKey, + AccessKeyID: dsInfo.AccessKey, + SecretAccessKey: dsInfo.SecretKey, }}, - &credentials.SharedCredentialsProvider{Filename: "", Profile: cwDatasource.Profile}, + &credentials.SharedCredentialsProvider{Filename: "", Profile: dsInfo.Profile}, &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute}, }) @@ -145,19 +159,9 @@ func getCredentials(cwDatasource *CloudwatchDatasource) *credentials.Credentials } func getAwsConfig(req *cwRequest) *aws.Config { - assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString() - accessKey := req.DataSource.JsonData.Get("accessKey").MustString() - secretKey := req.DataSource.JsonData.Get("secretKey").MustString() - cfg := &aws.Config{ - Region: aws.String(req.Region), - Credentials: getCredentials(&CloudwatchDatasource{ - AccessKey: accessKey, - SecretKey: secretKey, - Region: req.Region, - Profile: req.DataSource.Database, - AssumeRoleArn: assumeRoleArn, - }), + Region: aws.String(req.Region), + Credentials: getCredentials(req.GetDatasourceInfo()), } return cfg } diff --git a/pkg/api/cloudwatch/metrics.go b/pkg/api/cloudwatch/metrics.go index d8b3ba423c4..5744ecad45e 100644 --- a/pkg/api/cloudwatch/metrics.go +++ b/pkg/api/cloudwatch/metrics.go @@ -192,18 +192,23 @@ func handleGetMetrics(req *cwRequest, c *middleware.Context) { } } else { var err error - assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString() - accessKey := req.DataSource.JsonData.Get("accessKey").MustString() - secretKey := req.DataSource.JsonData.Get("secretKey").MustString() + /* + assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString() + accessKey := req.DataSource.JsonData.Get("accessKey").MustString() + secretKey := req.DataSource.JsonData.Get("secretKey").MustString() - cwData := &CloudwatchDatasource{ - AssumeRoleArn: assumeRoleArn, - Region: req.Region, - Namespace: reqParam.Parameters.Namespace, - Profile: req.DataSource.Database, - AccessKey: accessKey, - SecretKey: secretKey, - } + cwData := &datasourceInfo{ + AssumeRoleArn: assumeRoleArn, + Region: req.Region, + Namespace: reqParam.Parameters.Namespace, + Profile: req.DataSource.Database, + AccessKey: accessKey, + SecretKey: secretKey, + } + */ + + cwData := req.GetDatasourceInfo() + cwData.Namespace = reqParam.Parameters.Namespace if namespaceMetrics, err = getMetricsForCustomMetrics(cwData, getAllMetrics); err != nil { c.JsonApiErr(500, "Unable to call AWS API", err) @@ -242,7 +247,7 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { accessKey := req.DataSource.JsonData.Get("accessKey").MustString() secretKey := req.DataSource.JsonData.Get("secretKey").MustString() - cwDatasource := &CloudwatchDatasource{ + cwDatasource := &datasourceInfo{ Region: req.Region, Namespace: reqParam.Parameters.Namespace, Profile: req.DataSource.Database, @@ -265,7 +270,7 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { c.JSON(200, result) } -func getAllMetrics(cwData *CloudwatchDatasource) (cloudwatch.ListMetricsOutput, error) { +func getAllMetrics(cwData *datasourceInfo) (cloudwatch.ListMetricsOutput, error) { cfg := &aws.Config{ Region: aws.String(cwData.Region), Credentials: getCredentials(cwData), @@ -295,8 +300,8 @@ func getAllMetrics(cwData *CloudwatchDatasource) (cloudwatch.ListMetricsOutput, var metricsCacheLock sync.Mutex -func getMetricsForCustomMetrics(cwDatasource *CloudwatchDatasource, getAllMetrics func(*CloudwatchDatasource) (cloudwatch.ListMetricsOutput, error)) ([]string, error) { - result, err := getAllMetrics(cwDatasource) +func getMetricsForCustomMetrics(dsInfo *datasourceInfo, getAllMetrics func(*datasourceInfo) (cloudwatch.ListMetricsOutput, error)) ([]string, error) { + result, err := getAllMetrics(dsInfo) if err != nil { return []string{}, err } @@ -304,37 +309,37 @@ func getMetricsForCustomMetrics(cwDatasource *CloudwatchDatasource, getAllMetric metricsCacheLock.Lock() defer metricsCacheLock.Unlock() - if _, ok := customMetricsMetricsMap[cwDatasource.Profile]; !ok { - customMetricsMetricsMap[cwDatasource.Profile] = make(map[string]map[string]*CustomMetricsCache) + if _, ok := customMetricsMetricsMap[dsInfo.Profile]; !ok { + customMetricsMetricsMap[dsInfo.Profile] = make(map[string]map[string]*CustomMetricsCache) } - if _, ok := customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region]; !ok { - customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region] = make(map[string]*CustomMetricsCache) + if _, ok := customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region]; !ok { + customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region] = make(map[string]*CustomMetricsCache) } - if _, ok := customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace]; !ok { - customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace] = &CustomMetricsCache{} - customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = make([]string, 0) + 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].Cache = make([]string, 0) } - if customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Expire.After(time.Now()) { - return customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, nil + if customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire.After(time.Now()) { + return customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil } - customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = make([]string, 0) - customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Expire = time.Now().Add(5 * time.Minute) + customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = make([]string, 0) + customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire = time.Now().Add(5 * time.Minute) for _, metric := range result.Metrics { - if isDuplicate(customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, *metric.MetricName) { + if isDuplicate(customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, *metric.MetricName) { continue } - customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = append(customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, *metric.MetricName) + customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = append(customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, *metric.MetricName) } - return customMetricsMetricsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, nil + return customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil } var dimensionsCacheLock sync.Mutex -func getDimensionsForCustomMetrics(cwDatasource *CloudwatchDatasource, getAllMetrics func(*CloudwatchDatasource) (cloudwatch.ListMetricsOutput, error)) ([]string, error) { - result, err := getAllMetrics(cwDatasource) +func getDimensionsForCustomMetrics(dsInfo *datasourceInfo, getAllMetrics func(*datasourceInfo) (cloudwatch.ListMetricsOutput, error)) ([]string, error) { + result, err := getAllMetrics(dsInfo) if err != nil { return []string{}, err } @@ -342,33 +347,33 @@ func getDimensionsForCustomMetrics(cwDatasource *CloudwatchDatasource, getAllMet dimensionsCacheLock.Lock() defer dimensionsCacheLock.Unlock() - if _, ok := customMetricsDimensionsMap[cwDatasource.Profile]; !ok { - customMetricsDimensionsMap[cwDatasource.Profile] = make(map[string]map[string]*CustomMetricsCache) + if _, ok := customMetricsDimensionsMap[dsInfo.Profile]; !ok { + customMetricsDimensionsMap[dsInfo.Profile] = make(map[string]map[string]*CustomMetricsCache) } - if _, ok := customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region]; !ok { - customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region] = make(map[string]*CustomMetricsCache) + if _, ok := customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region]; !ok { + customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region] = make(map[string]*CustomMetricsCache) } - if _, ok := customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace]; !ok { - customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace] = &CustomMetricsCache{} - customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = make([]string, 0) + 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].Cache = make([]string, 0) } - if customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Expire.After(time.Now()) { - return customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, nil + if customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire.After(time.Now()) { + return customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil } - customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = make([]string, 0) - customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Expire = time.Now().Add(5 * time.Minute) + customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = make([]string, 0) + customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire = time.Now().Add(5 * time.Minute) for _, metric := range result.Metrics { for _, dimension := range metric.Dimensions { - if isDuplicate(customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, *dimension.Name) { + if isDuplicate(customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, *dimension.Name) { continue } - customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache = append(customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, *dimension.Name) + customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = append(customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, *dimension.Name) } } - return customMetricsDimensionsMap[cwDatasource.Profile][cwDatasource.Region][cwDatasource.Namespace].Cache, nil + return customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil } func isDuplicate(nameList []string, target string) bool { diff --git a/pkg/api/cloudwatch/metrics_test.go b/pkg/api/cloudwatch/metrics_test.go index 1595114329a..4ac8a70a273 100644 --- a/pkg/api/cloudwatch/metrics_test.go +++ b/pkg/api/cloudwatch/metrics_test.go @@ -11,13 +11,13 @@ import ( func TestCloudWatchMetrics(t *testing.T) { Convey("When calling getMetricsForCustomMetrics", t, func() { - dsInfo := &CloudwatchDatasource{ + dsInfo := &datasourceInfo{ Region: "us-east-1", Namespace: "Foo", Profile: "default", AssumeRoleArn: "", } - f := func(dsInfo *CloudwatchDatasource) (cloudwatch.ListMetricsOutput, error) { + f := func(dsInfo *datasourceInfo) (cloudwatch.ListMetricsOutput, error) { return cloudwatch.ListMetricsOutput{ Metrics: []*cloudwatch.Metric{ { @@ -39,13 +39,13 @@ func TestCloudWatchMetrics(t *testing.T) { }) Convey("When calling getDimensionsForCustomMetrics", t, func() { - dsInfo := &CloudwatchDatasource{ + dsInfo := &datasourceInfo{ Region: "us-east-1", Namespace: "Foo", Profile: "default", AssumeRoleArn: "", } - f := func(dsInfo *CloudwatchDatasource) (cloudwatch.ListMetricsOutput, error) { + f := func(dsInfo *datasourceInfo) (cloudwatch.ListMetricsOutput, error) { return cloudwatch.ListMetricsOutput{ Metrics: []*cloudwatch.Metric{ { From 3d21f06d5b21a323fc956844dd961aedf76e2951 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 24 Nov 2016 14:16:52 +0100 Subject: [PATCH 5/6] tech(cloudwatch): store keys in secure json blob --- pkg/api/cloudwatch/cloudwatch.go | 13 +++++++++++-- .../datasource/cloudwatch/partials/config.html | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pkg/api/cloudwatch/cloudwatch.go b/pkg/api/cloudwatch/cloudwatch.go index c19df76f585..991d336411c 100644 --- a/pkg/api/cloudwatch/cloudwatch.go +++ b/pkg/api/cloudwatch/cloudwatch.go @@ -45,8 +45,17 @@ type datasourceInfo struct { func (req *cwRequest) GetDatasourceInfo() *datasourceInfo { assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString() - accessKey := req.DataSource.JsonData.Get("accessKey").MustString() - secretKey := req.DataSource.JsonData.Get("secretKey").MustString() + accessKey := "" + secretKey := "" + + for key, value := range req.DataSource.SecureJsonData.Decrypt() { + if key == "accessKey" { + accessKey = value + } + if key == "secretKey" { + secretKey = value + } + } return &datasourceInfo{ AssumeRoleArn: assumeRoleArn, diff --git a/public/app/plugins/datasource/cloudwatch/partials/config.html b/public/app/plugins/datasource/cloudwatch/partials/config.html index c877ef470b0..9a5a4c68cf5 100644 --- a/public/app/plugins/datasource/cloudwatch/partials/config.html +++ b/public/app/plugins/datasource/cloudwatch/partials/config.html @@ -15,14 +15,14 @@
- + AWS Access key id
- + AWS Secret key From 7bc1c3cc1cb015a7a2e3f942b49e1dec67f970e4 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 24 Nov 2016 16:24:47 +0100 Subject: [PATCH 6/6] feat(cloudwatch): make it possible to reset keys closes #6697 --- pkg/api/cloudwatch/metrics.go | 30 ++-------------- pkg/api/datasources.go | 6 ++++ pkg/api/dtos/models.go | 36 +++++++++---------- public/app/features/plugins/ds_edit_ctrl.ts | 1 - .../datasource/cloudwatch/config_ctrl.ts | 20 +++++++++++ .../cloudwatch/partials/config.html | 24 +++++++------ 6 files changed, 61 insertions(+), 56 deletions(-) diff --git a/pkg/api/cloudwatch/metrics.go b/pkg/api/cloudwatch/metrics.go index 5744ecad45e..1880c6375b0 100644 --- a/pkg/api/cloudwatch/metrics.go +++ b/pkg/api/cloudwatch/metrics.go @@ -192,21 +192,6 @@ func handleGetMetrics(req *cwRequest, c *middleware.Context) { } } else { var err error - /* - assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString() - accessKey := req.DataSource.JsonData.Get("accessKey").MustString() - secretKey := req.DataSource.JsonData.Get("secretKey").MustString() - - cwData := &datasourceInfo{ - AssumeRoleArn: assumeRoleArn, - Region: req.Region, - Namespace: reqParam.Parameters.Namespace, - Profile: req.DataSource.Database, - AccessKey: accessKey, - SecretKey: secretKey, - } - */ - cwData := req.GetDatasourceInfo() cwData.Namespace = reqParam.Parameters.Namespace @@ -243,19 +228,10 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { } } else { var err error - assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString() - accessKey := req.DataSource.JsonData.Get("accessKey").MustString() - secretKey := req.DataSource.JsonData.Get("secretKey").MustString() + dsInfo := req.GetDatasourceInfo() + dsInfo.Namespace = reqParam.Parameters.Namespace - cwDatasource := &datasourceInfo{ - Region: req.Region, - Namespace: reqParam.Parameters.Namespace, - Profile: req.DataSource.Database, - AssumeRoleArn: assumeRoleArn, - AccessKey: accessKey, - SecretKey: secretKey, - } - if dimensionValues, err = getDimensionsForCustomMetrics(cwDatasource, getAllMetrics); err != nil { + if dimensionValues, err = getDimensionsForCustomMetrics(dsInfo, getAllMetrics); err != nil { c.JsonApiErr(500, "Unable to call AWS API", err) return } diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index 87c743c9ce6..cc305221817 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -215,5 +215,11 @@ func convertModelToDtos(ds *m.DataSource) dtos.DataSource { dto.TLSAuth.ClientKeySet = len(ds.SecureJsonData["tlsClientKey"]) > 0 } + for k, v := range ds.SecureJsonData { + if len(v) > 0 { + dto.EncryptedFields = append(dto.EncryptedFields, k) + } + } + return dto } diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index 9f8ae329fec..17ac39fa344 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -64,24 +64,24 @@ type DashboardRedirect struct { } type DataSource struct { - Id int64 `json:"id"` - OrgId int64 `json:"orgId"` - Name string `json:"name"` - Type string `json:"type"` - TypeLogoUrl string `json:"typeLogoUrl"` - Access m.DsAccess `json:"access"` - Url string `json:"url"` - Password string `json:"password"` - User string `json:"user"` - Database string `json:"database"` - BasicAuth bool `json:"basicAuth"` - BasicAuthUser string `json:"basicAuthUser"` - BasicAuthPassword string `json:"basicAuthPassword"` - WithCredentials bool `json:"withCredentials"` - IsDefault bool `json:"isDefault"` - JsonData *simplejson.Json `json:"jsonData,omitempty"` - SecureJsonData map[string]string `json:"secureJsonData,omitempty"` - TLSAuth TLSAuth `json:"tlsAuth,omitempty"` + Id int64 `json:"id"` + OrgId int64 `json:"orgId"` + Name string `json:"name"` + Type string `json:"type"` + TypeLogoUrl string `json:"typeLogoUrl"` + Access m.DsAccess `json:"access"` + Url string `json:"url"` + Password string `json:"password"` + User string `json:"user"` + Database string `json:"database"` + BasicAuth bool `json:"basicAuth"` + BasicAuthUser string `json:"basicAuthUser"` + BasicAuthPassword string `json:"basicAuthPassword"` + WithCredentials bool `json:"withCredentials"` + IsDefault bool `json:"isDefault"` + JsonData *simplejson.Json `json:"jsonData,omitempty"` + TLSAuth TLSAuth `json:"tlsAuth,omitempty"` + EncryptedFields []string `json:"encryptedFields"` } // TLSAuth is used to show if TLS certs have been uploaded already diff --git a/public/app/features/plugins/ds_edit_ctrl.ts b/public/app/features/plugins/ds_edit_ctrl.ts index 7ac29227001..9c808b188c4 100644 --- a/public/app/features/plugins/ds_edit_ctrl.ts +++ b/public/app/features/plugins/ds_edit_ctrl.ts @@ -68,7 +68,6 @@ export class DataSourceEditCtrl { this.backendSrv.get('/api/datasources/' + id).then(ds => { this.isNew = false; this.current = ds; - if (datasourceCreated) { datasourceCreated = false; this.testDatasource(); diff --git a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts index 8f9a8a970ba..a60cd6ae8c9 100644 --- a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts @@ -7,10 +7,30 @@ export class CloudWatchConfigCtrl { static templateUrl = 'partials/config.html'; current: any; + accessKeyExist: boolean = false; + secretKeyExist: boolean = false; + /** @ngInject */ constructor($scope) { this.current.jsonData.timeField = this.current.jsonData.timeField || '@timestamp'; this.current.jsonData.authType = this.current.jsonData.authType || 'credentials'; + + for (let key of this.current.encryptedFields) { + if (key === "accessKey") { + this.accessKeyExist = true; + } + if (key === "secretKey") { + this.secretKeyExist = true; + } + } + } + + resetAccessKey() { + this.accessKeyExist = false; + } + + resetSecretKey() { + this.secretKeyExist = false; } authTypes = [ diff --git a/public/app/plugins/datasource/cloudwatch/partials/config.html b/public/app/plugins/datasource/cloudwatch/partials/config.html index 9a5a4c68cf5..a08ed8be100 100644 --- a/public/app/plugins/datasource/cloudwatch/partials/config.html +++ b/public/app/plugins/datasource/cloudwatch/partials/config.html @@ -3,7 +3,7 @@
- +
@@ -14,18 +14,22 @@
- - - - AWS Access key id - + + + Reset +
- - - AWS Secret key - + + Reset +