mirror of
https://github.com/containrrr/watchtower.git
synced 2025-12-14 06:06:38 +01:00
Update Shoutrrr to v0.4 (#810)
This commit is contained in:
parent
60a6300f0e
commit
738215a1f7
10 changed files with 244 additions and 166 deletions
|
|
@ -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() {}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue