Explorar o código

Merge pull request #6698 from grafana/cloudwatch_configurable_keys

Cloudwatch configurable keys from config page.
Carl Bergquist %!s(int64=9) %!d(string=hai) anos
pai
achega
3c662ade8c

+ 47 - 9
pkg/api/cloudwatch/cloudwatch.go

@@ -33,6 +33,39 @@ 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 := ""
+	secretKey := ""
+
+	for key, value := range req.DataSource.SecureJsonData.Decrypt() {
+		if key == "accessKey" {
+			accessKey = value
+		}
+		if key == "secretKey" {
+			secretKey = value
+		}
+	}
+
+	return &datasourceInfo{
+		AssumeRoleArn: assumeRoleArn,
+		Region:        req.Region,
+		Profile:       req.DataSource.Database,
+		AccessKey:     accessKey,
+		SecretKey:     secretKey,
+	}
+}
+
 func init() {
 	actionHandlers = map[string]actionHandler{
 		"GetMetricStatistics":     handleGetMetricStatistics,
@@ -56,8 +89,8 @@ type cache struct {
 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(dsInfo *datasourceInfo) *credentials.Credentials {
+	cacheKey := dsInfo.Profile + ":" + dsInfo.AssumeRoleArn
 	credentialCacheLock.RLock()
 	if _, ok := awsCredentialCache[cacheKey]; ok {
 		if awsCredentialCache[cacheKey].expiration != nil &&
@@ -74,9 +107,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(dsInfo.AssumeRoleArn, "arn:aws:iam:") == 0 {
 		params := &sts.AssumeRoleInput{
-			RoleArn:         aws.String(assumeRoleArn),
+			RoleArn:         aws.String(dsInfo.AssumeRoleArn),
 			RoleSessionName: aws.String("GrafanaSession"),
 			DurationSeconds: aws.Int64(900),
 		}
@@ -85,13 +118,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: dsInfo.Profile},
 				&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(stsSess), ExpiryWindow: 5 * time.Minute},
 			})
 		stsConfig := &aws.Config{
-			Region:      aws.String(region),
+			Region:      aws.String(dsInfo.Region),
 			Credentials: stsCreds,
 		}
+
 		svc := sts.New(session.New(stsConfig), stsConfig)
 		resp, err := svc.AssumeRole(params)
 		if err != nil {
@@ -115,9 +149,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:     dsInfo.AccessKey,
+				SecretAccessKey: dsInfo.SecretKey,
+			}},
+			&credentials.SharedCredentialsProvider{Filename: "", Profile: dsInfo.Profile},
 			&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
 		})
+
 	credentialCacheLock.Lock()
 	awsCredentialCache[cacheKey] = cache{
 		credential: creds,
@@ -129,10 +168,9 @@ func getCredentials(profile string, region string, assumeRoleArn string) *creden
 }
 
 func getAwsConfig(req *cwRequest) *aws.Config {
-	assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString()
 	cfg := &aws.Config{
 		Region:      aws.String(req.Region),
-		Credentials: getCredentials(req.DataSource.Database, req.Region, assumeRoleArn),
+		Credentials: getCredentials(req.GetDatasourceInfo()),
 	}
 	return cfg
 }

+ 44 - 40
pkg/api/cloudwatch/metrics.go

@@ -192,8 +192,10 @@ 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 {
+		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)
 			return
 		}
