Add handling of string values for kapacitor threshold alerts
parent
740fae3528
commit
dac665b944
|
@ -199,6 +199,280 @@ trigger
|
|||
}
|
||||
}
|
||||
|
||||
func TestThresholdStringCrit(t *testing.T) {
|
||||
alert := chronograf.AlertRule{
|
||||
Name: "haproxy",
|
||||
Trigger: "threshold",
|
||||
Alerts: []string{"email"},
|
||||
TriggerValues: chronograf.TriggerValues{
|
||||
Operator: "equal to",
|
||||
Value: "DOWN",
|
||||
},
|
||||
Every: "10s",
|
||||
Message: `Haproxy monitor : {{.ID}} : {{ index .Tags "server" }} : {{ index .Tags "pxname" }} is {{ .Level }} `,
|
||||
Details: "Email template",
|
||||
Query: chronograf.QueryConfig{
|
||||
Database: "influxdb",
|
||||
RetentionPolicy: "autogen",
|
||||
Measurement: "haproxy",
|
||||
Fields: []chronograf.Field{
|
||||
{
|
||||
Field: "status",
|
||||
Funcs: []string{"last"},
|
||||
},
|
||||
},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Time: "10s",
|
||||
Tags: []string{"pxname"},
|
||||
},
|
||||
AreTagsAccepted: true,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
alert chronograf.AlertRule
|
||||
want chronograf.TICKScript
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Test valid template alert",
|
||||
alert: alert,
|
||||
want: `var db = 'influxdb'
|
||||
|
||||
var rp = 'autogen'
|
||||
|
||||
var measurement = 'haproxy'
|
||||
|
||||
var groupBy = ['pxname']
|
||||
|
||||
var whereFilter = lambda: TRUE
|
||||
|
||||
var period = 10s
|
||||
|
||||
var every = 10s
|
||||
|
||||
var name = 'haproxy'
|
||||
|
||||
var idVar = name + ':{{.Group}}'
|
||||
|
||||
var message = 'Haproxy monitor : {{.ID}} : {{ index .Tags "server" }} : {{ index .Tags "pxname" }} is {{ .Level }} '
|
||||
|
||||
var idTag = 'alertID'
|
||||
|
||||
var levelTag = 'level'
|
||||
|
||||
var messageField = 'message'
|
||||
|
||||
var durationField = 'duration'
|
||||
|
||||
var outputDB = 'chronograf'
|
||||
|
||||
var outputRP = 'autogen'
|
||||
|
||||
var outputMeasurement = 'alerts'
|
||||
|
||||
var triggerType = 'threshold'
|
||||
|
||||
var details = 'Email template'
|
||||
|
||||
var crit = 'DOWN'
|
||||
|
||||
var data = stream
|
||||
|from()
|
||||
.database(db)
|
||||
.retentionPolicy(rp)
|
||||
.measurement(measurement)
|
||||
.groupBy(groupBy)
|
||||
.where(whereFilter)
|
||||
|window()
|
||||
.period(period)
|
||||
.every(every)
|
||||
.align()
|
||||
|last('status')
|
||||
.as('value')
|
||||
|
||||
var trigger = data
|
||||
|alert()
|
||||
.crit(lambda: "value" == crit)
|
||||
.stateChangesOnly()
|
||||
.message(message)
|
||||
.id(idVar)
|
||||
.idTag(idTag)
|
||||
.levelTag(levelTag)
|
||||
.messageField(messageField)
|
||||
.durationField(durationField)
|
||||
.details(details)
|
||||
.email()
|
||||
|
||||
trigger
|
||||
|influxDBOut()
|
||||
.create()
|
||||
.database(outputDB)
|
||||
.retentionPolicy(outputRP)
|
||||
.measurement(outputMeasurement)
|
||||
.tag('alertName', name)
|
||||
.tag('triggerType', triggerType)
|
||||
|
||||
trigger
|
||||
|httpOut('output')
|
||||
`,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
gen := Alert{}
|
||||
got, err := gen.Generate(tt.alert)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("%q. Threshold() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
continue
|
||||
}
|
||||
if got != tt.want {
|
||||
diff := diffmatchpatch.New()
|
||||
delta := diff.DiffMain(string(tt.want), string(got), true)
|
||||
t.Errorf("%q\n%s", tt.name, diff.DiffPrettyText(delta))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Check with Nathaniel if kapacitor can do inequalities on strings
|
||||
// If it cannot, I think we should add operator checks.
|
||||
func TestThresholdStringCritGreater(t *testing.T) {
|
||||
alert := chronograf.AlertRule{
|
||||
Name: "haproxy",
|
||||
Trigger: "threshold",
|
||||
Alerts: []string{"email"},
|
||||
TriggerValues: chronograf.TriggerValues{
|
||||
Operator: "greater than",
|
||||
Value: "DOWN",
|
||||
},
|
||||
Every: "10s",
|
||||
Message: `Haproxy monitor : {{.ID}} : {{ index .Tags "server" }} : {{ index .Tags "pxname" }} is {{ .Level }} `,
|
||||
Details: "Email template",
|
||||
Query: chronograf.QueryConfig{
|
||||
Database: "influxdb",
|
||||
RetentionPolicy: "autogen",
|
||||
Measurement: "haproxy",
|
||||
Fields: []chronograf.Field{
|
||||
{
|
||||
Field: "status",
|
||||
Funcs: []string{"last"},
|
||||
},
|
||||
},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Time: "10s",
|
||||
Tags: []string{"pxname"},
|
||||
},
|
||||
AreTagsAccepted: true,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
alert chronograf.AlertRule
|
||||
want chronograf.TICKScript
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Test valid template alert",
|
||||
alert: alert,
|
||||
want: `var db = 'influxdb'
|
||||
|
||||
var rp = 'autogen'
|
||||
|
||||
var measurement = 'haproxy'
|
||||
|
||||
var groupBy = ['pxname']
|
||||
|
||||
var whereFilter = lambda: TRUE
|
||||
|
||||
var period = 10s
|
||||
|
||||
var every = 10s
|
||||
|
||||
var name = 'haproxy'
|
||||
|
||||
var idVar = name + ':{{.Group}}'
|
||||
|
||||
var message = 'Haproxy monitor : {{.ID}} : {{ index .Tags "server" }} : {{ index .Tags "pxname" }} is {{ .Level }} '
|
||||
|
||||
var idTag = 'alertID'
|
||||
|
||||
var levelTag = 'level'
|
||||
|
||||
var messageField = 'message'
|
||||
|
||||
var durationField = 'duration'
|
||||
|
||||
var outputDB = 'chronograf'
|
||||
|
||||
var outputRP = 'autogen'
|
||||
|
||||
var outputMeasurement = 'alerts'
|
||||
|
||||
var triggerType = 'threshold'
|
||||
|
||||
var details = 'Email template'
|
||||
|
||||
var crit = 'DOWN'
|
||||
|
||||
var data = stream
|
||||
|from()
|
||||
.database(db)
|
||||
.retentionPolicy(rp)
|
||||
.measurement(measurement)
|
||||
.groupBy(groupBy)
|
||||
.where(whereFilter)
|
||||
|window()
|
||||
.period(period)
|
||||
.every(every)
|
||||
.align()
|
||||
|last('status')
|
||||
.as('value')
|
||||
|
||||
var trigger = data
|
||||
|alert()
|
||||
.crit(lambda: "value" > crit)
|
||||
.stateChangesOnly()
|
||||
.message(message)
|
||||
.id(idVar)
|
||||
.idTag(idTag)
|
||||
.levelTag(levelTag)
|
||||
.messageField(messageField)
|
||||
.durationField(durationField)
|
||||
.details(details)
|
||||
.email()
|
||||
|
||||
trigger
|
||||
|influxDBOut()
|
||||
.create()
|
||||
.database(outputDB)
|
||||
.retentionPolicy(outputRP)
|
||||
.measurement(outputMeasurement)
|
||||
.tag('alertName', name)
|
||||
.tag('triggerType', triggerType)
|
||||
|
||||
trigger
|
||||
|httpOut('output')
|
||||
`,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
gen := Alert{}
|
||||
got, err := gen.Generate(tt.alert)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("%q. Threshold() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
continue
|
||||
}
|
||||
if got != tt.want {
|
||||
diff := diffmatchpatch.New()
|
||||
delta := diff.DiffMain(string(tt.want), string(got), true)
|
||||
t.Errorf("%q\n%s", tt.name, diff.DiffPrettyText(delta))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestThresholdDetail(t *testing.T) {
|
||||
alert := chronograf.AlertRule{
|
||||
Name: "name",
|
||||
|
|
|
@ -3,6 +3,7 @@ package kapacitor
|
|||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
|
@ -39,15 +40,16 @@ func Vars(rule chronograf.AlertRule) (string, error) {
|
|||
%s
|
||||
var crit = %s
|
||||
`
|
||||
return fmt.Sprintf(vars,
|
||||
common,
|
||||
rule.TriggerValues.Value), nil
|
||||
// If critical value is a string, we'll
|
||||
// need to single-quote it.
|
||||
crit := critVar(rule.TriggerValues.Value)
|
||||
return fmt.Sprintf(vars, common, crit), nil
|
||||
} else {
|
||||
vars := `
|
||||
%s
|
||||
var lower = %s
|
||||
var upper = %s
|
||||
`
|
||||
`
|
||||
return fmt.Sprintf(vars,
|
||||
common,
|
||||
rule.TriggerValues.Value,
|
||||
|
@ -178,3 +180,14 @@ func whereFilter(q chronograf.QueryConfig) string {
|
|||
|
||||
return "lambda: TRUE"
|
||||
}
|
||||
|
||||
// critVar return the same string if a numeric type
|
||||
// or if it is a string will return it as a kapacitor
|
||||
// formatted single-quoted string
|
||||
func critVar(value string) string {
|
||||
// Test if numeric if it can be converted to a float
|
||||
if _, err := strconv.ParseFloat(value, 64); err == nil {
|
||||
return value
|
||||
}
|
||||
return "'" + value + "'"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package kapacitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
||||
func TestVarsCritStringEqual(t *testing.T) {
|
||||
alert := chronograf.AlertRule{
|
||||
Name: "name",
|
||||
Trigger: "threshold",
|
||||
TriggerValues: chronograf.TriggerValues{
|
||||
Operator: "equal to",
|
||||
Value: "DOWN",
|
||||
},
|
||||
Every: "30s",
|
||||
Query: chronograf.QueryConfig{
|
||||
Database: "telegraf",
|
||||
Measurement: "haproxy",
|
||||
RetentionPolicy: "autogen",
|
||||
Fields: []chronograf.Field{
|
||||
{
|
||||
Field: "status",
|
||||
},
|
||||
},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Time: "10m",
|
||||
Tags: []string{"pxname"},
|
||||
},
|
||||
AreTagsAccepted: true,
|
||||
},
|
||||
}
|
||||
|
||||
raw, err := Vars(alert)
|
||||
if err != nil {
|
||||
fmt.Printf("%s", raw)
|
||||
t.Fatalf("Error generating alert: %v %s", err, raw)
|
||||
}
|
||||
|
||||
tick, err := formatTick(raw)
|
||||
if err != nil {
|
||||
t.Errorf("Error formatting alert: %v %s", err, raw)
|
||||
}
|
||||
|
||||
if err := validateTick(tick); err != nil {
|
||||
t.Errorf("Error validating alert: %v %s", err, tick)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue