diff --git a/chronograf.go b/chronograf.go index 9665ae9b59..4ee500b1e4 100644 --- a/chronograf.go +++ b/chronograf.go @@ -234,7 +234,7 @@ type SourcesStore interface { type AlertRule struct { ID string `json:"id,omitempty"` // ID is the unique ID of the alert TICKScript TICKScript `json:"tickscript"` // TICKScript is the raw tickscript associated with this Alert - 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 Alerts []string `json:"alerts"` // Alerts name all the services to notify (e.g. pagerduty) AlertNodes []KapacitorNode `json:"alertNodes,omitempty"` // AlertNodes define additional arguments to alerts diff --git a/kapacitor/ast.go b/kapacitor/ast.go index 9a115a1084..fd503dfdd8 100644 --- a/kapacitor/ast.go +++ b/kapacitor/ast.go @@ -368,6 +368,7 @@ func alertType(script chronograf.TICKScript) (string, error) { func Reverse(script chronograf.TICKScript) (chronograf.AlertRule, error) { rule := chronograf.AlertRule{ Alerts: []string{}, + Query: &chronograf.QueryConfig{}, } t, err := alertType(script) if err != nil { diff --git a/kapacitor/ast_test.go b/kapacitor/ast_test.go index 3fd4985e93..ef74e19199 100644 --- a/kapacitor/ast_test.go +++ b/kapacitor/ast_test.go @@ -84,7 +84,7 @@ func TestReverse(t *testing.T) { Every: "30s", Message: "message", Details: "details", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", RetentionPolicy: "autogen", Measurement: "cpu", @@ -193,7 +193,7 @@ func TestReverse(t *testing.T) { trigger |httpOut('output')`, want: chronograf.AlertRule{ - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -332,7 +332,7 @@ func TestReverse(t *testing.T) { Every: "10s", Message: `Haproxy monitor : {{.ID}} : {{ index .Tags "server" }} : {{ index .Tags "pxname" }} is {{ .Level }} `, Details: "Email template", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "influxdb", RetentionPolicy: "autogen", Measurement: "haproxy", @@ -447,7 +447,7 @@ func TestReverse(t *testing.T) { Every: "10s", Message: `Haproxy monitor : {{.ID}} : {{ index .Tags "server" }} : {{ index .Tags "pxname" }} is {{ .Level }} `, Details: "Email template", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "influxdb", RetentionPolicy: "autogen", Measurement: "haproxy", @@ -564,7 +564,7 @@ func TestReverse(t *testing.T) { Every: "30s", Message: "message", Details: "details", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -588,7 +588,6 @@ func TestReverse(t *testing.T) { Tags: []string{"host", "cluster_id"}, }, AreTagsAccepted: true, - RawText: "", }, }, }, @@ -690,7 +689,7 @@ func TestReverse(t *testing.T) { }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -714,7 +713,6 @@ func TestReverse(t *testing.T) { Tags: []string{"host", "cluster_id"}, }, AreTagsAccepted: true, - RawText: "", }, }, }, @@ -816,7 +814,7 @@ func TestReverse(t *testing.T) { }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -840,7 +838,6 @@ func TestReverse(t *testing.T) { Tags: []string{"host", "cluster_id"}, }, AreTagsAccepted: true, - RawText: "", }, }, }, @@ -930,7 +927,7 @@ func TestReverse(t *testing.T) { Value: "90", }, Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -953,7 +950,6 @@ func TestReverse(t *testing.T) { Tags: []string{"host", "cluster_id"}, }, AreTagsAccepted: true, - RawText: "", }, }, }, @@ -1066,7 +1062,7 @@ trigger }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -1090,7 +1086,6 @@ trigger Tags: []string{"host", "cluster_id"}, }, AreTagsAccepted: true, - RawText: "", }, }, }, @@ -1203,7 +1198,7 @@ trigger }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -1227,7 +1222,6 @@ trigger Tags: []string{"host", "cluster_id"}, }, AreTagsAccepted: true, - RawText: "", }, }, }, @@ -1318,7 +1312,7 @@ trigger Period: "10m0s", }, Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -1336,7 +1330,6 @@ trigger Tags: []string{"host", "cluster_id"}, }, AreTagsAccepted: true, - RawText: "", }, }, }, diff --git a/kapacitor/client.go b/kapacitor/client.go index cd494861f4..35350fa9a0 100644 --- a/kapacitor/client.go +++ b/kapacitor/client.go @@ -185,6 +185,7 @@ func (c *Client) All(ctx context.Context) (map[string]chronograf.AlertRule, erro } } else { rule.ID = task.ID + rule.Query = nil rule.TICKScript = script alerts[task.ID] = rule } @@ -210,6 +211,7 @@ func (c *Client) Get(ctx context.Context, id string) (chronograf.AlertRule, erro return chronograf.AlertRule{ ID: task.ID, Name: task.ID, + Query: nil, TICKScript: script, }, nil } @@ -278,8 +280,8 @@ func (c *Client) kapaClient(ctx context.Context) (*client.Client, error) { }) } -func toTask(q chronograf.QueryConfig) client.TaskType { - if q.RawText == nil || *q.RawText == "" { +func toTask(q *chronograf.QueryConfig) client.TaskType { + if q == nil || q.RawText == nil || *q.RawText == "" { return client.StreamTask } return client.BatchTask diff --git a/kapacitor/data_test.go b/kapacitor/data_test.go index d4dcb0660d..f6c78bfb02 100644 --- a/kapacitor/data_test.go +++ b/kapacitor/data_test.go @@ -45,7 +45,7 @@ func TestData(t *testing.T) { } alert := chronograf.AlertRule{ Trigger: "deadman", - Query: q, + Query: &q, } if tick, err := Data(alert); err != nil { t.Errorf("Error creating tick %v", err) diff --git a/kapacitor/influxout_test.go b/kapacitor/influxout_test.go index 80526ad1a7..fccb485302 100644 --- a/kapacitor/influxout_test.go +++ b/kapacitor/influxout_test.go @@ -28,7 +28,7 @@ func TestInfluxOut(t *testing.T) { got, err := InfluxOut(chronograf.AlertRule{ Name: "name", Trigger: "deadman", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Fields: []chronograf.Field{ { Field: "usage_user", diff --git a/kapacitor/tickscripts_test.go b/kapacitor/tickscripts_test.go index 38f8bc4ff7..dc70d93fe1 100644 --- a/kapacitor/tickscripts_test.go +++ b/kapacitor/tickscripts_test.go @@ -20,7 +20,7 @@ func TestGenerate(t *testing.T) { Value: "90", }, Every: "30s", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -65,7 +65,7 @@ func TestThreshold(t *testing.T) { }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -209,7 +209,7 @@ func TestThresholdStringCrit(t *testing.T) { Every: "10s", Message: `Haproxy monitor : {{.ID}} : {{ index .Tags "server" }} : {{ index .Tags "pxname" }} is {{ .Level }} `, Details: "Email template", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "influxdb", RetentionPolicy: "autogen", Measurement: "haproxy", @@ -347,7 +347,7 @@ func TestThresholdStringCritGreater(t *testing.T) { Every: "10s", Message: `Haproxy monitor : {{.ID}} : {{ index .Tags "server" }} : {{ index .Tags "pxname" }} is {{ .Level }} `, Details: "Email template", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "influxdb", RetentionPolicy: "autogen", Measurement: "haproxy", @@ -483,7 +483,7 @@ func TestThresholdDetail(t *testing.T) { Every: "30s", Message: "message", Details: "details", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -630,7 +630,7 @@ func TestThresholdInsideRange(t *testing.T) { }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -776,7 +776,7 @@ func TestThresholdOutsideRange(t *testing.T) { }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -921,7 +921,7 @@ func TestThresholdNoAggregate(t *testing.T) { }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -1058,7 +1058,7 @@ func TestRelative(t *testing.T) { }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -1215,7 +1215,7 @@ func TestRelativeChange(t *testing.T) { }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", @@ -1369,7 +1369,7 @@ func TestDeadman(t *testing.T) { }, Every: "30s", Message: "message", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "cpu", RetentionPolicy: "autogen", diff --git a/kapacitor/vars.go b/kapacitor/vars.go index 038834b463..7031a9c844 100644 --- a/kapacitor/vars.go +++ b/kapacitor/vars.go @@ -140,40 +140,45 @@ func window(rule chronograf.AlertRule) string { return "" } -func groupBy(q chronograf.QueryConfig) string { +func groupBy(q *chronograf.QueryConfig) string { groups := []string{} - for _, tag := range q.GroupBy.Tags { - groups = append(groups, fmt.Sprintf("'%s'", tag)) + if q != nil { + for _, tag := range q.GroupBy.Tags { + groups = append(groups, fmt.Sprintf("'%s'", tag)) + } } return "[" + strings.Join(groups, ",") + "]" } -func field(q chronograf.QueryConfig) (string, error) { - for _, field := range q.Fields { - return field.Field, nil +func field(q *chronograf.QueryConfig) (string, error) { + if q != nil { + for _, field := range q.Fields { + return field.Field, nil + } } return "", fmt.Errorf("No fields set in query") } -func whereFilter(q chronograf.QueryConfig) string { - operator := "==" - if !q.AreTagsAccepted { - operator = "!=" - } - - outer := []string{} - for tag, values := range q.Tags { - inner := []string{} - for _, value := range values { - inner = append(inner, fmt.Sprintf(`"%s" %s '%s'`, tag, operator, value)) +func whereFilter(q *chronograf.QueryConfig) string { + if q != nil { + operator := "==" + if !q.AreTagsAccepted { + operator = "!=" } - outer = append(outer, "("+strings.Join(inner, " OR ")+")") - } - if len(outer) > 0 { - sort.Strings(outer) - return "lambda: " + strings.Join(outer, " AND ") - } + outer := []string{} + for tag, values := range q.Tags { + inner := []string{} + for _, value := range values { + inner = append(inner, fmt.Sprintf(`"%s" %s '%s'`, tag, operator, value)) + } + outer = append(outer, "("+strings.Join(inner, " OR ")+")") + } + if len(outer) > 0 { + sort.Strings(outer) + return "lambda: " + strings.Join(outer, " AND ") + } + } return "lambda: TRUE" } diff --git a/kapacitor/vars_test.go b/kapacitor/vars_test.go index e19104aeff..f0ac6df364 100644 --- a/kapacitor/vars_test.go +++ b/kapacitor/vars_test.go @@ -16,7 +16,7 @@ func TestVarsCritStringEqual(t *testing.T) { Value: "DOWN", }, Every: "30s", - Query: chronograf.QueryConfig{ + Query: &chronograf.QueryConfig{ Database: "telegraf", Measurement: "haproxy", RetentionPolicy: "autogen", diff --git a/server/kapacitors.go b/server/kapacitors.go index a65d4f9ba8..1f27a78e45 100644 --- a/server/kapacitors.go +++ b/server/kapacitors.go @@ -357,19 +357,15 @@ func newAlertResponse(rule chronograf.AlertRule, tickScript chronograf.TICKScrip Status: status, } - if res.Query.ID == "" { - res.Query.ID = res.ID + if res.Alerts == nil { + res.Alerts = make([]string, 0) } - if res.AlertRule.Alerts == nil { - res.AlertRule.Alerts = make([]string, 0) + if res.AlertNodes == nil { + res.AlertNodes = make([]chronograf.KapacitorNode, 0) } - if res.AlertRule.AlertNodes == nil { - res.AlertRule.AlertNodes = make([]chronograf.KapacitorNode, 0) - } - - for _, n := range res.AlertRule.AlertNodes { + for _, n := range res.AlertNodes { if n.Args == nil { n.Args = make([]string, 0) } @@ -383,22 +379,28 @@ func newAlertResponse(rule chronograf.AlertRule, tickScript chronograf.TICKScrip } } - if res.AlertRule.Query.Fields == nil { - res.AlertRule.Query.Fields = make([]chronograf.Field, 0) - - } - for _, f := range res.AlertRule.Query.Fields { - if f.Funcs == nil { - f.Funcs = make([]string, 0) + if res.Query != nil { + if res.Query.ID == "" { + res.Query.ID = res.ID } - } - if res.AlertRule.Query.GroupBy.Tags == nil { - res.AlertRule.Query.GroupBy.Tags = make([]string, 0) - } + if res.Query.Fields == nil { + res.Query.Fields = make([]chronograf.Field, 0) + } - if res.AlertRule.Query.Tags == nil { - res.AlertRule.Query.Tags = make(map[string][]string) + for _, f := range res.Query.Fields { + if f.Funcs == nil { + f.Funcs = make([]string, 0) + } + } + + if res.Query.GroupBy.Tags == nil { + res.Query.GroupBy.Tags = make([]string, 0) + } + + if res.Query.Tags == nil { + res.Query.Tags = make(map[string][]string) + } } return res }