Update Shoutrrr to v0.4 (#810)

This commit is contained in:
nils måsén 2021-03-13 08:58:11 +01:00 committed by GitHub
parent 60a6300f0e
commit 738215a1f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 244 additions and 166 deletions

View file

@ -1,11 +1,8 @@
package notifications
import (
"fmt"
"os"
"time"
"github.com/containrrr/shoutrrr/pkg/format"
"github.com/spf13/cobra"
shoutrrrSmtp "github.com/containrrr/shoutrrr/pkg/services/smtp"
@ -29,11 +26,11 @@ type emailTypeNotifier struct {
}
// NewEmailNotifier is a factory method creating a new email notifier instance
func NewEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertableNotifier {
func NewEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
return newEmailNotifier(c, acceptedLogLevels)
}
func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertableNotifier {
func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
flags := c.PersistentFlags()
from, _ := flags.GetString("notification-email-from")
@ -63,7 +60,7 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
return n
}
func (e *emailTypeNotifier) GetURL() string {
func (e *emailTypeNotifier) GetURL() (string, error) {
conf := &shoutrrrSmtp.Config{
FromAddress: e.From,
FromName: "Watchtower",
@ -73,43 +70,29 @@ func (e *emailTypeNotifier) GetURL() string {
Subject: e.getSubject(),
Username: e.User,
Password: e.Password,
UseStartTLS: true,
UseStartTLS: !e.tlsSkipVerify,
UseHTML: false,
Encryption: shoutrrrSmtp.EncMethods.Auto,
Auth: shoutrrrSmtp.AuthTypes.None,
}
pkr := format.NewPropKeyResolver(conf)
var err error
if len(e.User) > 0 {
err = pkr.Set("auth", "Plain")
} else {
err = pkr.Set("auth", "None")
conf.Auth = shoutrrrSmtp.AuthTypes.Plain
}
if err != nil {
fmt.Printf("Could not set auth type for email notifier: %v", err)
if e.tlsSkipVerify {
conf.Encryption = shoutrrrSmtp.EncMethods.None
}
return conf.GetURL().String()
return conf.GetURL().String(), nil
}
func (e *emailTypeNotifier) getSubject() string {
var emailSubject string
subject := GetTitle()
if e.SubjectTag == "" {
emailSubject = "Watchtower updates"
} else {
emailSubject = e.SubjectTag + " Watchtower updates"
if e.SubjectTag != "" {
subject = e.SubjectTag + " " + subject
}
if hostname, err := os.Hostname(); err == nil {
emailSubject += " on " + hostname
}
return emailSubject
return subject
}
// 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() {}

View file

@ -1,6 +1,7 @@
package notifications
import (
"net/url"
"strings"
shoutrrrGotify "github.com/containrrr/shoutrrr/pkg/services/gotify"
@ -22,20 +23,20 @@ type gotifyTypeNotifier struct {
}
// NewGotifyNotifier is a factory method creating a new gotify notifier instance
func NewGotifyNotifier(c *cobra.Command, levels []log.Level) t.ConvertableNotifier {
func NewGotifyNotifier(c *cobra.Command, levels []log.Level) t.ConvertibleNotifier {
return newGotifyNotifier(c, levels)
}
func newGotifyNotifier(c *cobra.Command, levels []log.Level) t.ConvertableNotifier {
func newGotifyNotifier(c *cobra.Command, levels []log.Level) t.ConvertibleNotifier {
flags := c.PersistentFlags()
url := getGotifyURL(flags)
apiURL := getGotifyURL(flags)
token := getGotifyToken(flags)
skipVerify, _ := flags.GetBool("notification-gotify-tls-skip-verify")
n := &gotifyTypeNotifier{
gotifyURL: url,
gotifyURL: apiURL,
gotifyAppToken: token,
gotifyInsecureSkipVerify: skipVerify,
logLevels: levels,
@ -66,26 +67,19 @@ func getGotifyURL(flags *pflag.FlagSet) string {
return gotifyURL
}
func (n *gotifyTypeNotifier) GetURL() string {
url := n.gotifyURL
if strings.HasPrefix(url, "https://") {
url = strings.TrimPrefix(url, "https://")
} else {
url = strings.TrimPrefix(url, "http://")
func (n *gotifyTypeNotifier) GetURL() (string, error) {
apiURL, err := url.Parse(n.gotifyURL)
if err != nil {
return "", err
}
url = strings.TrimSuffix(url, "/")
config := &shoutrrrGotify.Config{
Host: url,
Token: n.gotifyAppToken,
Host: apiURL.Host,
Path: apiURL.Path,
DisableTLS: apiURL.Scheme == "http",
Title: GetTitle(),
Token: n.gotifyAppToken,
}
return config.GetURL().String()
return config.GetURL().String(), nil
}
func (n *gotifyTypeNotifier) StartNotification() {}
func (n *gotifyTypeNotifier) SendNotification() {}
func (n *gotifyTypeNotifier) Close() {}
func (n *gotifyTypeNotifier) Levels() []log.Level { return nil }

View file

@ -1,12 +1,11 @@
package notifications
import (
"strings"
shoutrrrTeams "github.com/containrrr/shoutrrr/pkg/services/teams"
t "github.com/containrrr/watchtower/pkg/types"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"net/url"
)
const (
@ -20,11 +19,11 @@ type msTeamsTypeNotifier struct {
}
// NewMsTeamsNotifier is a factory method creating a new teams notifier instance
func NewMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.ConvertableNotifier {
func NewMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
return newMsTeamsNotifier(cmd, acceptedLogLevels)
}
func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.ConvertableNotifier {
func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
flags := cmd.PersistentFlags()
@ -43,26 +42,19 @@ func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.Con
return n
}
func (n *msTeamsTypeNotifier) GetURL() string {
baseURL := "https://outlook.office.com/webhook/"
path := strings.Replace(n.webHookURL, baseURL, "", 1)
rawToken := strings.Replace(path, "/IncomingWebhook", "", 1)
token := strings.Split(rawToken, "/")
config := &shoutrrrTeams.Config{
Token: shoutrrrTeams.Token{
A: token[0],
B: token[1],
C: token[2],
},
func (n *msTeamsTypeNotifier) GetURL() (string, error) {
webhookURL, err := url.Parse(n.webHookURL)
if err != nil {
return "", err
}
return config.GetURL().String()
}
config, err := shoutrrrTeams.ConfigFromWebhookURL(*webhookURL)
if err != nil {
return "", err
}
func (n *msTeamsTypeNotifier) StartNotification() {}
func (n *msTeamsTypeNotifier) SendNotification() {}
func (n *msTeamsTypeNotifier) Close() {}
func (n *msTeamsTypeNotifier) Levels() []log.Level { return nil }
func (n *msTeamsTypeNotifier) Fire(entry *log.Entry) error { return nil }
config.Color = ColorHex
config.Title = GetTitle()
return config.GetURL().String(), nil
}

View file

@ -5,6 +5,7 @@ import (
"github.com/johntdyer/slackrus"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"os"
)
// Notifier can send log output as notification to admins, with optional batching.
@ -36,13 +37,13 @@ func NewNotifier(c *cobra.Command) *Notifier {
log.WithField("could not read notifications argument", log.Fields{"Error": err}).Fatal()
}
n.types = n.GetNotificationTypes(c, acceptedLogLevels, types)
n.types = n.getNotificationTypes(c, acceptedLogLevels, types)
return n
}
// GetNotificationTypes produces an array of notifiers from a list of types
func (n *Notifier) GetNotificationTypes(cmd *cobra.Command, levels []log.Level, types []string) []ty.Notifier {
// getNotificationTypes produces an array of notifiers from a list of types
func (n *Notifier) getNotificationTypes(cmd *cobra.Command, levels []log.Level, types []string) []ty.Notifier {
output := make([]ty.Notifier, 0)
for _, t := range types {
@ -52,7 +53,8 @@ func (n *Notifier) GetNotificationTypes(cmd *cobra.Command, levels []log.Level,
continue
}
var legacyNotifier ty.ConvertableNotifier
var legacyNotifier ty.ConvertibleNotifier
var err error
switch t {
case emailType:
@ -65,11 +67,20 @@ func (n *Notifier) GetNotificationTypes(cmd *cobra.Command, levels []log.Level,
legacyNotifier = newGotifyNotifier(cmd, []log.Level{})
default:
log.Fatalf("Unknown notification type %q", t)
// Not really needed, used for nil checking static analysis
continue
}
shoutrrrURL, err := legacyNotifier.GetURL()
if err != nil {
log.Fatal("failed to create notification config:", err)
}
println(shoutrrrURL)
notifier := newShoutrrrNotifierFromURL(
cmd,
legacyNotifier.GetURL(),
shoutrrrURL,
levels,
)
@ -99,3 +110,20 @@ func (n *Notifier) Close() {
t.Close()
}
}
// GetTitle returns a common notification title with hostname appended
func GetTitle() (title string) {
title = "Watchtower updates"
if hostname, err := os.Hostname(); err == nil {
title += " on " + hostname
}
return
}
// ColorHex is the default notification color used for services that support it (formatted as a CSS hex string)
const ColorHex = "#406170"
// ColorInt is the default notification color used for services that support it (as an int value)
const ColorInt = 0x406170

View file

@ -2,6 +2,7 @@ package notifications_test
import (
"fmt"
"net/url"
"os"
"testing"
@ -28,7 +29,9 @@ var _ = Describe("notifications", func() {
When("passing a discord url to the slack notifier", func() {
channel := "123456789"
token := "abvsihdbau"
expected := fmt.Sprintf("discord://%s@%s", token, channel)
color := notifications.ColorInt
title := url.QueryEscape(notifications.GetTitle())
expected := fmt.Sprintf("discord://%s@%s?avatar=&color=0x%x&colordebug=0x0&colorerror=0x0&colorinfo=0x0&colorwarn=0x0&splitlines=Yes&title=%s&username=watchtower", token, channel, color, title)
buildArgs := func(url string) []string {
return []string{
"--notifications",
@ -55,9 +58,11 @@ var _ = Describe("notifications", func() {
tokenA := "aaa"
tokenB := "bbb"
tokenC := "ccc"
color := url.QueryEscape(notifications.ColorHex)
title := url.QueryEscape(notifications.GetTitle())
hookURL := fmt.Sprintf("https://hooks.slack.com/services/%s/%s/%s", tokenA, tokenB, tokenC)
expectedOutput := fmt.Sprintf("slack://%s@%s/%s/%s", username, tokenA, tokenB, tokenC)
expectedOutput := fmt.Sprintf("slack://%s@%s/%s/%s?color=%s&title=%s", username, tokenA, tokenB, tokenC, color, title)
args := []string{
"--notification-slack-hook-url",
@ -78,8 +83,9 @@ var _ = Describe("notifications", func() {
It("should return the expected URL", func() {
token := "aaa"
host := "shoutrrr.local"
title := url.QueryEscape(notifications.GetTitle())
expectedOutput := fmt.Sprintf("gotify://%s/%s", host, token)
expectedOutput := fmt.Sprintf("gotify://%s/%s?disabletls=No&priority=0&title=%s", host, token, title)
args := []string{
"--notification-gotify-url",
@ -99,12 +105,14 @@ var _ = Describe("notifications", func() {
It("should return the expected URL", func() {
tokenA := "aaa"
tokenB := "bbb"
tokenC := "ccc"
tokenA := "11111111-4444-4444-8444-cccccccccccc@22222222-4444-4444-8444-cccccccccccc"
tokenB := "33333333012222222222333333333344"
tokenC := "44444444-4444-4444-8444-cccccccccccc"
color := url.QueryEscape(notifications.ColorHex)
title := url.QueryEscape(notifications.GetTitle())
hookURL := fmt.Sprintf("https://outlook.office.com/webhook/%s/IncomingWebhook/%s/%s", tokenA, tokenB, tokenC)
expectedOutput := fmt.Sprintf("teams://%s:%s@%s", tokenA, tokenB, tokenC)
expectedOutput := fmt.Sprintf("teams://%s/%s/%s?color=%s&host=outlook.office.com&title=%s", tokenA, tokenB, tokenC, color, title)
args := []string{
"--notification-msteams-hook",
@ -156,20 +164,30 @@ func buildExpectedURL(username string, password string, host string, port int, f
subject := fmt.Sprintf("Watchtower updates on %s", hostname)
var template = "smtp://%s:%s@%s:%d/?auth=%s&encryption=None&fromaddress=%s&fromname=Watchtower&starttls=Yes&subject=%s&toaddresses=%s&usehtml=No"
return fmt.Sprintf(template, username, password, host, port, auth, from, subject, to)
var template = "smtp://%s:%s@%s:%d/?auth=%s&encryption=Auto&fromaddress=%s&fromname=Watchtower&starttls=Yes&subject=%s&toaddresses=%s&usehtml=No"
return fmt.Sprintf(template,
url.QueryEscape(username),
url.QueryEscape(password),
host, port, auth,
url.QueryEscape(from),
url.QueryEscape(subject),
url.QueryEscape(to))
}
type builderFn = func(c *cobra.Command, acceptedLogLevels []log.Level) types.ConvertableNotifier
type builderFn = func(c *cobra.Command, acceptedLogLevels []log.Level) types.ConvertibleNotifier
func testURL(builder builderFn, args []string, expectedURL string) {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
command.ParseFlags(args)
err := command.ParseFlags(args)
Expect(err).NotTo(HaveOccurred())
notifier := builder(command, []log.Level{})
actualURL := notifier.GetURL()
actualURL, err := notifier.GetURL()
Expect(err).NotTo(HaveOccurred())
Expect(actualURL).To(Equal(expectedURL))
}

View file

@ -20,11 +20,11 @@ type slackTypeNotifier struct {
}
// NewSlackNotifier is a factory function used to generate new instance of the slack notifier type
func NewSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertableNotifier {
func NewSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
return newSlackNotifier(c, acceptedLogLevels)
}
func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertableNotifier {
func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
flags := c.PersistentFlags()
hookURL, _ := flags.GetString("notification-slack-hook-url")
@ -46,7 +46,7 @@ func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
return n
}
func (s *slackTypeNotifier) GetURL() string {
func (s *slackTypeNotifier) GetURL() (string, error) {
trimmedURL := strings.TrimRight(s.HookURL, "/")
trimmedURL = strings.TrimLeft(trimmedURL, "https://")
parts := strings.Split(trimmedURL, "/")
@ -54,10 +54,14 @@ func (s *slackTypeNotifier) GetURL() string {
if parts[0] == "discord.com" || parts[0] == "discordapp.com" {
log.Debug("Detected a discord slack wrapper URL, using shoutrrr discord service")
conf := &shoutrrrDisco.Config{
Channel: parts[len(parts)-3],
Token: parts[len(parts)-2],
Channel: parts[len(parts)-3],
Token: parts[len(parts)-2],
Color: ColorInt,
Title: GetTitle(),
SplitLines: true,
Username: s.Username,
}
return conf.GetURL().String()
return conf.GetURL().String(), nil
}
rawTokens := strings.Replace(s.HookURL, "https://hooks.slack.com/services/", "", 1)
@ -66,14 +70,9 @@ func (s *slackTypeNotifier) GetURL() string {
conf := &shoutrrrSlack.Config{
BotName: s.Username,
Token: tokens,
Color: ColorHex,
Title: GetTitle(),
}
return conf.GetURL().String()
return conf.GetURL().String(), nil
}
func (s *slackTypeNotifier) StartNotification() {
}
func (s *slackTypeNotifier) SendNotification() {}
func (s *slackTypeNotifier) Close() {}

View file

@ -1,7 +0,0 @@
package types
// ConvertableNotifier is a notifier capable of creating a shoutrrr URL
type ConvertableNotifier interface {
Notifier
GetURL() string
}

View file

@ -0,0 +1,6 @@
package types
// ConvertibleNotifier is a notifier capable of creating a shoutrrr URL
type ConvertibleNotifier interface {
GetURL() (string, error)
}