mirror of https://github.com/grafana/grafana.git
stackdriver: tests for parsing api response
This commit is contained in:
parent
54f16d5558
commit
2683699ab4
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"golang.org/x/net/context/ctxhttp"
|
"golang.org/x/net/context/ctxhttp"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
|
|
||||||
"github.com/grafana/grafana/pkg/components/null"
|
"github.com/grafana/grafana/pkg/components/null"
|
||||||
"github.com/grafana/grafana/pkg/log"
|
"github.com/grafana/grafana/pkg/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
|
@ -25,10 +24,14 @@ import (
|
||||||
"github.com/opentracing/opentracing-go"
|
"github.com/opentracing/opentracing-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var slog log.Logger
|
||||||
|
|
||||||
|
// StackdriverExecutor executes queries for the Stackdriver datasource
|
||||||
type StackdriverExecutor struct {
|
type StackdriverExecutor struct {
|
||||||
HTTPClient *http.Client
|
HTTPClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewStackdriverExecutor initializes a http client
|
||||||
func NewStackdriverExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
|
func NewStackdriverExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
|
||||||
httpClient, err := dsInfo.GetHttpClient()
|
httpClient, err := dsInfo.GetHttpClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -40,9 +43,8 @@ func NewStackdriverExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var glog = log.New("tsdb.stackdriver")
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
slog = log.New("tsdb.stackdriver")
|
||||||
tsdb.RegisterTsdbQueryEndpoint("stackdriver", NewStackdriverExecutor)
|
tsdb.RegisterTsdbQueryEndpoint("stackdriver", NewStackdriverExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,7 +68,7 @@ func (e *StackdriverExecutor) Query(ctx context.Context, dsInfo *models.DataSour
|
||||||
}
|
}
|
||||||
|
|
||||||
req.URL.RawQuery = query.Params.Encode()
|
req.URL.RawQuery = query.Params.Encode()
|
||||||
logger.Info("tsdbQuery", "req.URL.RawQuery", req.URL.RawQuery)
|
slog.Info("tsdbQuery", "req.URL.RawQuery", req.URL.RawQuery)
|
||||||
|
|
||||||
httpClient, err := dsInfo.GetHttpClient()
|
httpClient, err := dsInfo.GetHttpClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -138,7 +140,7 @@ func (e *StackdriverExecutor) parseQueries(tsdbQuery *tsdb.TsdbQuery) ([]*Stackd
|
||||||
params.Add("filter", metricType)
|
params.Add("filter", metricType)
|
||||||
|
|
||||||
if setting.Env == setting.DEV {
|
if setting.Env == setting.DEV {
|
||||||
glog.Debug("Stackdriver request", "params", params)
|
slog.Debug("Stackdriver request", "params", params)
|
||||||
}
|
}
|
||||||
|
|
||||||
stackdriverQueries = append(stackdriverQueries, &StackdriverQuery{
|
stackdriverQueries = append(stackdriverQueries, &StackdriverQuery{
|
||||||
|
|
@ -159,14 +161,14 @@ func (e *StackdriverExecutor) unmarshalResponse(res *http.Response) (StackDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
if res.StatusCode/100 != 2 {
|
if res.StatusCode/100 != 2 {
|
||||||
glog.Info("Request failed", "status", res.Status, "body", string(body))
|
slog.Info("Request failed", "status", res.Status, "body", string(body))
|
||||||
return StackDriverResponse{}, fmt.Errorf("Request failed status: %v", res.Status)
|
return StackDriverResponse{}, fmt.Errorf("Request failed status: %v", res.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
var data StackDriverResponse
|
var data StackDriverResponse
|
||||||
err = json.Unmarshal(body, &data)
|
err = json.Unmarshal(body, &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Info("Failed to unmarshal Stackdriver response", "error", err, "status", res.Status, "body", string(body))
|
slog.Info("Failed to unmarshal Stackdriver response", "error", err, "status", res.Status, "body", string(body))
|
||||||
return StackDriverResponse{}, err
|
return StackDriverResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,8 +184,13 @@ func (e *StackdriverExecutor) parseResponse(data StackDriverResponse, queryRefID
|
||||||
for _, point := range series.Points {
|
for _, point := range series.Points {
|
||||||
points = append(points, tsdb.NewTimePoint(null.FloatFrom(point.Value.DoubleValue), float64((point.Interval.EndTime).Unix())*1000))
|
points = append(points, tsdb.NewTimePoint(null.FloatFrom(point.Value.DoubleValue), float64((point.Interval.EndTime).Unix())*1000))
|
||||||
}
|
}
|
||||||
|
metricName := series.Metric.Type
|
||||||
|
|
||||||
|
for _, value := range series.Metric.Labels {
|
||||||
|
metricName += " " + value
|
||||||
|
}
|
||||||
queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{
|
queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{
|
||||||
Name: series.Metric.Type,
|
Name: metricName,
|
||||||
Points: points,
|
Points: points,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +204,7 @@ func (e *StackdriverExecutor) createRequest(ctx context.Context, dsInfo *models.
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "https://monitoring.googleapis.com/", nil)
|
req, err := http.NewRequest(http.MethodGet, "https://monitoring.googleapis.com/", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Info("Failed to create request", "error", err)
|
slog.Info("Failed to create request", "error", err)
|
||||||
return nil, fmt.Errorf("Failed to create request. error: %v", err)
|
return nil, fmt.Errorf("Failed to create request. error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package stackdriver
|
package stackdriver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -13,8 +15,9 @@ import (
|
||||||
|
|
||||||
func TestStackdriver(t *testing.T) {
|
func TestStackdriver(t *testing.T) {
|
||||||
Convey("Stackdriver", t, func() {
|
Convey("Stackdriver", t, func() {
|
||||||
|
executor := &StackdriverExecutor{}
|
||||||
|
|
||||||
Convey("Parse query from frontend", func() {
|
Convey("Parse query from frontend", func() {
|
||||||
executor := &StackdriverExecutor{}
|
|
||||||
fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local)
|
fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local)
|
||||||
tsdbQuery := &tsdb.TsdbQuery{
|
tsdbQuery := &tsdb.TsdbQuery{
|
||||||
TimeRange: &tsdb.TimeRange{
|
TimeRange: &tsdb.TimeRange{
|
||||||
|
|
@ -43,5 +46,51 @@ func TestStackdriver(t *testing.T) {
|
||||||
So(queries[0].Params["aggregation.perSeriesAligner"][0], ShouldEqual, "ALIGN_NONE")
|
So(queries[0].Params["aggregation.perSeriesAligner"][0], ShouldEqual, "ALIGN_NONE")
|
||||||
So(queries[0].Params["filter"][0], ShouldEqual, "time_series")
|
So(queries[0].Params["filter"][0], ShouldEqual, "time_series")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Parse stackdriver response for data aggregated to one time series", func() {
|
||||||
|
var data StackDriverResponse
|
||||||
|
|
||||||
|
jsonBody, err := ioutil.ReadFile("./test-data/1-series-response-agg-one-metric.json")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = json.Unmarshal(jsonBody, &data)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(len(data.TimeSeries), ShouldEqual, 1)
|
||||||
|
|
||||||
|
res, err := executor.parseResponse(data, "A")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
So(len(res.Series), ShouldEqual, 1)
|
||||||
|
So(res.Series[0].Name, ShouldEqual, "serviceruntime.googleapis.com/api/request_count")
|
||||||
|
So(len(res.Series[0].Points), ShouldEqual, 3)
|
||||||
|
|
||||||
|
So(res.Series[0].Points[0][0].Float64, ShouldEqual, 1.0666666666667)
|
||||||
|
So(res.Series[0].Points[1][0].Float64, ShouldEqual, 1.05)
|
||||||
|
So(res.Series[0].Points[2][0].Float64, ShouldEqual, 0.05)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Parse stackdriver response for data with no aggregation", func() {
|
||||||
|
var data StackDriverResponse
|
||||||
|
|
||||||
|
jsonBody, err := ioutil.ReadFile("./test-data/2-series-response-no-agg.json")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = json.Unmarshal(jsonBody, &data)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(len(data.TimeSeries), ShouldEqual, 3)
|
||||||
|
|
||||||
|
res, err := executor.parseResponse(data, "A")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
Convey("Should add labels to metric name", func() {
|
||||||
|
So(len(res.Series), ShouldEqual, 3)
|
||||||
|
So(res.Series[0].Name, ShouldEqual, "compute.googleapis.com/instance/cpu/usage_time collector-asia-east-1")
|
||||||
|
So(res.Series[1].Name, ShouldEqual, "compute.googleapis.com/instance/cpu/usage_time collector-europe-west-1")
|
||||||
|
So(res.Series[2].Name, ShouldEqual, "compute.googleapis.com/instance/cpu/usage_time collector-us-east-1")
|
||||||
|
|
||||||
|
So(len(res.Series[0].Points), ShouldEqual, 3)
|
||||||
|
So(res.Series[0].Points[0][0].Float64, ShouldEqual, 9.7730520330369)
|
||||||
|
So(res.Series[0].Points[1][0].Float64, ShouldEqual, 9.7323568146676)
|
||||||
|
So(res.Series[0].Points[2][0].Float64, ShouldEqual, 9.8566497180145)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"timeSeries": [
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"type": "serviceruntime.googleapis.com\/api\/request_count"
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"type": "consumed_api",
|
||||||
|
"labels": {
|
||||||
|
"project_id": "grafana-prod"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"metricKind": "GAUGE",
|
||||||
|
"valueType": "DOUBLE",
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:51:00Z",
|
||||||
|
"endTime": "2018-09-11T12:51:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 1.0666666666667
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:48:00Z",
|
||||||
|
"endTime": "2018-09-11T12:48:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 1.05
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:47:00Z",
|
||||||
|
"endTime": "2018-09-11T12:47:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 0.05
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
{
|
||||||
|
"timeSeries": [
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"labels": {
|
||||||
|
"instance_name": "collector-asia-east-1"
|
||||||
|
},
|
||||||
|
"type": "compute.googleapis.com\/instance\/cpu\/usage_time"
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"type": "gce_instance",
|
||||||
|
"labels": {
|
||||||
|
"instance_id": "1119268429530133111",
|
||||||
|
"zone": "asia-east1-a",
|
||||||
|
"project_id": "grafana-prod"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"metricKind": "DELTA",
|
||||||
|
"valueType": "DOUBLE",
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:30:00Z",
|
||||||
|
"endTime": "2018-09-11T12:31:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 9.7730520330369
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:29:00Z",
|
||||||
|
"endTime": "2018-09-11T12:30:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 9.7323568146676
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:28:00Z",
|
||||||
|
"endTime": "2018-09-11T12:29:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 9.8566497180145
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"labels": {
|
||||||
|
"instance_name": "collector-europe-west-1"
|
||||||
|
},
|
||||||
|
"type": "compute.googleapis.com\/instance\/cpu\/usage_time"
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"type": "gce_instance",
|
||||||
|
"labels": {
|
||||||
|
"instance_id": "22241654114540837222",
|
||||||
|
"zone": "europe-west1-b",
|
||||||
|
"project_id": "grafana-prod"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"metricKind": "DELTA",
|
||||||
|
"valueType": "DOUBLE",
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:30:00Z",
|
||||||
|
"endTime": "2018-09-11T12:31:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 8.8210971239023
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:29:00Z",
|
||||||
|
"endTime": "2018-09-11T12:30:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 8.9689492364414
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:28:00Z",
|
||||||
|
"endTime": "2018-09-11T12:29:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 9.0238475054502
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metric": {
|
||||||
|
"labels": {
|
||||||
|
"instance_name": "collector-us-east-1"
|
||||||
|
},
|
||||||
|
"type": "compute.googleapis.com\/instance\/cpu\/usage_time"
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"type": "gce_instance",
|
||||||
|
"labels": {
|
||||||
|
"instance_id": "3332264424035095333",
|
||||||
|
"zone": "us-east1-b",
|
||||||
|
"project_id": "grafana-prod"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"metricKind": "DELTA",
|
||||||
|
"valueType": "DOUBLE",
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:30:00Z",
|
||||||
|
"endTime": "2018-09-11T12:31:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 30.807846801355
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:29:00Z",
|
||||||
|
"endTime": "2018-09-11T12:30:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 30.903974115849
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interval": {
|
||||||
|
"startTime": "2018-09-11T12:28:00Z",
|
||||||
|
"endTime": "2018-09-11T12:29:00Z"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 30.829426143318
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -14,18 +14,12 @@ type StackdriverQuery struct {
|
||||||
type StackDriverResponse struct {
|
type StackDriverResponse struct {
|
||||||
TimeSeries []struct {
|
TimeSeries []struct {
|
||||||
Metric struct {
|
Metric struct {
|
||||||
Labels struct {
|
Labels map[string]string `json:"labels"`
|
||||||
InstanceName string `json:"instance_name"`
|
Type string `json:"type"`
|
||||||
} `json:"labels"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
} `json:"metric"`
|
} `json:"metric"`
|
||||||
Resource struct {
|
Resource struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Labels struct {
|
Labels map[string]string `json:"labels"`
|
||||||
InstanceID string `json:"instance_id"`
|
|
||||||
Zone string `json:"zone"`
|
|
||||||
ProjectID string `json:"project_id"`
|
|
||||||
} `json:"labels"`
|
|
||||||
} `json:"resource"`
|
} `json:"resource"`
|
||||||
MetricKind string `json:"metricKind"`
|
MetricKind string `json:"metricKind"`
|
||||||
ValueType string `json:"valueType"`
|
ValueType string `json:"valueType"`
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ export default class StackdriverDatasource {
|
||||||
datasourceId: this.id,
|
datasourceId: this.id,
|
||||||
metricType: `metric.type="${t.metricType}"`,
|
metricType: `metric.type="${t.metricType}"`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const result = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await this.backendSrv.datasourceRequest({
|
const { data } = await this.backendSrv.datasourceRequest({
|
||||||
url: '/api/tsdb/query',
|
url: '/api/tsdb/query',
|
||||||
|
|
@ -28,8 +31,6 @@ export default class StackdriverDatasource {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = [];
|
|
||||||
|
|
||||||
if (data.results) {
|
if (data.results) {
|
||||||
Object['values'](data.results).forEach(queryRes => {
|
Object['values'](data.results).forEach(queryRes => {
|
||||||
queryRes.series.forEach(series => {
|
queryRes.series.forEach(series => {
|
||||||
|
|
@ -37,11 +38,11 @@ export default class StackdriverDatasource {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return { data: result };
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { data: result };
|
||||||
}
|
}
|
||||||
|
|
||||||
testDatasource() {
|
testDatasource() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue