cherrypick notification changes from #450 (#745)

This commit is contained in:
Simon Aronsson 2021-01-06 20:06:56 +01:00 committed by GitHub
parent 3bbe1bd109
commit 35490c853d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 375 additions and 295 deletions

View file

@ -1,29 +1,22 @@
package notifications
import (
"encoding/base64"
"fmt"
"github.com/spf13/cobra"
"net/smtp"
"os"
"strings"
"time"
"github.com/spf13/cobra"
shoutrrrSmtp "github.com/containrrr/shoutrrr/pkg/services/smtp"
t "github.com/containrrr/watchtower/pkg/types"
log "github.com/sirupsen/logrus"
"strconv"
)
const (
emailType = "email"
)
// Implements Notifier, logrus.Hook
// The default logrus email integration would have several issues:
// - It would send one email per log output
// - It would only send errors
// We work around that by holding on to log entries until the update cycle is done.
type emailTypeNotifier struct {
url string
From, To string
Server, User, Password, SubjectTag string
Port int
@ -33,7 +26,12 @@ type emailTypeNotifier struct {
delay time.Duration
}
func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifier {
// NewEmailNotifier is a factory method creating a new email notifier instance
func NewEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertableNotifier {
return newEmailNotifier(c, acceptedLogLevels)
}
func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertableNotifier {
flags := c.PersistentFlags()
from, _ := flags.GetString("notification-email-from")
@ -47,6 +45,7 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifie
subjecttag, _ := flags.GetString("notification-email-subjecttag")
n := &emailTypeNotifier{
entries: []*log.Entry{},
From: from,
To: to,
Server: server,
@ -59,12 +58,33 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifie
SubjectTag: subjecttag,
}
log.AddHook(n)
return n
}
func (e *emailTypeNotifier) buildMessage(entries []*log.Entry) []byte {
func (e *emailTypeNotifier) GetURL() string {
conf := &shoutrrrSmtp.Config{
FromAddress: e.From,
FromName: "Watchtower",
ToAddresses: []string{e.To},
Port: uint16(e.Port),
Host: e.Server,
Subject: e.getSubject(),
Username: e.User,
Password: e.Password,
UseStartTLS: true,
UseHTML: false,
}
if len(e.User) > 0 {
conf.Set("auth", "Plain")
} else {
conf.Set("auth", "None")
}
return conf.GetURL().String()
}
func (e *emailTypeNotifier) getSubject() string {
var emailSubject string
if e.SubjectTag == "" {
@ -75,83 +95,13 @@ func (e *emailTypeNotifier) buildMessage(entries []*log.Entry) []byte {
if hostname, err := os.Hostname(); err == nil {
emailSubject += " on " + hostname
}
body := ""
for _, entry := range entries {
body += entry.Time.Format("2006-01-02 15:04:05") + " (" + entry.Level.String() + "): " + entry.Message + "\r\n"
// We don't use fields in watchtower, so don't bother sending them.
}
t := time.Now()
header := make(map[string]string)
header["From"] = e.From
header["To"] = e.To
header["Subject"] = emailSubject
header["Date"] = t.Format(time.RFC1123Z)
header["MIME-Version"] = "1.0"
header["Content-Type"] = "text/plain; charset=\"utf-8\""
header["Content-Transfer-Encoding"] = "base64"
message := ""
for k, v := range header {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
encodedBody := base64.StdEncoding.EncodeToString([]byte(body))
//RFC 2045 base64 encoding demands line no longer than 76 characters.
for _, line := range SplitSubN(encodedBody, 76) {
message += "\r\n" + line
}
return []byte(message)
return emailSubject
}
func (e *emailTypeNotifier) sendEntries(entries []*log.Entry) {
// Do the sending in a separate goroutine so we don't block the main process.
msg := e.buildMessage(entries)
go func() {
if e.delay > 0 {
time.Sleep(e.delay)
}
var auth smtp.Auth
if e.User != "" {
auth = smtp.PlainAuth("", e.User, e.Password, e.Server)
}
err := SendMail(e.Server+":"+strconv.Itoa(e.Port), e.tlsSkipVerify, auth, e.From, strings.Split(e.To, ","), msg)
if err != nil {
// Use fmt so it doesn't trigger another email.
fmt.Println("Failed to send notification email: ", err)
}
}()
}
func (e *emailTypeNotifier) StartNotification() {
if e.entries == nil {
e.entries = make([]*log.Entry, 0, 10)
}
}
func (e *emailTypeNotifier) SendNotification() {
if e.entries == nil || len(e.entries) <= 0 {
return
}
e.sendEntries(e.entries)
e.entries = nil
}
func (e *emailTypeNotifier) Levels() []log.Level {
return e.logLevels
}
func (e *emailTypeNotifier) Fire(entry *log.Entry) error {
if e.entries != nil {
e.entries = append(e.entries, entry)
} else {
e.sendEntries([]*log.Entry{entry})
}
return nil
}
// TODO: Delete these once all notifiers have been converted to shoutrrr
func (e *emailTypeNotifier) StartNotification() {}
func (e *emailTypeNotifier) SendNotification() {}
func (e *emailTypeNotifier) Levels() []log.Level { return nil }
func (e *emailTypeNotifier) Fire(entry *log.Entry) error { return nil }
func (e *emailTypeNotifier) Close() {}