Merge branch 'master' into master
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/endpointcreds"
|
||||
"github.com/aws/aws-sdk-go/aws/defaults"
|
||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
@@ -128,10 +129,10 @@ func remoteCredProvider(sess *session.Session) credentials.Provider {
|
||||
func ecsCredProvider(sess *session.Session, uri string) credentials.Provider {
|
||||
const host = `169.254.170.2`
|
||||
|
||||
c := ec2metadata.New(sess)
|
||||
d := defaults.Get()
|
||||
return endpointcreds.NewProviderClient(
|
||||
c.Client.Config,
|
||||
c.Client.Handlers,
|
||||
*d.Config,
|
||||
d.Handlers,
|
||||
fmt.Sprintf("http://%s%s", host, uri),
|
||||
func(p *endpointcreds.Provider) { p.ExpiryWindow = 5 * time.Minute })
|
||||
}
|
||||
@@ -141,6 +142,11 @@ func ec2RoleProvider(sess *session.Session) credentials.Provider {
|
||||
}
|
||||
|
||||
func (e *CloudWatchExecutor) getDsInfo(region string) *DatasourceInfo {
|
||||
defaultRegion := e.DataSource.JsonData.Get("defaultRegion").MustString()
|
||||
if region == "default" {
|
||||
region = defaultRegion
|
||||
}
|
||||
|
||||
authType := e.DataSource.JsonData.Get("authType").MustString()
|
||||
assumeRoleArn := e.DataSource.JsonData.Get("assumeRoleArn").MustString()
|
||||
accessKey := ""
|
||||
|
||||
@@ -127,7 +127,7 @@ func init() {
|
||||
"AWS/Events": {"RuleName"},
|
||||
"AWS/Firehose": {"DeliveryStreamName"},
|
||||
"AWS/IoT": {"Protocol"},
|
||||
"AWS/Kinesis": {"StreamName", "ShardID"},
|
||||
"AWS/Kinesis": {"StreamName", "ShardId"},
|
||||
"AWS/KinesisAnalytics": {"Flow", "Id", "Application"},
|
||||
"AWS/Lambda": {"FunctionName", "Resource", "Version", "Alias"},
|
||||
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
type GraphiteExecutor struct {
|
||||
@@ -158,7 +158,7 @@ func formatTimeRange(input string) string {
|
||||
if input == "now" {
|
||||
return input
|
||||
}
|
||||
return strings.Replace(strings.Replace(input, "m", "min", -1), "M", "mon", -1)
|
||||
return strings.Replace(strings.Replace(strings.Replace(input, "now", "", -1), "m", "min", -1), "M", "mon", -1)
|
||||
}
|
||||
|
||||
func fixIntervalFormat(target string) string {
|
||||
|
||||
@@ -18,14 +18,14 @@ func TestGraphiteFunctions(t *testing.T) {
|
||||
Convey("formatting time range for now-1m", func() {
|
||||
|
||||
timeRange := formatTimeRange("now-1m")
|
||||
So(timeRange, ShouldEqual, "now-1min")
|
||||
So(timeRange, ShouldEqual, "-1min")
|
||||
|
||||
})
|
||||
|
||||
Convey("formatting time range for now-1M", func() {
|
||||
|
||||
timeRange := formatTimeRange("now-1M")
|
||||
So(timeRange, ShouldEqual, "now-1mon")
|
||||
So(timeRange, ShouldEqual, "-1mon")
|
||||
|
||||
})
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ func TestInfluxdbQueryParser(t *testing.T) {
|
||||
Convey("can parse influxdb json model", func() {
|
||||
json := `
|
||||
{
|
||||
"dsType": "influxdb",
|
||||
"groupBy": [
|
||||
{
|
||||
"params": [
|
||||
@@ -123,7 +122,6 @@ func TestInfluxdbQueryParser(t *testing.T) {
|
||||
Convey("can part raw query json model", func() {
|
||||
json := `
|
||||
{
|
||||
"dsType": "influxdb",
|
||||
"groupBy": [
|
||||
{
|
||||
"params": [
|
||||
|
||||
@@ -50,6 +50,7 @@ func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResul
|
||||
result = append(result, &tsdb.TimeSeries{
|
||||
Name: rp.formatSerieName(row, column, query),
|
||||
Points: points,
|
||||
Tags: row.Tags,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ func (m *PostgresMacroEngine) evaluateMacro(name string, args []string) (string,
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing interval %v", args[1])
|
||||
}
|
||||
return fmt.Sprintf("(extract(epoch from \"%s\")/%v)::bigint*%v", args[0], interval.Seconds(), interval.Seconds()), nil
|
||||
return fmt.Sprintf("(extract(epoch from %s)/%v)::bigint*%v AS time", args[0], interval.Seconds(), interval.Seconds()), nil
|
||||
case "__unixEpochFilter":
|
||||
if len(args) == 0 {
|
||||
return "", fmt.Errorf("missing time column argument for macro %v", name)
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestMacroEngine(t *testing.T) {
|
||||
sql, err := engine.Interpolate(timeRange, "GROUP BY $__timeGroup(time_column,'5m')")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, "GROUP BY (extract(epoch from \"time_column\")/300)::bigint*300")
|
||||
So(sql, ShouldEqual, "GROUP BY (extract(epoch from time_column)/300)::bigint*300 AS time")
|
||||
})
|
||||
|
||||
Convey("interpolate __timeTo function", func() {
|
||||
|
||||
@@ -78,6 +78,15 @@ func (e PostgresQueryEndpoint) transformToTable(query *tsdb.Query, rows *core.Ro
|
||||
|
||||
rowLimit := 1000000
|
||||
rowCount := 0
|
||||
timeIndex := -1
|
||||
|
||||
// check if there is a column named time
|
||||
for i, col := range columnNames {
|
||||
switch col {
|
||||
case "time":
|
||||
timeIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
for ; rows.Next(); rowCount++ {
|
||||
if rowCount > rowLimit {
|
||||
@@ -89,6 +98,15 @@ func (e PostgresQueryEndpoint) transformToTable(query *tsdb.Query, rows *core.Ro
|
||||
return err
|
||||
}
|
||||
|
||||
// convert column named time to unix timestamp to make
|
||||
// native datetime postgres types work in annotation queries
|
||||
if timeIndex != -1 {
|
||||
switch value := values[timeIndex].(type) {
|
||||
case time.Time:
|
||||
values[timeIndex] = float64(value.UnixNano() / 1e9)
|
||||
}
|
||||
}
|
||||
|
||||
table.Rows = append(table.Rows, values)
|
||||
}
|
||||
|
||||
@@ -142,8 +160,13 @@ func (e PostgresQueryEndpoint) getTypedRowData(rows *core.Rows) (tsdb.RowValues,
|
||||
func (e PostgresQueryEndpoint) transformToTimeSeries(query *tsdb.Query, rows *core.Rows, result *tsdb.QueryResult) error {
|
||||
pointsBySeries := make(map[string]*tsdb.TimeSeries)
|
||||
seriesByQueryOrder := list.New()
|
||||
columnNames, err := rows.Columns()
|
||||
|
||||
columnNames, err := rows.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
columnTypes, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -153,13 +176,21 @@ func (e PostgresQueryEndpoint) transformToTimeSeries(query *tsdb.Query, rows *co
|
||||
timeIndex := -1
|
||||
metricIndex := -1
|
||||
|
||||
// check columns of resultset
|
||||
// check columns of resultset: a column named time is mandatory
|
||||
// the first text column is treated as metric name unless a column named metric is present
|
||||
for i, col := range columnNames {
|
||||
switch col {
|
||||
case "time":
|
||||
timeIndex = i
|
||||
case "metric":
|
||||
metricIndex = i
|
||||
default:
|
||||
if metricIndex == -1 {
|
||||
switch columnTypes[i].DatabaseTypeName() {
|
||||
case "UNKNOWN", "TEXT", "VARCHAR", "CHAR":
|
||||
metricIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,41 +83,48 @@ func (e *PrometheusExecutor) getClient(dsInfo *models.DataSource) (apiv1.API, er
|
||||
}
|
||||
|
||||
func (e *PrometheusExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
result := &tsdb.Response{}
|
||||
result := &tsdb.Response{
|
||||
Results: map[string]*tsdb.QueryResult{},
|
||||
}
|
||||
|
||||
client, err := e.getClient(dsInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query, err := parseQuery(dsInfo, tsdbQuery.Queries, tsdbQuery)
|
||||
querys, err := parseQuery(dsInfo, tsdbQuery.Queries, tsdbQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeRange := apiv1.Range{
|
||||
Start: query.Start,
|
||||
End: query.End,
|
||||
Step: query.Step,
|
||||
for _, query := range querys {
|
||||
timeRange := apiv1.Range{
|
||||
Start: query.Start,
|
||||
End: query.End,
|
||||
Step: query.Step,
|
||||
}
|
||||
|
||||
plog.Debug("Sending query", "start", timeRange.Start, "end", timeRange.End, "step", timeRange.Step, "query", query.Expr)
|
||||
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "alerting.prometheus")
|
||||
span.SetTag("expr", query.Expr)
|
||||
span.SetTag("start_unixnano", int64(query.Start.UnixNano()))
|
||||
span.SetTag("stop_unixnano", int64(query.End.UnixNano()))
|
||||
defer span.Finish()
|
||||
|
||||
value, err := client.QueryRange(ctx, query.Expr, timeRange)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queryResult, err := parseResponse(value, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Results[query.RefId] = queryResult
|
||||
}
|
||||
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "alerting.prometheus")
|
||||
span.SetTag("expr", query.Expr)
|
||||
span.SetTag("start_unixnano", int64(query.Start.UnixNano()))
|
||||
span.SetTag("stop_unixnano", int64(query.End.UnixNano()))
|
||||
defer span.Finish()
|
||||
|
||||
value, err := client.QueryRange(ctx, query.Expr, timeRange)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queryResult, err := parseResponse(value, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Results = queryResult
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -140,51 +147,54 @@ func formatLegend(metric model.Metric, query *PrometheusQuery) string {
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func parseQuery(dsInfo *models.DataSource, queries []*tsdb.Query, queryContext *tsdb.TsdbQuery) (*PrometheusQuery, error) {
|
||||
queryModel := queries[0]
|
||||
func parseQuery(dsInfo *models.DataSource, queries []*tsdb.Query, queryContext *tsdb.TsdbQuery) ([]*PrometheusQuery, error) {
|
||||
qs := []*PrometheusQuery{}
|
||||
for _, queryModel := range queries {
|
||||
expr, err := queryModel.Model.Get("expr").String()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expr, err := queryModel.Model.Get("expr").String()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
format := queryModel.Model.Get("legendFormat").MustString("")
|
||||
|
||||
start, err := queryContext.TimeRange.ParseFrom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
end, err := queryContext.TimeRange.ParseTo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dsInterval, err := tsdb.GetIntervalFrom(dsInfo, queryModel.Model, time.Second*15)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
intervalFactor := queryModel.Model.Get("intervalFactor").MustInt64(1)
|
||||
interval := intervalCalculator.Calculate(queryContext.TimeRange, dsInterval)
|
||||
step := time.Duration(int64(interval.Value) * intervalFactor)
|
||||
|
||||
qs = append(qs, &PrometheusQuery{
|
||||
Expr: expr,
|
||||
Step: step,
|
||||
LegendFormat: format,
|
||||
Start: start,
|
||||
End: end,
|
||||
RefId: queryModel.RefId,
|
||||
})
|
||||
}
|
||||
|
||||
format := queryModel.Model.Get("legendFormat").MustString("")
|
||||
|
||||
start, err := queryContext.TimeRange.ParseFrom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
end, err := queryContext.TimeRange.ParseTo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dsInterval, err := tsdb.GetIntervalFrom(dsInfo, queryModel.Model, time.Second*15)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
intervalFactor := queryModel.Model.Get("intervalFactor").MustInt64(1)
|
||||
interval := intervalCalculator.Calculate(queryContext.TimeRange, dsInterval)
|
||||
step := time.Duration(int64(interval.Value) * intervalFactor)
|
||||
|
||||
return &PrometheusQuery{
|
||||
Expr: expr,
|
||||
Step: step,
|
||||
LegendFormat: format,
|
||||
Start: start,
|
||||
End: end,
|
||||
}, nil
|
||||
return qs, nil
|
||||
}
|
||||
|
||||
func parseResponse(value model.Value, query *PrometheusQuery) (map[string]*tsdb.QueryResult, error) {
|
||||
queryResults := make(map[string]*tsdb.QueryResult)
|
||||
func parseResponse(value model.Value, query *PrometheusQuery) (*tsdb.QueryResult, error) {
|
||||
queryRes := tsdb.NewQueryResult()
|
||||
|
||||
data, ok := value.(model.Matrix)
|
||||
if !ok {
|
||||
return queryResults, fmt.Errorf("Unsupported result format: %s", value.Type().String())
|
||||
return queryRes, fmt.Errorf("Unsupported result format: %s", value.Type().String())
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
@@ -204,6 +214,5 @@ func parseResponse(value model.Value, query *PrometheusQuery) (map[string]*tsdb.
|
||||
queryRes.Series = append(queryRes.Series, &series)
|
||||
}
|
||||
|
||||
queryResults["A"] = queryRes
|
||||
return queryResults, nil
|
||||
return queryRes, nil
|
||||
}
|
||||
|
||||
@@ -60,9 +60,10 @@ func TestPrometheus(t *testing.T) {
|
||||
Convey("with 48h time range", func() {
|
||||
queryContext.TimeRange = tsdb.NewTimeRange("12h", "now")
|
||||
|
||||
model, err := parseQuery(dsInfo, queryModels, queryContext)
|
||||
|
||||
models, err := parseQuery(dsInfo, queryModels, queryContext)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
model := models[0]
|
||||
So(model.Step, ShouldEqual, time.Second*30)
|
||||
})
|
||||
})
|
||||
@@ -83,18 +84,22 @@ func TestPrometheus(t *testing.T) {
|
||||
Convey("with 48h time range", func() {
|
||||
queryContext.TimeRange = tsdb.NewTimeRange("48h", "now")
|
||||
|
||||
model, err := parseQuery(dsInfo, queryModels, queryContext)
|
||||
models, err := parseQuery(dsInfo, queryModels, queryContext)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
model := models[0]
|
||||
So(model.Step, ShouldEqual, time.Minute*2)
|
||||
})
|
||||
|
||||
Convey("with 1h time range", func() {
|
||||
queryContext.TimeRange = tsdb.NewTimeRange("1h", "now")
|
||||
|
||||
model, err := parseQuery(dsInfo, queryModels, queryContext)
|
||||
models, err := parseQuery(dsInfo, queryModels, queryContext)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
model := models[0]
|
||||
So(model.Step, ShouldEqual, time.Second*15)
|
||||
})
|
||||
})
|
||||
@@ -116,9 +121,11 @@ func TestPrometheus(t *testing.T) {
|
||||
Convey("with 48h time range", func() {
|
||||
queryContext.TimeRange = tsdb.NewTimeRange("48h", "now")
|
||||
|
||||
model, err := parseQuery(dsInfo, queryModels, queryContext)
|
||||
models, err := parseQuery(dsInfo, queryModels, queryContext)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
model := models[0]
|
||||
So(model.Step, ShouldEqual, time.Minute*20)
|
||||
})
|
||||
})
|
||||
@@ -139,9 +146,11 @@ func TestPrometheus(t *testing.T) {
|
||||
Convey("with 48h time range", func() {
|
||||
queryContext.TimeRange = tsdb.NewTimeRange("48h", "now")
|
||||
|
||||
model, err := parseQuery(dsInfo, queryModels, queryContext)
|
||||
models, err := parseQuery(dsInfo, queryModels, queryContext)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
model := models[0]
|
||||
So(model.Step, ShouldEqual, time.Minute*2)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,4 +8,5 @@ type PrometheusQuery struct {
|
||||
LegendFormat string
|
||||
Start time.Time
|
||||
End time.Time
|
||||
RefId string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user