diff --git a/alert.go b/alert.go index 3b874c6136..9bebf29153 100644 --- a/alert.go +++ b/alert.go @@ -464,10 +464,12 @@ func (a *AlertNode) runAlert([]byte) error { currentLevel = state.currentLevel() } else { // Check for previous state - currentLevel = a.restoreEventState(id) + var triggered time.Time + currentLevel, triggered = a.restoreEventState(id) if currentLevel != alert.OK { // Update the state with the restored state - a.updateState(p.Time, currentLevel, p.Group) + state = a.updateState(p.Time, currentLevel, p.Group) + state.triggered(triggered) } } l := a.determineLevel(p.Time, p.Fields, p.Tags, currentLevel) @@ -554,10 +556,12 @@ func (a *AlertNode) runAlert([]byte) error { currentLevel = state.currentLevel() } else { // Check for previous state - currentLevel = a.restoreEventState(id) + var triggered time.Time + currentLevel, triggered = a.restoreEventState(id) if currentLevel != alert.OK { // Update the state with the restored state - a.updateState(b.TMax, currentLevel, b.Group) + state = a.updateState(b.TMax, currentLevel, b.Group) + state.triggered(triggered) } } for i, p := range b.Points { @@ -683,7 +687,7 @@ func (a *AlertNode) hasTopic() bool { return a.topic != "" } -func (a *AlertNode) restoreEventState(id string) alert.Level { +func (a *AlertNode) restoreEventState(id string) (alert.Level, time.Time) { var topicState, anonTopicState alert.EventState var anonFound, topicFound bool // Check for previous state on anonTopic @@ -714,9 +718,9 @@ func (a *AlertNode) restoreEventState(id string) alert.Level { } // else nothing was found, nothing to do } if anonFound { - return anonTopicState.Level + return anonTopicState.Level, anonTopicState.Time } - return topicState.Level + return topicState.Level, topicState.Time } func (a *AlertNode) handleEvent(event alert.Event) { diff --git a/server/server_test.go b/server/server_test.go index 080a55b737..d0b3d624e6 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -7766,6 +7766,121 @@ stream } } +func TestServer_Alert_Duration(t *testing.T) { + // Setup test TCP server + ts, err := alerttest.NewTCPServer() + if err != nil { + t.Fatal(err) + } + defer ts.Close() + + // Create default config + c := NewConfig() + s := OpenServer(c) + cli := Client(s) + defer s.Close() + + tick := ` +stream + |from() + .measurement('alert') + |alert() + .id('id') + .message('message') + .details('details') + .crit(lambda: "value" > 1.0) + .tcp('` + ts.Addr + `') +` + + if _, err := cli.CreateTask(client.CreateTaskOptions{ + ID: "testAlertHandlers", + Type: client.StreamTask, + DBRPs: []client.DBRP{{ + Database: "mydb", + RetentionPolicy: "myrp", + }}, + TICKscript: tick, + Status: client.Enabled, + }); err != nil { + t.Fatal(err) + } + + // Write point + point := "alert value=2 0000000000" + v := url.Values{} + v.Add("precision", "s") + s.MustWrite("mydb", "myrp", point, v) + + // Restart the server + s.Restart() + + topic := "main:testAlertHandlers:alert2" + l := cli.TopicEventsLink(topic) + expTopicEvents := client.TopicEvents{ + Link: l, + Topic: topic, + Events: []client.TopicEvent{{ + Link: client.Link{Relation: client.Self, Href: fmt.Sprintf("/kapacitor/v1preview/alerts/topics/%s/events/id", topic)}, + ID: "id", + State: client.EventState{ + Message: "message", + Details: "details", + Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), + Duration: 0, + Level: "CRITICAL", + }, + }}, + } + + te, err := cli.ListTopicEvents(l, nil) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(te, expTopicEvents) { + t.Errorf("unexpected topic events for anonymous topic:\ngot\n%+v\nexp\n%+v\n", te, expTopicEvents) + } + event, err := cli.TopicEvent(expTopicEvents.Events[0].Link) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(event, expTopicEvents.Events[0]) { + t.Errorf("unexpected topic event for anonymous topic:\ngot\n%+v\nexp\n%+v\n", event, expTopicEvents.Events[0]) + } + + // Write point + point = "alert value=3 0000000001" + v = url.Values{} + v.Add("precision", "s") + s.MustWrite("mydb", "myrp", point, v) + + // Restart the server + s.Restart() + + expTopicEvents = client.TopicEvents{ + Link: l, + Topic: topic, + Events: []client.TopicEvent{{ + Link: client.Link{Relation: client.Self, Href: fmt.Sprintf("/kapacitor/v1preview/alerts/topics/%s/events/id", topic)}, + ID: "id", + State: client.EventState{ + Message: "message", + Details: "details", + Time: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC), + Duration: client.Duration(time.Second), + Level: "CRITICAL", + }, + }}, + } + + te, err = cli.ListTopicEvents(l, nil) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(te, expTopicEvents) { + t.Errorf("unexpected topic events for anonymous topic after second point:\ngot\n%+v\nexp\n%+v\n", te, expTopicEvents) + } +} + func TestServer_AlertAnonTopic(t *testing.T) { // Setup test TCP server ts, err := alerttest.NewTCPServer()