diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index bdf661dc4fc..648957ed96e 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -78,11 +78,14 @@ CloudWatch Datasource Plugin provides the following queries you can specify in t edit view. They allow you to fill a variable's options list with things like `region`, `namespaces`, `metric names` and `dimension keys/values`. +In place of `region` you can specify `default` to use the default region configured in the datasource for the query, +e.g. `metrics(AWS/DynamoDB, default)` or `dimension_values(default, ..., ..., ...)`. + Name | Description ------- | -------- *regions()* | Returns a list of regions AWS provides their service. *namespaces()* | Returns a list of namespaces CloudWatch support. -*metrics(namespace, [region])* | Returns a list of metrics in the namespace. (specify region for custom metrics) +*metrics(namespace, [region])* | Returns a list of metrics in the namespace. (specify region or use "default" for custom metrics) *dimension_keys(namespace)* | Returns a list of dimension keys in the namespace. *dimension_values(region, namespace, metric, dimension_key)* | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`. *ebs_volume_ids(region, instance_id)* | Returns a list of volume ids matching the specified `region`, `instance_id`. diff --git a/pkg/tsdb/cloudwatch/credentials.go b/pkg/tsdb/cloudwatch/credentials.go index 784f3b729ac..0c142bd4ea0 100644 --- a/pkg/tsdb/cloudwatch/credentials.go +++ b/pkg/tsdb/cloudwatch/credentials.go @@ -141,6 +141,11 @@ func ec2RoleProvider(sess *session.Session) credentials.Provider { } func (e *CloudWatchExecutor) getDsInfo(region string) *DatasourceInfo { + defaultRegion := e.DataSource.JsonData.Get("defaultRegion").MustString() + if region == "default" { + region = defaultRegion + } + authType := e.DataSource.JsonData.Get("authType").MustString() assumeRoleArn := e.DataSource.JsonData.Get("assumeRoleArn").MustString() accessKey := "" diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index ac4573ef43a..b0d37fd2186 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -39,7 +39,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { !!item.metricName && !_.isEmpty(item.statistics); }).map(function (item) { - item.region = templateSrv.replace(item.region, options.scopedVars); + item.region = templateSrv.replace(self.getActualRegion(item.region), options.scopedVars); item.namespace = templateSrv.replace(item.namespace, options.scopedVars); item.metricName = templateSrv.replace(item.metricName, options.scopedVars); item.dimensions = self.convertDimensionFormat(item.dimensions, options.scopeVars); @@ -165,21 +165,21 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { this.getMetrics = function (namespace, region) { return this.doMetricQueryRequest('metrics', { - region: templateSrv.replace(region), + region: templateSrv.replace(this.getActualRegion(region)), namespace: templateSrv.replace(namespace) }); }; this.getDimensionKeys = function(namespace, region) { return this.doMetricQueryRequest('dimension_keys', { - region: templateSrv.replace(region), + region: templateSrv.replace(this.getActualRegion(region)), namespace: templateSrv.replace(namespace) }); }; this.getDimensionValues = function(region, namespace, metricName, dimensionKey, filterDimensions) { return this.doMetricQueryRequest('dimension_values', { - region: templateSrv.replace(region), + region: templateSrv.replace(this.getActualRegion(region)), namespace: templateSrv.replace(namespace), metricName: templateSrv.replace(metricName), dimensionKey: templateSrv.replace(dimensionKey), @@ -189,14 +189,14 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { this.getEbsVolumeIds = function(region, instanceId) { return this.doMetricQueryRequest('ebs_volume_ids', { - region: templateSrv.replace(region), + region: templateSrv.replace(this.getActualRegion(region)), instanceId: templateSrv.replace(instanceId) }); }; this.getEc2InstanceAttribute = function(region, attributeName, filters) { return this.doMetricQueryRequest('ec2_instance_attribute', { - region: templateSrv.replace(region), + region: templateSrv.replace(this.getActualRegion(region)), attributeName: templateSrv.replace(attributeName), filters: filters }); @@ -267,7 +267,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { period = parseInt(period, 10); var parameters = { prefixMatching: annotation.prefixMatching, - region: templateSrv.replace(annotation.region), + region: templateSrv.replace(this.getActualRegion(annotation.region)), namespace: templateSrv.replace(annotation.namespace), metricName: templateSrv.replace(annotation.metricName), dimensions: this.convertDimensionFormat(annotation.dimensions, {}), @@ -341,6 +341,13 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { return this.defaultRegion; }; + this.getActualRegion = function(region) { + if (region === 'default' || _.isEmpty(region)) { + return this.getDefaultRegion(); + } + return region; + }; + this.getExpandedVariables = function(target, dimensionKey, variable, templateSrv) { /* if the all checkbox is marked we should add all values to the targets */ var allSelected = _.find(variable.options, {'selected': true, 'text': 'All'}); diff --git a/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts b/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts index 57a5fba443b..6bf22b0f2e7 100644 --- a/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts @@ -28,7 +28,7 @@ export class CloudWatchQueryParameterCtrl { target.statistics = target.statistics || ['Average']; target.dimensions = target.dimensions || {}; target.period = target.period || ''; - target.region = target.region || ''; + target.region = target.region || 'default'; $scope.regionSegment = uiSegmentSrv.getSegmentForValue($scope.target.region, 'select region'); $scope.namespaceSegment = uiSegmentSrv.getSegmentForValue($scope.target.namespace, 'select namespace'); @@ -51,7 +51,7 @@ export class CloudWatchQueryParameterCtrl { $scope.removeStatSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove stat --'}); if (_.isEmpty($scope.target.region)) { - $scope.target.region = $scope.datasource.getDefaultRegion(); + $scope.target.region = 'default'; } if (!$scope.onChange) { @@ -148,6 +148,10 @@ export class CloudWatchQueryParameterCtrl { $scope.getRegions = function() { return $scope.datasource.metricFindQuery('regions()') + .then(function(results) { + results.unshift({ text: 'default'}); + return results; + }) .then($scope.transformToSegments(true)); }; diff --git a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts b/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts index f278ce9305d..5eda60d5b9e 100644 --- a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts @@ -165,6 +165,55 @@ describe('CloudWatchDatasource', function() { }); }); + describe('When query region is "default"', function () { + it('should return the datasource region if empty or "default"', function() { + var defaultRegion = instanceSettings.jsonData.defaultRegion; + + expect(ctx.ds.getActualRegion()).to.be(defaultRegion); + expect(ctx.ds.getActualRegion('')).to.be(defaultRegion); + expect(ctx.ds.getActualRegion("default")).to.be(defaultRegion); + }); + + it('should return the specified region if specified', function() { + expect(ctx.ds.getActualRegion('some-fake-region-1')).to.be('some-fake-region-1'); + }); + + var requestParams; + beforeEach(function() { + ctx.ds.performTimeSeriesQuery = function(request) { + requestParams = request; + return ctx.$q.when({data: {}}); + }; + }); + + it('should query for the datasource region if empty or "default"', function(done) { + var query = { + range: { from: 'now-1h', to: 'now' }, + rangeRaw: { from: 1483228800, to: 1483232400 }, + targets: [ + { + region: 'default', + namespace: 'AWS/EC2', + metricName: 'CPUUtilization', + dimensions: { + InstanceId: 'i-12345678' + }, + statistics: ['Average'], + period: 300 + } + ] + }; + + ctx.ds.query(query).then(function(result) { + expect(requestParams.queries[0].region).to.be(instanceSettings.jsonData.defaultRegion); + done(); + }); + ctx.$rootScope.$apply(); + }); + + + }); + describe('When performing CloudWatch query for extended statistics', function() { var requestParams; @@ -348,6 +397,26 @@ describe('CloudWatchDatasource', function() { }); }); + describeMetricFindQuery('dimension_values(default,AWS/EC2,CPUUtilization,InstanceId)', scenario => { + scenario.setup(() => { + scenario.requestResponse = { + results: { + metricFindQuery: { + tables: [ + { rows: [['i-12345678', 'i-12345678']] } + ] + } + } + }; + }); + + it('should call __ListMetrics and return result', () => { + expect(scenario.result[0].text).to.contain('i-12345678'); + expect(scenario.request.queries[0].type).to.be('metricFindQuery'); + expect(scenario.request.queries[0].subtype).to.be('dimension_values'); + }); + }); + it('should caclculate the correct period', function () { var hourSec = 60 * 60; var daySec = hourSec * 24;