@@ -226,8 +228,10 @@ 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 {
+		dsInfo := req.GetDatasourceInfo()
+		dsInfo.Namespace = reqParam.Parameters.Namespace
+
+		if dimensionValues, err = getDimensionsForCustomMetrics(dsInfo, getAllMetrics); err != nil {
 			c.JsonApiErr(500, "Unable to call AWS API", err)
 			return
 		}
@@ -242,16 +246,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 *datasourceInfo) (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 +276,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(dsInfo *datasourceInfo, getAllMetrics func(*datasourceInfo) (cloudwatch.ListMetricsOutput, error)) ([]string, error) {
+	result, err := getAllMetrics(dsInfo)
 	if err != nil {
 		return []string{}, err
 	}
@@ -281,37 +285,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[dsInfo.Profile]; !ok {
+		customMetricsMetricsMap[dsInfo.Profile] = make(map[string]map[string]*CustomMetricsCache)
 	}
-	if _, ok := customMetricsMetricsMap[database][region]; !ok {
-		customMetricsMetricsMap[database][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[database][region][namespace]; !ok {
-		customMetricsMetricsMap[database][region][namespace] = &CustomMetricsCache{}
-		customMetricsMetricsMap[database][region][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[database][region][namespace].Expire.After(time.Now()) {
-		return customMetricsMetricsMap[database][region][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[database][region][namespace].Cache = make([]string, 0)
-	customMetricsMetricsMap[database][region][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[database][region][namespace].Cache, *metric.MetricName) {
+		if isDuplicate(customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, *metric.MetricName) {
 			continue
 		}
-		customMetricsMetricsMap[database][region][namespace].Cache = append(customMetricsMetricsMap[database][region][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[database][region][namespace].Cache, nil
+	return customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.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(dsInfo *datasourceInfo, getAllMetrics func(*datasourceInfo) (cloudwatch.ListMetricsOutput, error)) ([]string, error) {
+	result, err := getAllMetrics(dsInfo)
 	if err != nil {
 		return []string{}, err
 	}
@@ -319,33 +323,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[dsInfo.Profile]; !ok {
+		customMetricsDimensionsMap[dsInfo.Profile] = make(map[string]map[string]*CustomMetricsCache)
 	}
-	if _, ok := customMetricsDimensionsMap[database][region]; !ok {
-		customMetricsDimensionsMap[database][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[database][region][namespace]; !ok {
-		customMetricsDimensionsMap[database][region][namespace] = &CustomMetricsCache{}
-		customMetricsDimensionsMap[database][region][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[database][region][namespace].Expire.After(time.Now()) {
-		return customMetricsDimensionsMap[database][region][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[database][region][namespace].Cache = make([]string, 0)
-	customMetricsDimensionsMap[database][region][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[database][region][namespace].Cache, *dimension.Name) {
+			if isDuplicate(customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, *dimension.Name) {
 				continue
 			}
-			customMetricsDimensionsMap[database][region][namespace].Cache = append(customMetricsDimensionsMap[database][region][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[database][region][namespace].Cache, nil
+	return customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil
 }
 
 func isDuplicate(nameList []string, target string) bool {

+ 16 - 12
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 := &datasourceInfo{
+			Region:        "us-east-1",
+			Namespace:     "Foo",
+			Profile:       "default",
+			AssumeRoleArn: "",
+		}
+		f := func(dsInfo *datasourceInfo) (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 := &datasourceInfo{
+			Region:        "us-east-1",
+			Namespace:     "Foo",
+			Profile:       "default",
+			AssumeRoleArn: "",
+		}
+		f := func(dsInfo *datasourceInfo) (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")

+ 6 - 0
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
 }

+ 18 - 18
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

+ 0 - 1
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();

+ 51 - 0
public/app/plugins/datasource/cloudwatch/config_ctrl.ts

@@ -0,0 +1,51 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import angular from 'angular';
+import _ from 'lodash';
+
+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 = [
+    {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'},
+  ];
+}
+

+ 1 - 4
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';

+ 53 - 30
public/app/plugins/datasource/cloudwatch/partials/config.html

@@ -1,34 +1,57 @@
 <h3 class="page-heading">CloudWatch details</h3>
 
 <div class="gf-form-group max-width-30">
-	<div class="gf-form">
-		<label class="gf-form-label width-13">Credentials profile name</label>
-		<input type="text" class="gf-form-input max-width-18" ng-model='ctrl.current.database' placeholder="default"></input>
-		<info-popover mode="right-absolute">
-			Credentials profile name, as specified in ~/.aws/credentials, leave blank for default
-		</info-popover>
-	</div>
-	<div class="gf-form">
-		<label class="gf-form-label width-13">Default Region</label>
-		<div class="gf-form-select-wrapper max-width-18 gf-form-select-wrapper--has-help-icon">
-			<select class="gf-form-input" ng-model="ctrl.current.jsonData.defaultRegion" ng-options="region for region in ['ap-northeast-1', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2', 'ap-south-1', 'cn-north-1', 'eu-central-1', 'eu-west-1', 'sa-east-1', 'us-east-1', 'us-east-2', 'us-gov-west-1', 'us-west-1', 'us-west-2']"></select>
-			<info-popover mode="right-absolute">
-				Specify the region, such as for US West (Oregon) use ` us-west-2 ` as the region.
-			</info-popover>
-		</div>
-	</div>
-	<div class="gf-form">
-		<label class="gf-form-label width-13">Custom Metrics namespace</label>
-		<input type="text" class="gf-form-input max-width-18" ng-model='ctrl.current.jsonData.customMetricsNamespaces' placeholder="Namespace1,Namespace2"></input>
-		<info-popover mode="right-absolute">
-			Namespaces of Custom Metrics
-		</info-popover>
-	</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 class="gf-form">
+    <label class="gf-form-label width-13">Auth Provider</label>
+    <select class="gf-form-input gf-max-width-13" ng-model="ctrl.current.jsonData.authType" ng-options="f.value as f.name for f in ctrl.authTypes"></select>
+  </div>
+
+  <div class="gf-form" ng-show='ctrl.current.jsonData.authType == "credentials"'>
+    <label class="gf-form-label width-13">Credentials profile name</label>
+    <input type="text" class="gf-form-input max-width-18" ng-model='ctrl.current.database' placeholder="default"></input>
+    <info-popover mode="right-absolute">
+      Credentials profile name, as specified in ~/.aws/credentials, leave blank for default
+    </info-popover>
+  </div>
+  <div class="gf-form" ng-show='ctrl.current.jsonData.authType == "keys"'>
+    <label class="gf-form-label width-13">Access key </label>
+    <label class="gf-form-label width-13" ng-show="ctrl.accessKeyExist">Configured</label>
+    <a class="gf-form-button btn btn-danger btn-small" type="submit" ng-click="ctrl.resetAccessKey()" ng-show="ctrl.accessKeyExist">Reset</a>
+    <input type="text"
+      class="gf-form-input max-width-18"
+      ng-hide="ctrl.accessKeyExist"
+      ng-model='ctrl.current.secureJsonData.accessKey'></input>
+  </div>
+  <div class="gf-form" ng-show='ctrl.current.jsonData.authType == "keys"'>
+    <label class="gf-form-label width-13">Secret key</label>
+    <label class="gf-form-label width-13" ng-show="ctrl.secretKeyExist">Configured</label>
+    <a class="btn btn-danger gf-form-button btn-small" type="submit" ng-click="ctrl.resetSecretKey()" ng-show="ctrl.secretKeyExist">Reset</a>
+    <input type="text"
+      class="gf-form-input max-width-18"
+      ng-hide="ctrl.secretKeyExist"
+      ng-model='ctrl.current.secureJsonData.secretKey'></input>
+  </div>
+  <div class="gf-form" ng-show='ctrl.current.jsonData.authType == "arn"'>
+    <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 class="gf-form">
+    <label class="gf-form-label width-13">Default Region</label>
+    <div class="gf-form-select-wrapper max-width-18 gf-form-select-wrapper--has-help-icon">
+      <select class="gf-form-input" ng-model="ctrl.current.jsonData.defaultRegion" ng-options="region for region in ['ap-northeast-1', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2', 'ap-south-1', 'cn-north-1', 'eu-central-1', 'eu-west-1', 'sa-east-1', 'us-east-1', 'us-east-2', 'us-gov-west-1', 'us-west-1', 'us-west-2']"></select>
+      <info-popover mode="right-absolute">
+        Specify the region, such as for US West (Oregon) use ` us-west-2 ` as the region.
+      </info-popover>
+    </div>
+  </div>
+  <div class="gf-form">
+    <label class="gf-form-label width-13">Custom Metrics namespace</label>
+    <input type="text" class="gf-form-input max-width-18" ng-model='ctrl.current.jsonData.customMetricsNamespaces' placeholder="Namespace1,Namespace2"></input>
+    <info-popover mode="right-absolute">
+      Namespaces of Custom Metrics
+    </info-popover>
+  </div>
 </div>