Skip to content

Commit

Permalink
Merge remote-tracking branch 'grafana/master' into backend-service-in…
Browse files Browse the repository at this point in the history
…-grafanaui

* grafana/master:
  Annotations: Improve annotation option tooltips (grafana#17384)
  InfluxDB: Fixes single quotes are not escaped (grafana#17398)
  Chore: Bump axios to 0.19.0 (grafana#17403)
  Alerting: golint fixes for alerting (grafana#17246)
  Batch disable users (grafana#17254)
  Chore: Remove unused properties in explore (grafana#17359)
  MySQL/Postgres/MSSQL: Add parsing for day, weeks and year intervals in macros (grafana#13086)
  Security: Prevent csv formula injection attack  (grafana#17363)
  • Loading branch information
ryantxu committed Jun 3, 2019
2 parents 69a1960 + 9501227 commit b5b1df9
Show file tree
Hide file tree
Showing 70 changed files with 480 additions and 280 deletions.
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-include local/Makefile

.PHONY: all deps-go deps-js deps build-go build-server build-cli build-js build build-docker-dev build-docker-full lint-go test-go test-js test run clean gosec revive devenv devenv-down
.PHONY: all deps-go deps-js deps build-go build-server build-cli build-js build build-docker-dev build-docker-full lint-go test-go test-js test run clean gosec revive devenv devenv-down revive-alerting

GO := GO111MODULE=on go
GO_FILES := ./pkg/...
Expand Down Expand Up @@ -84,6 +84,11 @@ revive: scripts/go/bin/revive
-config ./scripts/go/configs/revive.toml \
$(GO_FILES)

revive-alerting: scripts/go/bin/revive
@scripts/go/bin/revive \
-formatter stylish \
./pkg/services/alerting/...

# create docker-compose file with provided sources and start them
# example: make devenv sources=postgres,openldap
ifeq ($(sources),)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"@types/react-window": "1.7.0",
"angular-mocks": "1.6.6",
"autoprefixer": "9.5.0",
"axios": "0.18.0",
"axios": "0.19.0",
"babel-core": "7.0.0-bridge.0",
"babel-jest": "24.8.0",
"babel-loader": "8.0.5",
Expand Down
2 changes: 0 additions & 2 deletions packages/grafana-ui/src/types/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@ export class DataSourcePlugin<
export interface DataSourcePluginMeta extends PluginMeta {
builtIn?: boolean; // Is this for all
metrics?: boolean;
tables?: boolean;
logs?: boolean;
explore?: boolean;
annotations?: boolean;
alerting?: boolean;
mixed?: boolean;
Expand Down
4 changes: 2 additions & 2 deletions pkg/api/alerting.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ func AlertTest(c *m.ReqContext, dto dtos.AlertTestCommand) Response {
}

backendCmd := alerting.AlertTestCommand{
OrgId: c.OrgId,
OrgID: c.OrgId,
Dashboard: dto.Dashboard,
PanelId: dto.PanelId,
PanelID: dto.PanelId,
User: c.SignedInUser,
}

Expand Down
28 changes: 28 additions & 0 deletions pkg/components/gtime/gtime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package gtime

import (
"regexp"
"strconv"
"time"
)

// ParseInterval parses and interval with support for all units that Grafana uses.
func ParseInterval(interval string) (time.Duration, error) {
re := regexp.MustCompile(`(\d+)([wdy])`)
result := re.FindSubmatch([]byte(interval))

if len(result) == 3 {
num, _ := strconv.Atoi(string(result[1]))
period := string(result[2])

if period == `d` {
return time.Hour * 24 * time.Duration(num), nil
} else if period == `w` {
return time.Hour * 24 * 7 * time.Duration(num), nil
} else {
return time.Hour * 24 * 7 * 365 * time.Duration(num), nil
}
} else {
return time.ParseDuration(interval)
}
}
34 changes: 34 additions & 0 deletions pkg/components/gtime/gtime_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package gtime

import (
"errors"
"fmt"
"testing"
"time"
)

func TestParseInterval(t *testing.T) {
tcs := []struct {
interval string
duration time.Duration
err error
}{
{interval: "1d", duration: time.Hour * 24},
{interval: "1w", duration: time.Hour * 24 * 7},
{interval: "1y", duration: time.Hour * 24 * 7 * 365},
{interval: "1M", err: errors.New("time: unknown unit M in duration 1M")},
{interval: "invalid-duration", err: errors.New("time: invalid duration invalid-duration")},
}

for i, tc := range tcs {
t.Run(fmt.Sprintf("testcase %d", i), func(t *testing.T) {
res, err := ParseInterval(tc.interval)
if err != nil && err.Error() != tc.err.Error() {
t.Fatalf("expected '%v' got '%v'", tc.err, err)
}
if res != tc.duration {
t.Errorf("expected %v got %v", tc.duration, res)
}
})
}
}
5 changes: 5 additions & 0 deletions pkg/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ type DisableUserCommand struct {
IsDisabled bool
}

type BatchDisableUsersCommand struct {
UserIds []int64
IsDisabled bool
}

type DeleteUserCommand struct {
UserId int64
}
Expand Down
27 changes: 16 additions & 11 deletions pkg/services/alerting/conditions/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,25 @@ var (
rangedTypes = []string{"within_range", "outside_range"}
)

// AlertEvaluator evaluates the reduced value of a timeserie.
// Returning true if a timeserie is violating the condition
// ex: ThresholdEvaluator, NoValueEvaluator, RangeEvaluator
type AlertEvaluator interface {
Eval(reducedValue null.Float) bool
}

type NoValueEvaluator struct{}
type noValueEvaluator struct{}

func (e *NoValueEvaluator) Eval(reducedValue null.Float) bool {
func (e *noValueEvaluator) Eval(reducedValue null.Float) bool {
return !reducedValue.Valid
}

type ThresholdEvaluator struct {
type thresholdEvaluator struct {
Type string
Threshold float64
}

func newThresholdEvaluator(typ string, model *simplejson.Json) (*ThresholdEvaluator, error) {
func newThresholdEvaluator(typ string, model *simplejson.Json) (*thresholdEvaluator, error) {
params := model.Get("params").MustArray()
if len(params) == 0 {
return nil, fmt.Errorf("Evaluator missing threshold parameter")
Expand All @@ -40,12 +43,12 @@ func newThresholdEvaluator(typ string, model *simplejson.Json) (*ThresholdEvalua
return nil, fmt.Errorf("Evaluator has invalid parameter")
}

defaultEval := &ThresholdEvaluator{Type: typ}
defaultEval := &thresholdEvaluator{Type: typ}
defaultEval.Threshold, _ = firstParam.Float64()
return defaultEval, nil
}

func (e *ThresholdEvaluator) Eval(reducedValue null.Float) bool {
func (e *thresholdEvaluator) Eval(reducedValue null.Float) bool {
if !reducedValue.Valid {
return false
}
Expand All @@ -60,13 +63,13 @@ func (e *ThresholdEvaluator) Eval(reducedValue null.Float) bool {
return false
}

type RangedEvaluator struct {
type rangedEvaluator struct {
Type string
Lower float64
Upper float64
}

func newRangedEvaluator(typ string, model *simplejson.Json) (*RangedEvaluator, error) {
func newRangedEvaluator(typ string, model *simplejson.Json) (*rangedEvaluator, error) {
params := model.Get("params").MustArray()
if len(params) == 0 {
return nil, alerting.ValidationError{Reason: "Evaluator missing threshold parameter"}
Expand All @@ -82,13 +85,13 @@ func newRangedEvaluator(typ string, model *simplejson.Json) (*RangedEvaluator, e
return nil, alerting.ValidationError{Reason: "Evaluator has invalid second parameter"}
}

rangedEval := &RangedEvaluator{Type: typ}
rangedEval := &rangedEvaluator{Type: typ}
rangedEval.Lower, _ = firstParam.Float64()
rangedEval.Upper, _ = secondParam.Float64()
return rangedEval, nil
}

func (e *RangedEvaluator) Eval(reducedValue null.Float) bool {
func (e *rangedEvaluator) Eval(reducedValue null.Float) bool {
if !reducedValue.Valid {
return false
}
Expand All @@ -105,6 +108,8 @@ func (e *RangedEvaluator) Eval(reducedValue null.Float) bool {
return false
}

// NewAlertEvaluator is a factory function for returning
// an `AlertEvaluator` depending on the json model.
func NewAlertEvaluator(model *simplejson.Json) (AlertEvaluator, error) {
typ := model.Get("type").MustString()
if typ == "" {
Expand All @@ -120,7 +125,7 @@ func NewAlertEvaluator(model *simplejson.Json) (AlertEvaluator, error) {
}

if typ == "no_value" {
return &NoValueEvaluator{}, nil
return &noValueEvaluator{}, nil
}

return nil, fmt.Errorf("Evaluator invalid evaluator type: %s", typ)
Expand Down
39 changes: 22 additions & 17 deletions pkg/services/alerting/conditions/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,31 @@ import (

func init() {
alerting.RegisterCondition("query", func(model *simplejson.Json, index int) (alerting.Condition, error) {
return NewQueryCondition(model, index)
return newQueryCondition(model, index)
})
}

// QueryCondition is responsible for issue and query, reduce the
// timeseries into single values and evaluate if they are firing or not.
type QueryCondition struct {
Index int
Query AlertQuery
Reducer QueryReducer
Reducer *queryReducer
Evaluator AlertEvaluator
Operator string
HandleRequest tsdb.HandleRequestFunc
}

// AlertQuery contains information about what datasource a query
// should be sent to and the query object.
type AlertQuery struct {
Model *simplejson.Json
DatasourceId int64
DatasourceID int64
From string
To string
}

// Eval evaluates the `QueryCondition`.
func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.ConditionResult, error) {
timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To)

Expand Down Expand Up @@ -101,8 +106,8 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.Conditio

func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *tsdb.TimeRange) (tsdb.TimeSeriesSlice, error) {
getDsInfo := &models.GetDataSourceByIdQuery{
Id: c.Query.DatasourceId,
OrgId: context.Rule.OrgId,
Id: c.Query.DatasourceID,
OrgId: context.Rule.OrgID,
}

if err := bus.Dispatch(getDsInfo); err != nil {
Expand Down Expand Up @@ -154,16 +159,16 @@ func (c *QueryCondition) getRequestForAlertRule(datasource *models.DataSource, t
return req
}

func NewQueryCondition(model *simplejson.Json, index int) (*QueryCondition, error) {
func newQueryCondition(model *simplejson.Json, index int) (*QueryCondition, error) {
condition := QueryCondition{}
condition.Index = index
condition.HandleRequest = tsdb.HandleRequest

queryJson := model.Get("query")
queryJSON := model.Get("query")

condition.Query.Model = queryJson.Get("model")
condition.Query.From = queryJson.Get("params").MustArray()[1].(string)
condition.Query.To = queryJson.Get("params").MustArray()[2].(string)
condition.Query.Model = queryJSON.Get("model")
condition.Query.From = queryJSON.Get("params").MustArray()[1].(string)
condition.Query.To = queryJSON.Get("params").MustArray()[2].(string)

if err := validateFromValue(condition.Query.From); err != nil {
return nil, err
Expand All @@ -173,20 +178,20 @@ func NewQueryCondition(model *simplejson.Json, index int) (*QueryCondition, erro
return nil, err
}

condition.Query.DatasourceId = queryJson.Get("datasourceId").MustInt64()
condition.Query.DatasourceID = queryJSON.Get("datasourceId").MustInt64()

reducerJson := model.Get("reducer")
condition.Reducer = NewSimpleReducer(reducerJson.Get("type").MustString())
reducerJSON := model.Get("reducer")
condition.Reducer = newSimpleReducer(reducerJSON.Get("type").MustString())

evaluatorJson := model.Get("evaluator")
evaluator, err := NewAlertEvaluator(evaluatorJson)
evaluatorJSON := model.Get("evaluator")
evaluator, err := NewAlertEvaluator(evaluatorJSON)
if err != nil {
return nil, err
}
condition.Evaluator = evaluator

operatorJson := model.Get("operator")
operator := operatorJson.Get("type").MustString("and")
operatorJSON := model.Get("operator")
operator := operatorJSON.Get("type").MustString("and")
condition.Operator = operator

return &condition, nil
Expand Down
9 changes: 4 additions & 5 deletions pkg/services/alerting/conditions/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,15 @@ func TestQueryCondition(t *testing.T) {

So(ctx.condition.Query.From, ShouldEqual, "5m")
So(ctx.condition.Query.To, ShouldEqual, "now")
So(ctx.condition.Query.DatasourceId, ShouldEqual, 1)
So(ctx.condition.Query.DatasourceID, ShouldEqual, 1)

Convey("Can read query reducer", func() {
reducer, ok := ctx.condition.Reducer.(*SimpleReducer)
So(ok, ShouldBeTrue)
reducer := ctx.condition.Reducer
So(reducer.Type, ShouldEqual, "avg")
})

Convey("Can read evaluator", func() {
evaluator, ok := ctx.condition.Evaluator.(*ThresholdEvaluator)
evaluator, ok := ctx.condition.Evaluator.(*thresholdEvaluator)
So(ok, ShouldBeTrue)
So(evaluator.Type, ShouldEqual, "gt")
})
Expand Down Expand Up @@ -163,7 +162,7 @@ func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error)
}`))
So(err, ShouldBeNil)

condition, err := NewQueryCondition(jsonModel, 0)
condition, err := newQueryCondition(jsonModel, 0)
So(err, ShouldBeNil)

ctx.condition = condition
Expand Down
16 changes: 8 additions & 8 deletions pkg/services/alerting/conditions/reducer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import (
"github.com/grafana/grafana/pkg/tsdb"
)

type QueryReducer interface {
Reduce(timeSeries *tsdb.TimeSeries) null.Float
}
// queryReducer reduces an timeserie to a nullable float
type queryReducer struct {

type SimpleReducer struct {
// Type is how the timeserie should be reduced.
// Ex avg, sum, max, min, count
Type string
}

func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) null.Float {
func (s *queryReducer) Reduce(series *tsdb.TimeSeries) null.Float {
if len(series.Points) == 0 {
return null.FloatFromPtr(nil)
}
Expand All @@ -31,7 +31,7 @@ func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) null.Float {
for _, point := range series.Points {
if point[0].Valid {
value += point[0].Float64
validPointsCount += 1
validPointsCount++
allNull = false
}
}
Expand Down Expand Up @@ -117,8 +117,8 @@ func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) null.Float {
return null.FloatFrom(value)
}

func NewSimpleReducer(typ string) *SimpleReducer {
return &SimpleReducer{Type: typ}
func newSimpleReducer(t string) *queryReducer {
return &queryReducer{Type: t}
}

func calculateDiff(series *tsdb.TimeSeries, allNull bool, value float64, fn func(float64, float64) float64) (bool, float64) {
Expand Down
Loading

0 comments on commit b5b1df9

Please sign in to comment.