Explicitly specify default region in CloudWatch datasource (#9440)

The datasource uses the default region in the query if the region
is "" in the settings. However setting the region to an empty string
is almost impossible and rendered incorrectly.

This commit introduces a special value "default" for region which
is shown in the drop down and is translated to the default region
of the data source when performing queries.
This commit is contained in:
Mikael Olenfalk 2017-12-11 09:37:27 +01:00 committed by Torkel Ödegaard
parent 0506cfdc1d
commit 8e7166b5c4
5 changed files with 98 additions and 10 deletions

View File

@ -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`.

View File

@ -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 := ""

View File

@ -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'});

View File

@ -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));
};

View File

@ -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;