Commit 8e7166b5 by Mikael Olenfalk Committed by Torkel Ödegaard

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.
parent 0506cfdc
......@@ -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`.
......
......@@ -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 := ""
......
......@@ -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'});
......
......@@ -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));
};
......
......@@ -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;
......
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