mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(cloudwatch): refactoring and cleanup of backend code, started moving hard coded stuff in the frontend to the backend, changed name of metricFind queries region() -> regions() , and namespace() -> namespaces() to be more consistent with the others, #684
This commit is contained in:
parent
04f4454974
commit
180ba33ac8
150
pkg/api/cloudwatch/cloudwatch.go
Normal file
150
pkg/api/cloudwatch/cloudwatch.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package cloudwatch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type actionHandler func(*cwRequest, *middleware.Context)
|
||||||
|
|
||||||
|
var actionHandlers map[string]actionHandler
|
||||||
|
|
||||||
|
type cwRequest struct {
|
||||||
|
Region string `json:"region"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
Body []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
actionHandlers = map[string]actionHandler{
|
||||||
|
"GetMetricStatistics": handleGetMetricStatistics,
|
||||||
|
"ListMetrics": handleListMetrics,
|
||||||
|
"DescribeInstances": handleDescribeInstances,
|
||||||
|
"__GetRegions": handleGetRegions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
|
||||||
|
svc := cloudwatch.New(&aws.Config{Region: aws.String(req.Region)})
|
||||||
|
|
||||||
|
reqParam := &struct {
|
||||||
|
Parameters struct {
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
|
MetricName string `json:"metricName"`
|
||||||
|
Dimensions []*cloudwatch.Dimension `json:"dimensions"`
|
||||||
|
Statistics []*string `json:"statistics"`
|
||||||
|
StartTime int64 `json:"startTime"`
|
||||||
|
EndTime int64 `json:"endTime"`
|
||||||
|
Period int64 `json:"period"`
|
||||||
|
} `json:"parameters"`
|
||||||
|
}{}
|
||||||
|
json.Unmarshal(req.Body, reqParam)
|
||||||
|
|
||||||
|
params := &cloudwatch.GetMetricStatisticsInput{
|
||||||
|
Namespace: aws.String(reqParam.Parameters.Namespace),
|
||||||
|
MetricName: aws.String(reqParam.Parameters.MetricName),
|
||||||
|
Dimensions: reqParam.Parameters.Dimensions,
|
||||||
|
Statistics: reqParam.Parameters.Statistics,
|
||||||
|
StartTime: aws.Time(time.Unix(reqParam.Parameters.StartTime, 0)),
|
||||||
|
EndTime: aws.Time(time.Unix(reqParam.Parameters.EndTime, 0)),
|
||||||
|
Period: aws.Int64(reqParam.Parameters.Period),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := svc.GetMetricStatistics(params)
|
||||||
|
if err != nil {
|
||||||
|
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleListMetrics(req *cwRequest, c *middleware.Context) {
|
||||||
|
svc := cloudwatch.New(&aws.Config{Region: aws.String(req.Region)})
|
||||||
|
reqParam := &struct {
|
||||||
|
Parameters struct {
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
|
MetricName string `json:"metricName"`
|
||||||
|
Dimensions []*cloudwatch.DimensionFilter `json:"dimensions"`
|
||||||
|
} `json:"parameters"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
json.Unmarshal(req.Body, reqParam)
|
||||||
|
|
||||||
|
params := &cloudwatch.ListMetricsInput{
|
||||||
|
Namespace: aws.String(reqParam.Parameters.Namespace),
|
||||||
|
MetricName: aws.String(reqParam.Parameters.MetricName),
|
||||||
|
Dimensions: reqParam.Parameters.Dimensions,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := svc.ListMetrics(params)
|
||||||
|
if err != nil {
|
||||||
|
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleDescribeInstances(req *cwRequest, c *middleware.Context) {
|
||||||
|
svc := ec2.New(&aws.Config{Region: aws.String(req.Region)})
|
||||||
|
|
||||||
|
reqParam := &struct {
|
||||||
|
Parameters struct {
|
||||||
|
Filters []*ec2.Filter `json:"filters"`
|
||||||
|
InstanceIds []*string `json:"instanceIds"`
|
||||||
|
} `json:"parameters"`
|
||||||
|
}{}
|
||||||
|
json.Unmarshal(req.Body, reqParam)
|
||||||
|
|
||||||
|
params := &ec2.DescribeInstancesInput{}
|
||||||
|
if len(reqParam.Parameters.Filters) > 0 {
|
||||||
|
params.Filters = reqParam.Parameters.Filters
|
||||||
|
}
|
||||||
|
if len(reqParam.Parameters.InstanceIds) > 0 {
|
||||||
|
params.InstanceIDs = reqParam.Parameters.InstanceIds
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := svc.DescribeInstances(params)
|
||||||
|
if err != nil {
|
||||||
|
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetRegions(req *cwRequest, c *middleware.Context) {
|
||||||
|
regions := []string{
|
||||||
|
"us-west-2", "us-west-1", "eu-west-1", "eu-central-1", "ap-southeast-1",
|
||||||
|
"ap-southeast-2", "ap-northeast-1", "sa-east-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
result := []interface{}{}
|
||||||
|
for _, region := range regions {
|
||||||
|
result = append(result, util.DynMap{"text": region, "value": region})
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleRequest(c *middleware.Context) {
|
||||||
|
var req cwRequest
|
||||||
|
req.Body, _ = ioutil.ReadAll(c.Req.Request.Body)
|
||||||
|
json.Unmarshal(req.Body, &req)
|
||||||
|
|
||||||
|
if handler, found := actionHandlers[req.Action]; !found {
|
||||||
|
c.JsonApiErr(500, "Unexpected AWS Action", errors.New(req.Action))
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
handler(&req, c)
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/api/cloudwatch"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/middleware"
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
@ -83,7 +84,7 @@ func ProxyDataSourceRequest(c *middleware.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if query.Result.Type == m.DS_CLOUDWATCH {
|
if query.Result.Type == m.DS_CLOUDWATCH {
|
||||||
ProxyCloudWatchDataSourceRequest(c)
|
cloudwatch.HandleRequest(c)
|
||||||
} else {
|
} else {
|
||||||
proxyPath := c.Params("*")
|
proxyPath := c.Params("*")
|
||||||
proxy := NewReverseProxy(&ds, proxyPath, targetUrl)
|
proxy := NewReverseProxy(&ds, proxyPath, targetUrl)
|
||||||
|
@ -1,125 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/grafana/grafana/pkg/middleware"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ProxyCloudWatchDataSourceRequest(c *middleware.Context) {
|
|
||||||
body, _ := ioutil.ReadAll(c.Req.Request.Body)
|
|
||||||
|
|
||||||
reqInfo := &struct {
|
|
||||||
Region string `json:"region"`
|
|
||||||
Service string `json:"service"`
|
|
||||||
Action string `json:"action"`
|
|
||||||
}{}
|
|
||||||
json.Unmarshal([]byte(body), reqInfo)
|
|
||||||
|
|
||||||
switch reqInfo.Service {
|
|
||||||
case "CloudWatch":
|
|
||||||
svc := cloudwatch.New(&aws.Config{Region: aws.String(reqInfo.Region)})
|
|
||||||
|
|
||||||
switch reqInfo.Action {
|
|
||||||
case "GetMetricStatistics":
|
|
||||||
reqParam := &struct {
|
|
||||||
Parameters struct {
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
MetricName string `json:"metricName"`
|
|
||||||
Dimensions []*cloudwatch.Dimension `json:"dimensions"`
|
|
||||||
Statistics []*string `json:"statistics"`
|
|
||||||
StartTime int64 `json:"startTime"`
|
|
||||||
EndTime int64 `json:"endTime"`
|
|
||||||
Period int64 `json:"period"`
|
|
||||||
} `json:"parameters"`
|
|
||||||
}{}
|
|
||||||
json.Unmarshal([]byte(body), reqParam)
|
|
||||||
|
|
||||||
params := &cloudwatch.GetMetricStatisticsInput{
|
|
||||||
Namespace: aws.String(reqParam.Parameters.Namespace),
|
|
||||||
MetricName: aws.String(reqParam.Parameters.MetricName),
|
|
||||||
Dimensions: reqParam.Parameters.Dimensions,
|
|
||||||
Statistics: reqParam.Parameters.Statistics,
|
|
||||||
StartTime: aws.Time(time.Unix(reqParam.Parameters.StartTime, 0)),
|
|
||||||
EndTime: aws.Time(time.Unix(reqParam.Parameters.EndTime, 0)),
|
|
||||||
Period: aws.Int64(reqParam.Parameters.Period),
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := svc.GetMetricStatistics(params)
|
|
||||||
if err != nil {
|
|
||||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
respJson, _ := json.Marshal(resp)
|
|
||||||
fmt.Fprint(c.RW(), string(respJson))
|
|
||||||
case "ListMetrics":
|
|
||||||
reqParam := &struct {
|
|
||||||
Parameters struct {
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
MetricName string `json:"metricName"`
|
|
||||||
Dimensions []*cloudwatch.DimensionFilter `json:"dimensions"`
|
|
||||||
} `json:"parameters"`
|
|
||||||
}{}
|
|
||||||
json.Unmarshal([]byte(body), reqParam)
|
|
||||||
|
|
||||||
params := &cloudwatch.ListMetricsInput{
|
|
||||||
Namespace: aws.String(reqParam.Parameters.Namespace),
|
|
||||||
MetricName: aws.String(reqParam.Parameters.MetricName),
|
|
||||||
Dimensions: reqParam.Parameters.Dimensions,
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := svc.ListMetrics(params)
|
|
||||||
if err != nil {
|
|
||||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
respJson, _ := json.Marshal(resp)
|
|
||||||
fmt.Fprint(c.RW(), string(respJson))
|
|
||||||
default:
|
|
||||||
c.JsonApiErr(500, "Unexpected CloudWatch action", errors.New(reqInfo.Action))
|
|
||||||
}
|
|
||||||
case "EC2":
|
|
||||||
svc := ec2.New(&aws.Config{Region: aws.String(reqInfo.Region)})
|
|
||||||
|
|
||||||
switch reqInfo.Action {
|
|
||||||
case "DescribeInstances":
|
|
||||||
reqParam := &struct {
|
|
||||||
Parameters struct {
|
|
||||||
Filters []*ec2.Filter `json:"filters"`
|
|
||||||
InstanceIds []*string `json:"instanceIds"`
|
|
||||||
} `json:"parameters"`
|
|
||||||
}{}
|
|
||||||
json.Unmarshal([]byte(body), reqParam)
|
|
||||||
|
|
||||||
params := &ec2.DescribeInstancesInput{}
|
|
||||||
if len(reqParam.Parameters.Filters) > 0 {
|
|
||||||
params.Filters = reqParam.Parameters.Filters
|
|
||||||
}
|
|
||||||
if len(reqParam.Parameters.InstanceIds) > 0 {
|
|
||||||
params.InstanceIDs = reqParam.Parameters.InstanceIds
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := svc.DescribeInstances(params)
|
|
||||||
if err != nil {
|
|
||||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
respJson, _ := json.Marshal(resp)
|
|
||||||
fmt.Fprint(c.RW(), string(respJson))
|
|
||||||
default:
|
|
||||||
c.JsonApiErr(500, "Unexpected EC2 action", errors.New(reqInfo.Action))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
c.JsonApiErr(500, "Unexpected service", errors.New(reqInfo.Service))
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,9 +20,6 @@ function (angular, _) {
|
|||||||
this.defaultRegion = datasource.jsonData.defaultRegion;
|
this.defaultRegion = datasource.jsonData.defaultRegion;
|
||||||
|
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
this.supportedRegion = [
|
|
||||||
'us-east-1', 'us-west-2', 'us-west-1', 'eu-west-1', 'eu-central-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'sa-east-1'
|
|
||||||
];
|
|
||||||
|
|
||||||
this.supportedMetrics = {
|
this.supportedMetrics = {
|
||||||
'AWS/AutoScaling': [
|
'AWS/AutoScaling': [
|
||||||
@ -265,7 +262,6 @@ function (angular, _) {
|
|||||||
CloudWatchDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
|
CloudWatchDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
|
||||||
return this.awsRequest({
|
return this.awsRequest({
|
||||||
region: query.region,
|
region: query.region,
|
||||||
service: 'CloudWatch',
|
|
||||||
action: 'GetMetricStatistics',
|
action: 'GetMetricStatistics',
|
||||||
parameters: {
|
parameters: {
|
||||||
namespace: query.namespace,
|
namespace: query.namespace,
|
||||||
@ -280,7 +276,7 @@ function (angular, _) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CloudWatchDatasource.prototype.getRegions = function() {
|
CloudWatchDatasource.prototype.getRegions = function() {
|
||||||
return $q.when(this.supportedRegion);
|
return this.awsRequest({action: '__GetRegions'});
|
||||||
};
|
};
|
||||||
|
|
||||||
CloudWatchDatasource.prototype.getNamespaces = function() {
|
CloudWatchDatasource.prototype.getNamespaces = function() {
|
||||||
@ -300,7 +296,6 @@ function (angular, _) {
|
|||||||
CloudWatchDatasource.prototype.getDimensionValues = function(region, namespace, metricName, dimensions) {
|
CloudWatchDatasource.prototype.getDimensionValues = function(region, namespace, metricName, dimensions) {
|
||||||
var request = {
|
var request = {
|
||||||
region: templateSrv.replace(region),
|
region: templateSrv.replace(region),
|
||||||
service: 'CloudWatch',
|
|
||||||
action: 'ListMetrics',
|
action: 'ListMetrics',
|
||||||
parameters: {
|
parameters: {
|
||||||
namespace: templateSrv.replace(namespace),
|
namespace: templateSrv.replace(namespace),
|
||||||
@ -321,7 +316,6 @@ function (angular, _) {
|
|||||||
CloudWatchDatasource.prototype.performEC2DescribeInstances = function(region, filters, instanceIds) {
|
CloudWatchDatasource.prototype.performEC2DescribeInstances = function(region, filters, instanceIds) {
|
||||||
return this.awsRequest({
|
return this.awsRequest({
|
||||||
region: region,
|
region: region,
|
||||||
service: 'EC2',
|
|
||||||
action: 'DescribeInstances',
|
action: 'DescribeInstances',
|
||||||
parameters: {
|
parameters: {
|
||||||
filter: filters,
|
filter: filters,
|
||||||
@ -341,12 +335,12 @@ function (angular, _) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var regionQuery = query.match(/^region\(\)/);
|
var regionQuery = query.match(/^regions\(\)/);
|
||||||
if (regionQuery) {
|
if (regionQuery) {
|
||||||
return this.getRegions().then(transformSuggestData);
|
return this.getRegions();
|
||||||
}
|
}
|
||||||
|
|
||||||
var namespaceQuery = query.match(/^namespace\(\)/);
|
var namespaceQuery = query.match(/^namespaces\(\)/);
|
||||||
if (namespaceQuery) {
|
if (namespaceQuery) {
|
||||||
return this.getNamespaces().then(transformSuggestData);
|
return this.getNamespaces().then(transformSuggestData);
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,12 @@ function (angular, _) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.getRegions = function() {
|
$scope.getRegions = function() {
|
||||||
return $scope.datasource.metricFindQuery('region()')
|
return $scope.datasource.metricFindQuery('regions()')
|
||||||
.then($scope.transformToSegments(true));
|
.then($scope.transformToSegments(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getNamespaces = function() {
|
$scope.getNamespaces = function() {
|
||||||
return $scope.datasource.metricFindQuery('namespace()')
|
return $scope.datasource.metricFindQuery('namespaces()')
|
||||||
.then($scope.transformToSegments(true));
|
.then($scope.transformToSegments(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,17 +108,27 @@ describe('CloudWatchDatasource', function() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return suggest list for region()', function(done) {
|
describe('regions()', () => {
|
||||||
var query = 'region()';
|
let params, result;
|
||||||
ctx.ds.metricFindQuery(query).then(function(result) {
|
beforeEach(() => {
|
||||||
expect(result[0].text).to.contain('us-east-1');
|
ctx.backendSrv.datasourceRequest = args => {
|
||||||
done();
|
params = args;
|
||||||
|
return ctx.$q.when({data: [{text: 'us-east-1'}]});
|
||||||
|
};
|
||||||
|
ctx.ds.metricFindQuery("regions()").then(args => {
|
||||||
|
result = args;
|
||||||
});
|
});
|
||||||
ctx.$rootScope.$apply();
|
ctx.$rootScope.$apply();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should issue __GetRegions request', () => {
|
||||||
|
expect(result[0].text).to.contain('us-east-1');
|
||||||
|
expect(params.data.action).to.be('__GetRegions');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should return suggest list for namespace()', function(done) {
|
it('should return suggest list for namespace()', function(done) {
|
||||||
var query = 'namespace()';
|
var query = 'namespaces()';
|
||||||
ctx.ds.metricFindQuery(query).then(function(result) {
|
ctx.ds.metricFindQuery(query).then(function(result) {
|
||||||
result = result.map(function(v) { return v.text; });
|
result = result.map(function(v) { return v.text; });
|
||||||
expect(result).to.contain('AWS/EC2');
|
expect(result).to.contain('AWS/EC2');
|
||||||
|
Loading…
Reference in New Issue
Block a user