diff --git a/cmd/bosun/conf/conf.go b/cmd/bosun/conf/conf.go index 005a94abf5..0c25463b7e 100644 --- a/cmd/bosun/conf/conf.go +++ b/cmd/bosun/conf/conf.go @@ -449,6 +449,10 @@ type Alert struct { AlertTemplateKeys map[string]*template.Template `json:"-"` } +func (a *Alert) GetName() string { + return a.Name +} + // A Locator stores the information about the location of the rule in the underlying // rule store type Locator interface{} diff --git a/cmd/bosun/conf/unknownNotify.go b/cmd/bosun/conf/unknownNotify.go index 6f6ff30585..c46b79773f 100644 --- a/cmd/bosun/conf/unknownNotify.go +++ b/cmd/bosun/conf/unknownNotify.go @@ -3,6 +3,7 @@ package conf import ( "bytes" "fmt" + "net/url" "time" "bosun.org/cmd/bosun/conf/template" @@ -11,9 +12,17 @@ import ( ) type unknownContext struct { - Time time.Time - Name string - Group models.AlertKeys + Time time.Time + Name string + Group models.AlertKeys + States *models.IncidentState + makeLink func(string, *url.Values) string +} + +func (u *unknownContext) IncidentUnknownLink(i int64) string { + return u.makeLink("/incident", &url.Values{ + "id": []string{fmt.Sprint(i)}, + }) } var defaultUnknownTemplate = &Template{ @@ -44,11 +53,13 @@ func init() { unknownDefaults.body = template.Must(template.New("body").Parse(body)) } -func (n *Notification) PrepareUnknown(t *Template, c SystemConfProvider, name string, aks []models.AlertKey) *PreparedNotifications { +func (n *Notification) PrepareUnknown(t *Template, c SystemConfProvider, name string, aks []models.AlertKey, states *models.IncidentState) *PreparedNotifications { ctx := &unknownContext{ - Time: time.Now().UTC(), - Name: name, - Group: aks, + Time: time.Now().UTC(), + Name: name, + Group: aks, + States: states, + makeLink: c.MakeLink, } pn := &PreparedNotifications{} buf := &bytes.Buffer{} @@ -101,8 +112,8 @@ func (n *Notification) PrepareUnknown(t *Template, c SystemConfProvider, name st return pn } -func (n *Notification) NotifyUnknown(t *Template, c SystemConfProvider, name string, aks []models.AlertKey) { - go n.PrepareUnknown(t, c, name, aks).Send(c) +func (n *Notification) NotifyUnknown(t *Template, c SystemConfProvider, name string, aks []models.AlertKey, states *models.IncidentState) { + go n.PrepareUnknown(t, c, name, aks, states).Send(c) } var unknownMultiDefaults defaultTemplates @@ -111,6 +122,7 @@ type unknownMultiContext struct { Time time.Time Threshold int Groups map[string]models.AlertKeys + States []*models.IncidentState } func init() { @@ -135,11 +147,12 @@ func init() { unknownMultiDefaults.body = template.Must(template.New("body").Parse(body)) } -func (n *Notification) PrepareMultipleUnknowns(t *Template, c SystemConfProvider, groups map[string]models.AlertKeys) *PreparedNotifications { +func (n *Notification) PrepareMultipleUnknowns(t *Template, c SystemConfProvider, groups map[string]models.AlertKeys, states []*models.IncidentState) *PreparedNotifications { ctx := &unknownMultiContext{ Time: time.Now().UTC(), Threshold: c.GetUnknownThreshold(), Groups: groups, + States: states, } pn := &PreparedNotifications{} buf := &bytes.Buffer{} @@ -180,8 +193,8 @@ func (n *Notification) PrepareMultipleUnknowns(t *Template, c SystemConfProvider return pn } -func (n *Notification) NotifyMultipleUnknowns(t *Template, c SystemConfProvider, groups map[string]models.AlertKeys) { - n.PrepareMultipleUnknowns(t, c, groups).Send(c) +func (n *Notification) NotifyMultipleUnknowns(t *Template, c SystemConfProvider, groups map[string]models.AlertKeys, states []*models.IncidentState) { + n.PrepareMultipleUnknowns(t, c, groups, states).Send(c) } // code common to PrepareAction / PrepareUnknown / PrepareMultipleUnknowns diff --git a/cmd/bosun/expr/expr.go b/cmd/bosun/expr/expr.go index 130d779685..e2177c4901 100644 --- a/cmd/bosun/expr/expr.go +++ b/cmd/bosun/expr/expr.go @@ -33,6 +33,7 @@ type State struct { unjoinedOk bool autods int vValue float64 + supplicant interface{} Timer miniprofiler.Timer @@ -96,7 +97,7 @@ func New(expr string, funcs ...map[string]parse.Func) (*Expr, error) { // Execute applies a parse expression to the specified OpenTSDB context, and // returns one result per group. T may be nil to ignore timings. -func (e *Expr) Execute(backends *Backends, providers *BosunProviders, T miniprofiler.Timer, now time.Time, autods int, unjoinedOk bool) (r *Results, queries []opentsdb.Request, err error) { +func (e *Expr) Execute(backends *Backends, providers *BosunProviders, T miniprofiler.Timer, now time.Time, autods int, unjoinedOk bool, supplicant interface{}) (r *Results, queries []opentsdb.Request, err error) { if providers.Squelched == nil { providers.Squelched = func(tags opentsdb.TagSet) bool { return false @@ -109,7 +110,9 @@ func (e *Expr) Execute(backends *Backends, providers *BosunProviders, T miniprof unjoinedOk: unjoinedOk, Backends: backends, BosunProviders: providers, + supplicant: supplicant, Timer: T, + supplicant: supplicant, } return e.ExecuteState(s) } diff --git a/cmd/bosun/expr/expr_test.go b/cmd/bosun/expr/expr_test.go index 3a744ee99d..56f01d1508 100644 --- a/cmd/bosun/expr/expr_test.go +++ b/cmd/bosun/expr/expr_test.go @@ -68,7 +68,7 @@ func TestExprSimple(t *testing.T) { InfluxConfig: client.HTTPConfig{}, } providers := &BosunProviders{} - r, _, err := e.Execute(backends, providers, nil, time.Now(), 0, false) + r, _, err := e.Execute(backends, providers, nil, time.Now(), 0, false, nil) if err != nil { t.Error(err) break @@ -214,7 +214,7 @@ func TestQueryExpr(t *testing.T) { InfluxConfig: client.HTTPConfig{}, } providers := &BosunProviders{} - results, _, err := e.Execute(backends, providers, nil, queryTime, 0, false) + results, _, err := e.Execute(backends, providers, nil, queryTime, 0, false, nil) if err != nil { t.Fatal(err) } diff --git a/cmd/bosun/expr/funcs_test.go b/cmd/bosun/expr/funcs_test.go index e03d6b2058..548d711663 100644 --- a/cmd/bosun/expr/funcs_test.go +++ b/cmd/bosun/expr/funcs_test.go @@ -32,7 +32,7 @@ func testExpression(eio exprInOut) error { InfluxConfig: client.HTTPConfig{}, } providers := &BosunProviders{} - r, _, err := e.Execute(backends, providers, nil, queryTime, 0, false) + r, _, err := e.Execute(backends, providers, nil, queryTime, 0, false, nil) if err != nil { return err } diff --git a/cmd/bosun/expr/graphite.go b/cmd/bosun/expr/graphite.go index f3421a7a36..d3f7532f59 100644 --- a/cmd/bosun/expr/graphite.go +++ b/cmd/bosun/expr/graphite.go @@ -179,9 +179,10 @@ func GraphiteQuery(e *State, query string, sduration, eduration, format string) st := e.now.Add(-time.Duration(sd)) et := e.now.Add(-time.Duration(ed)) req := &graphite.Request{ - Targets: []string{query}, - Start: &st, - End: &et, + Targets: []string{query}, + Start: &st, + End: &et, + DataProv: e.supplicant, } s, err := timeGraphiteRequest(e, req) if err != nil { @@ -215,6 +216,7 @@ func timeGraphiteRequest(e *State, req *graphite.Request) (resp graphite.Respons e.Timer.StepCustomTiming("graphite", "query", string(b), func() { key := req.CacheKey() getFn := func() (interface{}, error) { + e.GraphiteContext.SubstitueHeaders(req) return e.GraphiteContext.Query(req) } var val interface{} diff --git a/cmd/bosun/sched/check.go b/cmd/bosun/sched/check.go index c13142a478..ce695f2d32 100644 --- a/cmd/bosun/sched/check.go +++ b/cmd/bosun/sched/check.go @@ -643,7 +643,7 @@ func (s *Schedule) executeExpr(T miniprofiler.Timer, rh *RunHistory, a *conf.Ale History: s, Annotate: s.annotate, } - results, _, err := e.Execute(rh.Backends, providers, T, rh.Start, 0, a.UnjoinedOK) + results, _, err := e.Execute(rh.Backends, providers, T, rh.Start, 0, a.UnjoinedOK, a) return results, err } diff --git a/cmd/bosun/sched/notify.go b/cmd/bosun/sched/notify.go index 4ecd7a78c4..ccab66ec6b 100644 --- a/cmd/bosun/sched/notify.go +++ b/cmd/bosun/sched/notify.go @@ -186,6 +186,8 @@ func (s *Schedule) sendUnknownNotifications() { ustates[st.AlertKey] = st } var c int + var multiUstates []*models.IncidentState + hitThreshold := false overThresholdSets := make(map[string]models.AlertKeys) minGroupSize := s.SystemConf.GetMinGroupSize() @@ -199,20 +201,23 @@ func (s *Schedule) sendUnknownNotifications() { } for name, group := range groupSets { c++ - if c >= threshold && threshold > 0 { - if !hitThreshold && len(groupSets) == c { - // If the threshold is hit but only 1 email remains, just send the normal unknown - n.NotifyUnknown(gk.template, s.SystemConf, name, group) - break + for _, ak := range group { + if c >= threshold && threshold > 0 { + if !hitThreshold && len(groupSets) == c { + // If the threshold is hit but only 1 email remains, just send the normal unknown + n.NotifyUnknown(gk.template, s.SystemConf, name, group, ustates[ak]) + break + } + hitThreshold = true + overThresholdSets[name] = group + multiUstates = append(multiUstates, ustates[ak]) + } else { + n.NotifyUnknown(gk.template, s.SystemConf, name, group, ustates[ak]) } - hitThreshold = true - overThresholdSets[name] = group - } else { - n.NotifyUnknown(gk.template, s.SystemConf, name, group) } } if len(overThresholdSets) > 0 { - n.NotifyMultipleUnknowns(gk.template, s.SystemConf, overThresholdSets) + n.NotifyMultipleUnknowns(gk.template, s.SystemConf, overThresholdSets, multiUstates) } } s.pendingUnknowns = make(map[notificationGroupKey][]*models.IncidentState) diff --git a/cmd/bosun/sched/template.go b/cmd/bosun/sched/template.go index 55fce27667..99b0d13ca5 100644 --- a/cmd/bosun/sched/template.go +++ b/cmd/bosun/sched/template.go @@ -303,7 +303,7 @@ func (c *Context) evalExpr(e *expr.Expr, filter bool, series bool, autods int) ( Squelched: c.schedule.RuleConf.AlertSquelched(c.Alert), History: c.schedule, } - res, _, err := e.Execute(c.runHistory.Backends, providers, nil, c.runHistory.Start, autods, c.Alert.UnjoinedOK) + res, _, err := e.Execute(c.runHistory.Backends, providers, nil, c.runHistory.Start, autods, c.Alert.UnjoinedOK, c.Alert) if err != nil { return nil, "", fmt.Errorf("%s: %v", e, err) } diff --git a/cmd/bosun/web/chart.go b/cmd/bosun/web/chart.go index a3e928ae3f..10a5910918 100644 --- a/cmd/bosun/web/chart.go +++ b/cmd/bosun/web/chart.go @@ -253,7 +253,7 @@ func ExprGraph(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (in Squelched: nil, History: nil, } - res, _, err := e.Execute(backends, providers, t, now, autods, false) + res, _, err := e.Execute(backends, providers, t, now, autods, false, nil) if err != nil { return nil, err } diff --git a/cmd/bosun/web/expr.go b/cmd/bosun/web/expr.go index 786a9ea069..ab1d9f3d6e 100644 --- a/cmd/bosun/web/expr.go +++ b/cmd/bosun/web/expr.go @@ -95,7 +95,7 @@ func Expr(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (v inter History: nil, Annotate: AnnotateBackend, } - res, queries, err := e.Execute(backends, providers, t, now, 0, false) + res, queries, err := e.Execute(backends, providers, t, now, 0, false, nil) if err != nil { return nil, err } diff --git a/cmd/bosun/web/static.go b/cmd/bosun/web/static.go index 1bc5dc38ac..f391c4077a 100644 --- a/cmd/bosun/web/static.go +++ b/cmd/bosun/web/static.go @@ -24747,32 +24747,32 @@ kPbj6XFNA6z51KuYSaTMHYWJRyzmlGCtFn9hrRxGXePwpOlxhYTb5W8O37F+S5OMtQkoZ+OZhB8rhTJy "/partials/incident.html": { local: "web/static/partials/incident.html", - size: 5542, + size: 5554, modtime: 0, compressed: ` -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 `, }, @@ -24932,48 +24932,48 @@ Pg0Exj9G3PR0Lhd5Fu79+Iz8EQAA//8VMhJI5ggAAA== "/templates/index.html": { local: "web/static/templates/index.html", - size: 8578, + size: 8595, modtime: 0, compressed: ` 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 `, }, diff --git a/cmd/bosun/web/static/partials/incident.html b/cmd/bosun/web/static/partials/incident.html index d6c782adae..2be1b7e2d7 100644 --- a/cmd/bosun/web/static/partials/incident.html +++ b/cmd/bosun/web/static/partials/incident.html @@ -130,7 +130,7 @@

Events

- +

diff --git a/cmd/bosun/web/static/templates/index.html b/cmd/bosun/web/static/templates/index.html index cd324dfb8a..60ab0e6c08 100644 --- a/cmd/bosun/web/static/templates/index.html +++ b/cmd/bosun/web/static/templates/index.html @@ -117,8 +117,8 @@