Create and use custom template package

This commit is contained in:
Fabian Reinartz 2015-10-11 13:32:24 +02:00
parent 41821faf92
commit 3a2db95a8e
3 changed files with 100 additions and 49 deletions

29
main.go
View File

@ -17,13 +17,11 @@ import (
"database/sql"
"flag"
"fmt"
"html/template"
"net/http"
"os"
"os/signal"
"path/filepath"
"syscall"
txttemplate "text/template"
"github.com/prometheus/common/log"
"github.com/prometheus/common/route"
@ -32,6 +30,7 @@ import (
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/provider"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
)
@ -50,6 +49,8 @@ func main() {
}
defer db.Close()
tmpl := &template.Template{}
alerts, err := provider.NewSQLAlerts(db)
if err != nil {
log.Fatal(err)
@ -115,7 +116,7 @@ func main() {
for _, ec := range nc.EmailConfigs {
all = append(all, &notify.LogNotifier{
Log: log.With("notifier", "email"),
Notifier: notify.NewEmail(ec),
Notifier: notify.NewEmail(ec, tmpl),
})
}
@ -145,14 +146,13 @@ func main() {
}
routedNotifier.Lock()
log.Debugf("set notifiers for routes %#v", res)
routedNotifier.Notifiers = res
routedNotifier.Unlock()
}
disp := NewDispatcher(alerts, notifier)
if err := reloadConfig(*configFile, disp, types.ReloadFunc(build), inhibitor); err != nil {
if err := reloadConfig(*configFile, disp, types.ReloadFunc(build), inhibitor, tmpl); err != nil {
log.Fatalf("Couldn't load configuration (-config.file=%s): %v", *configFile, err)
}
@ -174,7 +174,7 @@ func main() {
go func() {
for range hup {
if err := reloadConfig(*configFile, disp, types.ReloadFunc(build), inhibitor); err != nil {
if err := reloadConfig(*configFile, disp, types.ReloadFunc(build), inhibitor, tmpl); err != nil {
log.Errorf("Couldn't load configuration (-config.file=%s): %v", *configFile, err)
}
}
@ -194,23 +194,6 @@ func reloadConfig(filename string, rls ...types.Reloadable) error {
return err
}
t := template.New("")
for _, tpath := range conf.Templates {
t, err = t.ParseGlob(tpath)
if err != nil {
return err
}
}
tt := txttemplate.New("")
for _, tpath := range conf.Templates {
tt, err = tt.ParseGlob(tpath)
if err != nil {
return err
}
}
notify.SetTemplate(t, tt)
for _, rl := range rls {
rl.ApplyConfig(conf)
}

View File

@ -5,13 +5,11 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"html/template"
"net"
"net/http"
"net/smtp"
"os"
"strings"
txttemplate "text/template"
"time"
"github.com/prometheus/common/log"
@ -20,6 +18,7 @@ import (
"golang.org/x/net/context/ctxhttp"
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
)
@ -68,10 +67,11 @@ func (w *Webhook) Notify(ctx context.Context, alerts ...*types.Alert) error {
type Email struct {
conf *config.EmailConfig
tmpl *template.Template
}
func NewEmail(ec *config.EmailConfig) *Email {
return &Email{conf: ec}
func NewEmail(c *config.EmailConfig, t *template.Template) *Email {
return &Email{conf: c, tmpl: t}
}
func (n *Email) auth(mechs string) (smtp.Auth, *tls.Config, error) {
@ -161,16 +161,17 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) error {
Date: time.Now().Format(time.RFC1123Z),
}
if err := txttmpl.ExecuteTemplate(wc, n.conf.Templates.Header, &data); err != nil {
if err := n.tmpl.ExecuteText(wc, n.conf.Templates.Header, &data); err != nil {
return err
}
// TODO(fabxc): do a multipart write that considers the plain template.
return tmpl.ExecuteTemplate(wc, n.conf.Templates.HTML, &data)
return n.tmpl.ExecuteHTML(wc, n.conf.Templates.HTML, &data)
}
type Slack struct {
conf *config.SlackConfig
tmpl *template.Template
}
// slackReq is the request for sending a slack notification.
@ -209,21 +210,25 @@ func (n *Slack) Notify(ctx context.Context, as ...*types.Alert) error {
color = n.conf.ColorFiring
}
var title, link, pretext, text, fallback bytes.Buffer
if err := tmpl.ExecuteTemplate(&title, n.conf.Templates.Title, alerts); err != nil {
return err
}
if err := tmpl.ExecuteTemplate(&text, n.conf.Templates.Text, alerts); err != nil {
return err
var err error
tmpl := func(name string) (s string) {
if err != nil {
return
}
s, err = n.tmpl.ExecuteHTMLString(name, struct {
Alerts model.Alerts
}{
Alerts: alerts,
})
return s
}
attachment := &slackAttachment{
Title: title.String(),
TitleLink: link.String(),
Pretext: pretext.String(),
Text: text.String(),
Fallback: fallback.String(),
Title: tmpl(n.conf.Templates.Title),
TitleLink: tmpl(n.conf.Templates.TitleLink),
Pretext: tmpl(n.conf.Templates.Pretext),
Text: tmpl(n.conf.Templates.Text),
Fallback: tmpl(n.conf.Templates.Fallback),
Fields: []slackAttachmentField{{
Title: "Status",
@ -233,6 +238,10 @@ func (n *Slack) Notify(ctx context.Context, as ...*types.Alert) error {
Color: color,
MrkdwnIn: []string{"fallback", "pretext"},
}
if err != nil {
return err
}
req := &slackReq{
Channel: n.conf.Channel,
Attachments: []slackAttachment{*attachment},
@ -256,11 +265,3 @@ func (n *Slack) Notify(ctx context.Context, as ...*types.Alert) error {
return nil
}
var tmpl *template.Template
var txttmpl *txttemplate.Template
func SetTemplate(t *template.Template, tt *txttemplate.Template) {
tmpl = t
txttmpl = tt
}

67
template/template.go Normal file
View File

@ -0,0 +1,67 @@
package template
import (
text_tmpl "html/template"
html_tmpl "text/template"
"bytes"
"io"
"strings"
"github.com/prometheus/alertmanager/config"
)
type Template struct {
text *text_tmpl.Template
html *html_tmpl.Template
}
type FuncMap map[string]interface{}
var DefaultFuncs = FuncMap{
"upper": strings.ToUpper,
"lower": strings.ToLower,
}
func (t *Template) funcs(fm FuncMap) *Template {
t.text.Funcs(text_tmpl.FuncMap(fm))
t.html.Funcs(html_tmpl.FuncMap(fm))
return t
}
func (t *Template) ExecuteTextString(name string, data interface{}) (string, error) {
var buf bytes.Buffer
err := t.ExecuteText(&buf, name, data)
return buf.String(), err
}
func (t *Template) ExecuteText(w io.Writer, name string, data interface{}) error {
return t.text.ExecuteTemplate(w, name, data)
}
func (t *Template) ExecuteHTMLString(name string, data interface{}) (string, error) {
var buf bytes.Buffer
err := t.ExecuteHTML(&buf, name, data)
return buf.String(), err
}
func (t *Template) ExecuteHTML(w io.Writer, name string, data interface{}) error {
return t.html.ExecuteTemplate(w, name, data)
}
func (t *Template) ApplyConfig(conf *config.Config) {
var (
tt = text_tmpl.New("")
ht = html_tmpl.New("")
)
for _, tf := range conf.Templates {
tt = text_tmpl.Must(tt.ParseGlob(tf))
ht = html_tmpl.Must(ht.ParseGlob(tf))
}
t.text = tt
t.html = ht
t.funcs(DefaultFuncs)
}