Ver Fonte

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

Torkel Ödegaard há 10 anos atrás
pai
commit
180ba33ac8

+ 150 - 0
pkg/api/cloudwatch/cloudwatch.go

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

+ 2 - 1
pkg/api/dataproxy.go

@@ -8,6 +8,7 @@ import (
 	"net/url"
 	"time"
 
+	"github.com/grafana/grafana/pkg/api/cloudwatch"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
@@ -83,7 +84,7 @@ func ProxyDataSourceRequest(c *middleware.Context) {
 	}
 
 	if query.Result.Type == m.DS_CLOUDWATCH {
-		ProxyCloudWatchDataSourceRequest(c)
+		cloudwatch.HandleRequest(c)
 	} else {
 		proxyPath := c.Params("*")
 		proxy := NewReverseProxy(&ds, proxyPath, targetUrl)

+ 0 - 125
pkg/api/dataproxy_cloudwatch.go

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

+ 4 - 10
public/app/plugins/datasource/cloudwatch/datasource.js

@@ -20,9 +20,6 @@ function (angular, _) {
       this.defaultRegion = datasource.jsonData.defaultRegion;
 
       /* 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 = {
         'AWS/AutoScaling': [
@@ -265,7 +262,6 @@ function (angular, _) {
     CloudWatchDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
       return this.awsRequest({
         region: query.region,
-        service: 'CloudWatch',
         action: 'GetMetricStatistics',
         parameters:  {
           namespace: query.namespace,
@@ -280,7 +276,7 @@ function (angular, _) {
     };
 
     CloudWatchDatasource.prototype.getRegions = function() {
-      return $q.when(this.supportedRegion);
+      return this.awsRequest({action: '__GetRegions'});
     };
 
     CloudWatchDatasource.prototype.getNamespaces = function() {
@@ -300,7 +296,6 @@ function (angular, _) {
     CloudWatchDatasource.prototype.getDimensionValues = function(region, namespace, metricName, dimensions) {
       var request = {
         region: templateSrv.replace(region),
-        service: 'CloudWatch',
         action: 'ListMetrics',
         parameters: {
           namespace: templateSrv.replace(namespace),
@@ -321,7 +316,6 @@ function (angular, _) {
     CloudWatchDatasource.prototype.performEC2DescribeInstances = function(region, filters, instanceIds) {
       return this.awsRequest({
         region: region,
-        service: 'EC2',
         action: 'DescribeInstances',
         parameters: {
           filter: filters,
@@ -341,12 +335,12 @@ function (angular, _) {
         });
       };
 
-      var regionQuery = query.match(/^region\(\)/);
+      var regionQuery = query.match(/^regions\(\)/);
       if (regionQuery) {
-        return this.getRegions().then(transformSuggestData);
+        return this.getRegions();
       }
 
-      var namespaceQuery = query.match(/^namespace\(\)/);
+      var namespaceQuery = query.match(/^namespaces\(\)/);
       if (namespaceQuery) {
         return this.getNamespaces().then(transformSuggestData);
       }

+ 2 - 2
public/app/plugins/datasource/cloudwatch/query_ctrl.js

@@ -25,12 +25,12 @@ function (angular, _) {
     };
 
     $scope.getRegions = function() {
-      return $scope.datasource.metricFindQuery('region()')
+      return $scope.datasource.metricFindQuery('regions()')
         .then($scope.transformToSegments(true));
     };
 
     $scope.getNamespaces = function() {
-      return $scope.datasource.metricFindQuery('namespace()')
+      return $scope.datasource.metricFindQuery('namespaces()')
         .then($scope.transformToSegments(true));
     };
 

+ 16 - 6
public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts

@@ -108,17 +108,27 @@ describe('CloudWatchDatasource', function() {
       };
     });
 
-    it('should return suggest list for region()', function(done) {
-      var query = 'region()';
-      ctx.ds.metricFindQuery(query).then(function(result) {
+    describe('regions()', () => {
+      let params, result;
+      beforeEach(() => {
+        ctx.backendSrv.datasourceRequest = args => {
+          params = args;
+          return ctx.$q.when({data: [{text: 'us-east-1'}]});
+        };
+        ctx.ds.metricFindQuery("regions()").then(args => {
+          result = args;
+        });
+        ctx.$rootScope.$apply();
+      });
+
+      it('should issue __GetRegions request', () => {
         expect(result[0].text).to.contain('us-east-1');
-        done();
+        expect(params.data.action).to.be('__GetRegions');
       });
-      ctx.$rootScope.$apply();
     });
 
     it('should return suggest list for namespace()', function(done) {
-      var query = 'namespace()';
+      var query = 'namespaces()';
       ctx.ds.metricFindQuery(query).then(function(result) {
         result = result.map(function(v) { return v.text; });
         expect(result).to.contain('AWS/EC2');