Add kapa alert arguments and properties
parent
aa4b6fb689
commit
bd6f3e1a6b
|
@ -108,7 +108,7 @@ type AlertRule struct {
|
||||||
Query QueryConfig `json:"query"` // Query is the filter of data for the alert.
|
Query QueryConfig `json:"query"` // Query is the filter of data for the alert.
|
||||||
Every string `json:"every"` // Every how often to check for the alerting criteria
|
Every string `json:"every"` // Every how often to check for the alerting criteria
|
||||||
Alerts []string `json:"alerts"` // Alerts name all the services to notify (e.g. pagerduty)
|
Alerts []string `json:"alerts"` // Alerts name all the services to notify (e.g. pagerduty)
|
||||||
AlertNodes []KapacitorNode `json:"alertNodes"` // AlertNodes define additional arguments to alerts
|
AlertNodes []KapacitorNode `json:"alertNodes,omitempty"` // AlertNodes define additional arguments to alerts
|
||||||
Message string `json:"message"` // Message included with alert
|
Message string `json:"message"` // Message included with alert
|
||||||
Details string `json:"details"` // Details is generally used for the Email alert. If empty will not be added.
|
Details string `json:"details"` // Details is generally used for the Email alert. If empty will not be added.
|
||||||
Trigger string `json:"trigger"` // Trigger is a type that defines when to trigger the alert
|
Trigger string `json:"trigger"` // Trigger is a type that defines when to trigger the alert
|
||||||
|
@ -178,7 +178,7 @@ type QueryConfig struct {
|
||||||
type KapacitorNode struct {
|
type KapacitorNode struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Args []string `json:"args"`
|
Args []string `json:"args"`
|
||||||
Properties []*KapacitorProperty `json:"properties"`
|
Properties []KapacitorProperty `json:"properties"`
|
||||||
// In the future we could add chaining methods here.
|
// In the future we could add chaining methods here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,13 @@ package kapacitor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/influxdata/chronograf"
|
"github.com/influxdata/chronograf"
|
||||||
)
|
)
|
||||||
|
|
||||||
func kapaService(alert string) (string, error) {
|
func kapaHandler(handler string) (string, error) {
|
||||||
switch alert {
|
switch handler {
|
||||||
case "hipchat":
|
case "hipchat":
|
||||||
return "hipChat", nil
|
return "hipChat", nil
|
||||||
case "opsgenie":
|
case "opsgenie":
|
||||||
|
@ -18,25 +19,78 @@ func kapaService(alert string) (string, error) {
|
||||||
return "victorOps", nil
|
return "victorOps", nil
|
||||||
case "smtp":
|
case "smtp":
|
||||||
return "email", nil
|
return "email", nil
|
||||||
case "sensu", "slack", "email", "talk", "telegram":
|
case "http":
|
||||||
return alert, nil
|
return "post", nil
|
||||||
|
case "sensu", "slack", "email", "talk", "telegram", "post", "tcp":
|
||||||
|
return handler, nil
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("Unsupport alert %s", alert)
|
return "", fmt.Errorf("Unsupported alert handler %s", handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toKapaFunc(method string, args []string) (string, error) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return fmt.Sprintf(".%s()", method), nil
|
||||||
|
}
|
||||||
|
params := make([]string, len(args))
|
||||||
|
copy(params, args)
|
||||||
|
// Kapacitor strings are quoted
|
||||||
|
for i, p := range params {
|
||||||
|
params[i] = fmt.Sprintf("'%s'", p)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(".%s(%s)", method, strings.Join(params, ",")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addAlertNodes(rule chronograf.AlertRule) (string, error) {
|
||||||
|
alert := ""
|
||||||
|
// Using a map to try to combine older API in .Alerts with .AlertNodes
|
||||||
|
nodes := map[string]chronograf.KapacitorNode{}
|
||||||
|
for _, node := range rule.AlertNodes {
|
||||||
|
handler, err := kapaHandler(node.Name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
nodes[handler] = node
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, a := range rule.Alerts {
|
||||||
|
handler, err := kapaHandler(a)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// If the this handler is not in nodes, then there are
|
||||||
|
// there are no arguments or properties
|
||||||
|
if _, ok := nodes[handler]; !ok {
|
||||||
|
alert = alert + fmt.Sprintf(".%s()", handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for handler, node := range nodes {
|
||||||
|
service, err := toKapaFunc(handler, node.Args)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
alert = alert + service
|
||||||
|
for _, prop := range node.Properties {
|
||||||
|
alertProperty, err := toKapaFunc(prop.Name, prop.Args)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
alert = alert + alertProperty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return alert, nil
|
||||||
|
}
|
||||||
|
|
||||||
// AlertServices generates alert chaining methods to be attached to an alert from all rule Services
|
// AlertServices generates alert chaining methods to be attached to an alert from all rule Services
|
||||||
func AlertServices(rule chronograf.AlertRule) (string, error) {
|
func AlertServices(rule chronograf.AlertRule) (string, error) {
|
||||||
alert := ""
|
node, err := addAlertNodes(rule)
|
||||||
for _, service := range rule.Alerts {
|
|
||||||
srv, err := kapaService(service)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if err := ValidateAlert(srv); err != nil {
|
|
||||||
|
if err := ValidateAlert(node); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
alert = alert + fmt.Sprintf(".%s()", srv)
|
return node, nil
|
||||||
}
|
|
||||||
return alert, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,103 @@ func TestAlertServices(t *testing.T) {
|
||||||
.slack()
|
.slack()
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Test single valid service and property",
|
||||||
|
rule: chronograf.AlertRule{
|
||||||
|
Alerts: []string{"slack"},
|
||||||
|
AlertNodes: []chronograf.KapacitorNode{
|
||||||
|
{
|
||||||
|
Name: "slack",
|
||||||
|
Properties: []chronograf.KapacitorProperty{
|
||||||
|
{
|
||||||
|
Name: "channel",
|
||||||
|
Args: []string{"#general"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `alert()
|
||||||
|
.slack()
|
||||||
|
.channel('#general')
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test tcp",
|
||||||
|
rule: chronograf.AlertRule{
|
||||||
|
AlertNodes: []chronograf.KapacitorNode{
|
||||||
|
{
|
||||||
|
Name: "tcp",
|
||||||
|
Args: []string{"myaddress:22"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `alert()
|
||||||
|
.tcp('myaddress:22')
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test tcp no argument",
|
||||||
|
rule: chronograf.AlertRule{
|
||||||
|
AlertNodes: []chronograf.KapacitorNode{
|
||||||
|
{
|
||||||
|
Name: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test tcp no argument with other services",
|
||||||
|
rule: chronograf.AlertRule{
|
||||||
|
Alerts: []string{"slack", "tcp", "email"},
|
||||||
|
AlertNodes: []chronograf.KapacitorNode{
|
||||||
|
{
|
||||||
|
Name: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test http as post",
|
||||||
|
rule: chronograf.AlertRule{
|
||||||
|
AlertNodes: []chronograf.KapacitorNode{
|
||||||
|
{
|
||||||
|
Name: "http",
|
||||||
|
Args: []string{"http://myaddress"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `alert()
|
||||||
|
.post('http://myaddress')
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test post",
|
||||||
|
rule: chronograf.AlertRule{
|
||||||
|
AlertNodes: []chronograf.KapacitorNode{
|
||||||
|
{
|
||||||
|
Name: "post",
|
||||||
|
Args: []string{"http://myaddress"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `alert()
|
||||||
|
.post('http://myaddress')
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test http no arguments",
|
||||||
|
rule: chronograf.AlertRule{
|
||||||
|
AlertNodes: []chronograf.KapacitorNode{
|
||||||
|
{
|
||||||
|
Name: "http",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
got, err := AlertServices(tt.rule)
|
got, err := AlertServices(tt.rule)
|
||||||
|
|
|
@ -18,7 +18,7 @@ func ValidateAlert(service string) error {
|
||||||
// Simple tick script to check alert service.
|
// Simple tick script to check alert service.
|
||||||
// If a pipeline cannot be created then we know this is an invalid
|
// If a pipeline cannot be created then we know this is an invalid
|
||||||
// service. At least with this version of kapacitor!
|
// service. At least with this version of kapacitor!
|
||||||
script := fmt.Sprintf("stream|from()|alert().%s()", service)
|
script := fmt.Sprintf("stream|from()|alert()%s", service)
|
||||||
return validateTick(chronograf.TICKScript(script))
|
return validateTick(chronograf.TICKScript(script))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,12 @@ func TestValidateAlert(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Test valid template alert",
|
name: "Test valid template alert",
|
||||||
service: "slack",
|
service: ".slack()",
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Test invalid template alert",
|
name: "Test invalid template alert",
|
||||||
service: "invalid",
|
service: ".invalid()",
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue