Commit 2683699a by Daniel Lee

stackdriver: tests for parsing api response

parent 54f16d55
......@@ -15,7 +15,6 @@ import (
"golang.org/x/net/context/ctxhttp"
"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/log"
"github.com/grafana/grafana/pkg/models"
......@@ -25,10 +24,14 @@ import (
"github.com/opentracing/opentracing-go"
)
var slog log.Logger
// StackdriverExecutor executes queries for the Stackdriver datasource
type StackdriverExecutor struct {
HTTPClient *http.Client
}
// NewStackdriverExecutor initializes a http client
func NewStackdriverExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
httpClient, err := dsInfo.GetHttpClient()
if err != nil {
......@@ -40,9 +43,8 @@ func NewStackdriverExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint,
}, nil
}
var glog = log.New("tsdb.stackdriver")
func init() {
slog = log.New("tsdb.stackdriver")
tsdb.RegisterTsdbQueryEndpoint("stackdriver", NewStackdriverExecutor)
}
......@@ -66,7 +68,7 @@ func (e *StackdriverExecutor) Query(ctx context.Context, dsInfo *models.DataSour
}
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()
if err != nil {
......@@ -138,7 +140,7 @@ func (e *StackdriverExecutor) parseQueries(tsdbQuery *tsdb.TsdbQuery) ([]*Stackd
params.Add("filter", metricType)
if setting.Env == setting.DEV {
glog.Debug("Stackdriver request", "params", params)
slog.Debug("Stackdriver request", "params", params)
}
stackdriverQueries = append(stackdriverQueries, &StackdriverQuery{
......@@ -159,14 +161,14 @@ func (e *StackdriverExecutor) unmarshalResponse(res *http.Response) (StackDriver
}
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)
}
var data StackDriverResponse
err = json.Unmarshal(body, &data)
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
}
......@@ -182,8 +184,13 @@ func (e *StackdriverExecutor) parseResponse(data StackDriverResponse, queryRefID
for _, point := range series.Points {
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{
Name: series.Metric.Type,
Name: metricName,
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)
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)
}
......
package stackdriver
import (
"encoding/json"
"fmt"
"io/ioutil"
"testing"
"time"
......@@ -13,8 +15,9 @@ import (
func TestStackdriver(t *testing.T) {
Convey("Stackdriver", t, func() {
executor := &StackdriverExecutor{}
Convey("Parse query from frontend", func() {
executor := &StackdriverExecutor{}
fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local)
tsdbQuery := &tsdb.TsdbQuery{
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["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)
})
})
})
}
{
"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
}
}
]
}
]
}
{
"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 {
TimeSeries []struct {
Metric struct {
Labels struct {
InstanceName string `json:"instance_name"`
} `json:"labels"`
Type string `json:"type"`
Labels map[string]string `json:"labels"`
Type string `json:"type"`
} `json:"metric"`
Resource struct {
Type string `json:"type"`
Labels struct {
InstanceID string `json:"instance_id"`
Zone string `json:"zone"`
ProjectID string `json:"project_id"`
} `json:"labels"`
Type string `json:"type"`
Labels map[string]string `json:"labels"`
} `json:"resource"`
MetricKind string `json:"metricKind"`
ValueType string `json:"valueType"`
......
......@@ -17,6 +17,9 @@ export default class StackdriverDatasource {
datasourceId: this.id,
metricType: `metric.type="${t.metricType}"`,
}));
const result = [];
try {
const { data } = await this.backendSrv.datasourceRequest({
url: '/api/tsdb/query',
......@@ -28,8 +31,6 @@ export default class StackdriverDatasource {
},
});
const result = [];
if (data.results) {
Object['values'](data.results).forEach(queryRes => {
queryRes.series.forEach(series => {
......@@ -37,11 +38,11 @@ export default class StackdriverDatasource {
});
});
}
return { data: result };
} catch (error) {
console.log(error);
}
return { data: result };
}
testDatasource() {
......
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