mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 03:52:31 +08:00
CloudWatch proxy support
This commit is contained in:
@ -67,8 +67,12 @@ func ProxyDataSourceRequest(c *middleware.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyPath := c.Params("*")
|
if query.Result.Type == m.DS_CLOUDWATCH {
|
||||||
proxy := NewReverseProxy(&query.Result, proxyPath)
|
ProxyCloudWatchDataSourceRequest(c)
|
||||||
proxy.Transport = dataProxyTransport
|
} else {
|
||||||
proxy.ServeHTTP(c.RW(), c.Req.Request)
|
proxyPath := c.Params("*")
|
||||||
|
proxy := NewReverseProxy(&query.Result, proxyPath)
|
||||||
|
proxy.Transport = dataProxyTransport
|
||||||
|
proxy.ServeHTTP(c.RW(), c.Req.Request)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
107
pkg/api/dataproxy_cloudwatch.go
Normal file
107
pkg/api/dataproxy_cloudwatch.go
Normal file
@ -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))
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ const (
|
|||||||
DS_INFLUXDB_08 = "influxdb_08"
|
DS_INFLUXDB_08 = "influxdb_08"
|
||||||
DS_ES = "elasticsearch"
|
DS_ES = "elasticsearch"
|
||||||
DS_OPENTSDB = "opentsdb"
|
DS_OPENTSDB = "opentsdb"
|
||||||
|
DS_CLOUDWATCH = "cloudwatch"
|
||||||
DS_ACCESS_DIRECT = "direct"
|
DS_ACCESS_DIRECT = "direct"
|
||||||
DS_ACCESS_PROXY = "proxy"
|
DS_ACCESS_PROXY = "proxy"
|
||||||
)
|
)
|
||||||
|
@ -18,6 +18,8 @@ function (angular, _, kbn) {
|
|||||||
this.type = 'cloudwatch';
|
this.type = 'cloudwatch';
|
||||||
this.name = datasource.name;
|
this.name = datasource.name;
|
||||||
this.supportMetrics = true;
|
this.supportMetrics = true;
|
||||||
|
this.proxyMode = (datasource.jsonData.access === 'proxy');
|
||||||
|
this.proxyUrl = datasource.url;
|
||||||
|
|
||||||
this.defaultRegion = datasource.jsonData.defaultRegion;
|
this.defaultRegion = datasource.jsonData.defaultRegion;
|
||||||
this.credentials = {
|
this.credentials = {
|
||||||
@ -194,9 +196,9 @@ function (angular, _, kbn) {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
query.statistics = getActivatedStatistics(target.statistics);
|
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
|
// CloudWatch limit datapoints up to 1440
|
||||||
if (range / query.period >= 1440) {
|
if (range / query.period >= 1440) {
|
||||||
query.period = Math.floor(range / 1440 / 60) * 60;
|
query.period = Math.floor(range / 1440 / 60) * 60;
|
||||||
@ -400,11 +402,42 @@ function (angular, _, kbn) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CloudWatchDatasource.prototype.getCloudWatchClient = function(region) {
|
CloudWatchDatasource.prototype.getCloudWatchClient = function(region) {
|
||||||
return new AWS.CloudWatch({
|
if (!this.proxyMode) {
|
||||||
region: region,
|
return new AWS.CloudWatch({
|
||||||
accessKeyId: this.credentials.accessKeyId,
|
region: region,
|
||||||
secretAccessKey: this.credentials.secretAccessKey
|
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() {
|
CloudWatchDatasource.prototype.getDefaultRegion = function() {
|
||||||
@ -440,7 +473,7 @@ function (angular, _, kbn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function convertToCloudWatchTime(date) {
|
function convertToCloudWatchTime(date) {
|
||||||
return kbn.parseDate(date);
|
return Math.round(kbn.parseDate(date).getTime() / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CloudWatchDatasource;
|
return CloudWatchDatasource;
|
||||||
|
@ -9,6 +9,14 @@
|
|||||||
<input type="text" class="tight-form-input input-large" ng-model='current.jsonData.defaultRegion' placeholder="" required></input>
|
<input type="text" class="tight-form-input input-large" ng-model='current.jsonData.defaultRegion' placeholder="" required></input>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</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 class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tight-form last">
|
<div class="tight-form last">
|
||||||
|
Reference in New Issue
Block a user