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  }