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  }