bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/conf/actionNotify.go (about) 1 package conf 2 3 import ( 4 "bytes" 5 "fmt" 6 "net/url" 7 "strings" 8 9 "bosun.org/slog" 10 11 "bosun.org/cmd/bosun/conf/template" 12 "bosun.org/models" 13 ) 14 15 type defaultTemplates struct { 16 body, subject *template.Template 17 } 18 19 var actionDefaults defaultTemplates 20 21 func init() { 22 subject := `{{$first := index .States 0}}{{$count := len .States}} 23 {{.User}} {{.ActionType}} 24 {{if gt $count 1}} {{$count}} Alerts. 25 {{else}} Incident #{{$first.Id}} ({{$first.Subject}}) 26 {{end}}` 27 body := `{{$count := len .States}}{{.User}} {{.ActionType}} {{$count}} alert{{if gt $count 1}}s{{end}}: <br/> 28 <strong>Message:</strong> {{.Message}} <br/> 29 <strong>Incidents:</strong> <br/> 30 <ul> 31 {{range .States}} 32 <li> 33 <a href="{{$.IncidentLink .Id}}">#{{.Id}}:</a> 34 {{.Subject}} 35 </li> 36 {{end}} 37 </ul>` 38 actionDefaults.subject = template.Must(template.New("subject").Parse(strings.Replace(subject, "\n", "", -1))) 39 actionDefaults.body = template.Must(template.New("body").Parse(body)) 40 } 41 42 type ActionNotificationContext struct { 43 States []*ActionNotificationIncidentState 44 User string 45 Message string 46 ActionType models.ActionType 47 makeLink func(string, *url.Values) string 48 } 49 50 type ActionNotificationIncidentState struct { 51 *models.IncidentState 52 AlertVars Vars 53 } 54 55 func (a ActionNotificationContext) IncidentLink(i int64) string { 56 return a.makeLink("/incident", &url.Values{ 57 "id": []string{fmt.Sprint(i)}, 58 }) 59 } 60 61 // NotifyAction should be used for action notifications. 62 func (n *Notification) NotifyAction(at models.ActionType, t *Template, c SystemConfProvider, states []*models.IncidentState, user, message string, rcp RuleConfProvider) { 63 go n.PrepareAction(at, t, c, states, user, message, rcp).Send(c) 64 } 65 66 func (n *Notification) RunOnActionType(at models.ActionType) bool { 67 if n.RunOnActions == "all" || n.RunOnActions == "true" { 68 return true 69 } 70 if n.RunOnActions == "none" || n.RunOnActions == "false" { 71 return false 72 } 73 for _, a := range strings.Split(n.RunOnActions, ",") { 74 if models.ActionShortNames[a] == at { 75 return true 76 } 77 } 78 return false 79 } 80 81 // Prepate an action notification, but don't send yet. 82 func (n *Notification) PrepareAction(at models.ActionType, t *Template, c SystemConfProvider, states []*models.IncidentState, user, message string, rcp RuleConfProvider) *PreparedNotifications { 83 pn := &PreparedNotifications{Name: n.Name, Print: n.Print} 84 // get template keys to use for actions. Merge with default sets 85 tks := n.ActionTemplateKeys[at].Combine(n.ActionTemplateKeys[models.ActionNone]) 86 buf := &bytes.Buffer{} 87 render := func(key string, defaultTmpl *template.Template) (string, error) { 88 tpl := defaultTmpl 89 if key != "" { 90 tpl = t.Get(key) 91 } else { 92 key = "default" 93 } 94 buf.Reset() 95 var actionStates []*ActionNotificationIncidentState 96 for _, state := range states { 97 actionStates = append(actionStates, &ActionNotificationIncidentState{IncidentState: state, AlertVars: rcp.GetAlert(state.Alert).Vars}) 98 } 99 ctx := ActionNotificationContext{ 100 States: actionStates, 101 User: user, 102 Message: message, 103 ActionType: at, 104 makeLink: c.MakeLink, 105 } 106 err := tpl.Execute(buf, ctx) 107 if err != nil { 108 e := fmt.Sprintf("executing action template '%s': %s", key, err) 109 pn.Errors = append(pn.Errors, e) 110 slog.Errorf(e) 111 return "", err 112 } 113 return buf.String(), nil 114 } 115 116 ak := map[string][]string{ 117 "alert_key": {}, 118 } 119 120 for i := range states { 121 contain := func(key string, array []string) bool { 122 for _, value := range array { 123 if value == key { 124 return true 125 } 126 } 127 return false 128 } 129 130 if contain(fmt.Sprint(states[i].AlertKey), ak["alert_key"]) != true { 131 ak["alert_key"] = append(ak["alert_key"], fmt.Sprint(states[i].AlertKey)) 132 } 133 } 134 135 details := &NotificationDetails{ 136 Ak: ak["alert_key"], 137 At: at.String(), 138 NotifyName: n.Name, 139 TemplateKey: tks.BodyTemplate, 140 } 141 142 n.prepareFromTemplateKeys(pn, *tks, render, actionDefaults, details) 143 return pn 144 }