bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/conf/unknownNotify.go (about) 1 package conf 2 3 import ( 4 "bytes" 5 "fmt" 6 "net/url" 7 "time" 8 9 "bosun.org/cmd/bosun/conf/template" 10 "bosun.org/models" 11 "bosun.org/slog" 12 ) 13 14 type unknownContext struct { 15 Time time.Time 16 Name string 17 Group models.AlertKeys 18 States *models.IncidentState 19 makeLink func(string, *url.Values) string 20 } 21 22 func (u *unknownContext) IncidentUnknownLink(i int64) string { 23 return u.makeLink("/incident", &url.Values{ 24 "id": []string{fmt.Sprint(i)}, 25 }) 26 } 27 28 var defaultUnknownTemplate = &Template{ 29 Body: template.Must(template.New("body").Parse(` 30 <p>Time: {{.Time}} 31 <p>Name: {{.Name}} 32 <p>Alerts: 33 {{range .Group}} 34 <br>{{.}} 35 {{end}} 36 `)), 37 Subject: template.Must(template.New("subject").Parse(`{{.Name}}: {{.Group | len}} unknown alerts`)), 38 } 39 40 var unknownDefaults defaultTemplates 41 42 func init() { 43 subject := `{{.Name}}: {{.Group | len}} unknown alerts` 44 body := ` 45 <p>Time: {{.Time}} 46 <p>Name: {{.Name}} 47 <p>Alerts: 48 {{range .Group}} 49 <br>{{.}} 50 {{end}} 51 ` 52 unknownDefaults.subject = template.Must(template.New("subject").Parse(subject)) 53 unknownDefaults.body = template.Must(template.New("body").Parse(body)) 54 } 55 56 func (n *Notification) PrepareUnknown(t *Template, c SystemConfProvider, name string, aks []models.AlertKey, states *models.IncidentState) *PreparedNotifications { 57 ctx := &unknownContext{ 58 Time: time.Now().UTC(), 59 Name: name, 60 Group: aks, 61 States: states, 62 makeLink: c.MakeLink, 63 } 64 pn := &PreparedNotifications{} 65 buf := &bytes.Buffer{} 66 render := func(key string, defaultTmpl *template.Template) (string, error) { 67 tpl := defaultTmpl 68 if key != "" { 69 tpl = t.Get(key) 70 } else { 71 key = "default" 72 } 73 buf.Reset() 74 err := tpl.Execute(buf, ctx) 75 if err != nil { 76 e := fmt.Sprintf("executing unknown template '%s': %s", key, err) 77 pn.Errors = append(pn.Errors, e) 78 slog.Errorf(e) 79 return "", err 80 } 81 return buf.String(), nil 82 } 83 84 tks := n.UnknownTemplateKeys 85 86 ak := map[string][]string{ 87 "alert_key": {}, 88 } 89 90 for i := range aks { 91 contain := func(key string, array []string) bool { 92 for _, value := range array { 93 if value == key { 94 return true 95 } 96 } 97 return false 98 } 99 if contain(fmt.Sprint(aks[i]), ak["alert_key"]) != true { 100 ak["alert_key"] = append(ak["alert_key"], fmt.Sprint(aks[i])) 101 } 102 } 103 104 details := &NotificationDetails{ 105 NotifyName: n.Name, 106 TemplateKey: tks.BodyTemplate, 107 Ak: ak["alert_key"], 108 NotifyType: 2, 109 } 110 111 n.prepareFromTemplateKeys(pn, tks, render, unknownDefaults, details) 112 return pn 113 } 114 115 func (n *Notification) NotifyUnknown(t *Template, c SystemConfProvider, name string, aks []models.AlertKey, states *models.IncidentState) { 116 go n.PrepareUnknown(t, c, name, aks, states).Send(c) 117 } 118 119 var unknownMultiDefaults defaultTemplates 120 121 type unknownMultiContext struct { 122 Time time.Time 123 Threshold int 124 Groups map[string]models.AlertKeys 125 States []*models.IncidentState 126 } 127 128 func init() { 129 subject := `{{.Groups | len}} unknown alert instances suppressed` 130 body := ` 131 <p>Threshold of {{ .Threshold }} reached for unknown notifications. The following unknown 132 group emails were not sent. 133 <ul> 134 {{ range $group, $alertKeys := .Groups }} 135 <li> 136 {{ $group }} 137 <ul> 138 {{ range $ak := $alertKeys }} 139 <li>{{ $ak }}</li> 140 {{ end }} 141 </ul> 142 </li> 143 {{ end }} 144 </ul> 145 ` 146 unknownMultiDefaults.subject = template.Must(template.New("subject").Parse(subject)) 147 unknownMultiDefaults.body = template.Must(template.New("body").Parse(body)) 148 } 149 150 func (n *Notification) PrepareMultipleUnknowns(t *Template, c SystemConfProvider, groups map[string]models.AlertKeys, states []*models.IncidentState) *PreparedNotifications { 151 ctx := &unknownMultiContext{ 152 Time: time.Now().UTC(), 153 Threshold: c.GetUnknownThreshold(), 154 Groups: groups, 155 States: states, 156 } 157 pn := &PreparedNotifications{} 158 buf := &bytes.Buffer{} 159 render := func(key string, defaultTmpl *template.Template) (string, error) { 160 tpl := defaultTmpl 161 if key != "" { 162 tpl = t.Get(key) 163 } else { 164 key = "default" 165 } 166 buf.Reset() 167 err := tpl.Execute(buf, ctx) 168 if err != nil { 169 e := fmt.Sprintf("executing unknown multi template '%s': %s", key, err) 170 pn.Errors = append(pn.Errors, e) 171 slog.Errorf(e) 172 return "", err 173 } 174 return buf.String(), nil 175 } 176 177 tks := n.UnknownMultiTemplateKeys 178 179 ak := []string{} 180 181 for _, v := range groups { 182 ak = append(ak, fmt.Sprint(v)) 183 } 184 185 details := &NotificationDetails{ 186 NotifyName: n.Name, 187 TemplateKey: tks.BodyTemplate, 188 Ak: ak, 189 NotifyType: 3, 190 } 191 192 n.prepareFromTemplateKeys(pn, tks, render, unknownMultiDefaults, details) 193 return pn 194 } 195 196 func (n *Notification) NotifyMultipleUnknowns(t *Template, c SystemConfProvider, groups map[string]models.AlertKeys, states []*models.IncidentState) { 197 n.PrepareMultipleUnknowns(t, c, groups, states).Send(c) 198 } 199 200 // code common to PrepareAction / PrepareUnknown / PrepareMultipleUnknowns 201 func (n *Notification) prepareFromTemplateKeys(pn *PreparedNotifications, tks NotificationTemplateKeys, render func(string, *template.Template) (string, error), defaults defaultTemplates, alertDetails *NotificationDetails) { 202 if len(n.Email) > 0 || n.Post != nil || tks.PostTemplate != "" { 203 body, _ := render(tks.BodyTemplate, defaults.body) 204 if subject, err := render(tks.EmailSubjectTemplate, defaults.subject); err == nil { 205 pn.Email = n.PrepEmail(subject, body, "", nil) 206 } 207 } 208 209 postURL, getURL := "", "" 210 if tks.PostTemplate != "" { 211 if p, err := render(tks.PostTemplate, nil); err == nil { 212 postURL = p 213 } 214 } else if n.Post != nil { 215 postURL = n.Post.String() 216 } 217 if tks.GetTemplate != "" { 218 if g, err := render(tks.GetTemplate, nil); err == nil { 219 getURL = g 220 } 221 } else if n.Get != nil { 222 getURL = n.Get.String() 223 } 224 if postURL != "" { 225 body, _ := render(tks.BodyTemplate, defaults.subject) 226 pn.HTTP = append(pn.HTTP, n.PrepHttp("POST", postURL, body, alertDetails)) 227 } 228 if getURL != "" { 229 pn.HTTP = append(pn.HTTP, n.PrepHttp("GET", getURL, "", alertDetails)) 230 } 231 }