diff --git a/pkg/api/dataproxy.go b/pkg/api/dataproxy.go index 11075294b66..92495ab59a2 100644 --- a/pkg/api/dataproxy.go +++ b/pkg/api/dataproxy.go @@ -67,8 +67,12 @@ func ProxyDataSourceRequest(c *middleware.Context) { return } - proxyPath := c.Params("*") - proxy := NewReverseProxy(&query.Result, proxyPath) - proxy.Transport = dataProxyTransport - proxy.ServeHTTP(c.RW(), c.Req.Request) + if query.Result.Type == m.DS_CLOUDWATCH { + ProxyCloudWatchDataSourceRequest(c) + } else { + proxyPath := c.Params("*") + proxy := NewReverseProxy(&query.Result, proxyPath) + proxy.Transport = dataProxyTransport + proxy.ServeHTTP(c.RW(), c.Req.Request) + } } diff --git a/pkg/api/dataproxy_cloudwatch.go b/pkg/api/dataproxy_cloudwatch.go new file mode 100644 index 00000000000..3a0b4b16bc2 --- /dev/null +++ b/pkg/api/dataproxy_cloudwatch.go @@ -0,0 +1,107 @@ +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/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) + + 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 []map[string]string `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) + + statistics := make([]*string, 0) + for k := range reqParam.Parameters.Statistics { + statistics = append(statistics, &reqParam.Parameters.Statistics[k]) + } + dimensions := make([]*cloudwatch.Dimension, 0) + for _, d := range reqParam.Parameters.Dimensions { + dimensions = append(dimensions, &cloudwatch.Dimension{ + Name: aws.String(d["Name"]), + Value: aws.String(d["Value"]), + }) + } + + params := &cloudwatch.GetMetricStatisticsInput{ + Namespace: aws.String(reqParam.Parameters.Namespace), + MetricName: aws.String(reqParam.Parameters.MetricName), + Dimensions: dimensions, + Statistics: 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 []map[string]string `json:"Dimensions"` + } `json:"parameters"` + }{} + json.Unmarshal([]byte(body), reqParam) + + dimensions := make([]*cloudwatch.DimensionFilter, 0) + for _, d := range reqParam.Parameters.Dimensions { + dimensions = append(dimensions, &cloudwatch.DimensionFilter{ + Name: aws.String(d["Name"]), + Value: aws.String(d["Value"]), + }) + } + + params := &cloudwatch.ListMetricsInput{ + Namespace: aws.String(reqParam.Parameters.Namespace), + MetricName: aws.String(reqParam.Parameters.MetricName), + Dimensions: 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)) + } +} diff --git a/pkg/models/datasource.go b/pkg/models/datasource.go index c756faaba59..75e2134c09f 100644 --- a/pkg/models/datasource.go +++ b/pkg/models/datasource.go @@ -11,6 +11,7 @@ const ( DS_INFLUXDB_08 = "influxdb_08" DS_ES = "elasticsearch" DS_OPENTSDB = "opentsdb" + DS_CLOUDWATCH = "cloudwatch" DS_ACCESS_DIRECT = "direct" DS_ACCESS_PROXY = "proxy" ) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index 2ba5d5698bb..8152f7ca5d8 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -18,6 +18,8 @@ function (angular, _, kbn) { this.type = 'cloudwatch'; this.name = datasource.name; this.supportMetrics = true; + this.proxyMode = (datasource.jsonData.access === 'proxy'); + this.proxyUrl = datasource.url; this.defaultRegion = datasource.jsonData.defaultRegion; this.credentials = { @@ -194,9 +196,9 @@ function (angular, _, kbn) { }; }); query.statistics = getActivatedStatistics(target.statistics); - query.period = target.period; + query.period = parseInt(target.period, 10); - var range = (end.getTime() - start.getTime()) / 1000; + var range = end - start; // CloudWatch limit datapoints up to 1440 if (range / query.period >= 1440) { query.period = Math.floor(range / 1440 / 60) * 60; @@ -400,11 +402,42 @@ function (angular, _, kbn) { }; CloudWatchDatasource.prototype.getCloudWatchClient = function(region) { - return new AWS.CloudWatch({ - region: region, - accessKeyId: this.credentials.accessKeyId, - secretAccessKey: this.credentials.secretAccessKey - }); + if (!this.proxyMode) { + return new AWS.CloudWatch({ + region: region, + accessKeyId: this.credentials.accessKeyId, + secretAccessKey: this.credentials.secretAccessKey + }); + } else { + var self = this; + var generateRequestProxy = function(service, action) { + return function(params, callback) { + var data = { + region: region, + service: service, + action: action, + parameters: params + }; + + var options = { + method: 'POST', + url: self.proxyUrl, + data: data + }; + + $http(options).then(function(response) { + callback(null, response.data); + }, function(err) { + callback(err, []); + }); + }; + }; + + return { + getMetricStatistics: generateRequestProxy('CloudWatch', 'GetMetricStatistics'), + listMetrics: generateRequestProxy('CloudWatch', 'ListMetrics') + }; + } }; CloudWatchDatasource.prototype.getDefaultRegion = function() { @@ -440,7 +473,7 @@ function (angular, _, kbn) { } function convertToCloudWatchTime(date) { - return kbn.parseDate(date); + return Math.round(kbn.parseDate(date).getTime() / 1000); } return CloudWatchDatasource; diff --git a/public/app/plugins/datasource/cloudwatch/partials/config.html b/public/app/plugins/datasource/cloudwatch/partials/config.html index 476e75f6e64..95f62c87a9f 100644 --- a/public/app/plugins/datasource/cloudwatch/partials/config.html +++ b/public/app/plugins/datasource/cloudwatch/partials/config.html @@ -9,6 +9,14 @@ +