Commit 00f76eca by Mitsuhiro Tanda

CloudWatch proxy support

parent 1c6b7203
......@@ -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)
}
}
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))
}
}
......@@ -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"
)
......
......@@ -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;
......
......@@ -9,6 +9,14 @@
<input type="text" class="tight-form-input input-large" ng-model='current.jsonData.defaultRegion' placeholder="" required></input>
</li>
</ul>
<ul class="tight-form-list">
<li class="tight-form-item">
Access <tip>Direct = url is used directly from browser, Proxy = Grafana backend will proxy the request</label>
</li>
<li>
<select class="input-medium tight-form-input" ng-model="current.jsonData.access" ng-options="f for f in ['direct', 'proxy']" ng-init="current.jsonData.access = current.jsonData.access || 'direct'"></select>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form last">
......
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