Commit 3c662ade by Carl Bergquist Committed by GitHub

Merge pull request #6698 from grafana/cloudwatch_configurable_keys

Cloudwatch configurable keys from config page.
parents 180940a3 7bc1c3cc
......@@ -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
}
......
......@@ -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 {
......
......@@ -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")
......
......@@ -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
}
......@@ -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
......
......@@ -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();
......
///<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'},
];
}
......@@ -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';
......
<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>
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