Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions pkg/notifications/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
return n
}

func (e *emailTypeNotifier) GetURL(c *cobra.Command) (string, error) {
func (e *emailTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
conf := &shoutrrrSmtp.Config{
FromAddress: e.From,
FromName: "Watchtower",
ToAddresses: []string{e.To},
Port: uint16(e.Port),
Host: e.Server,
Subject: e.getSubject(c),
Subject: e.getSubject(c, title),
Username: e.User,
Password: e.Password,
UseStartTLS: !e.tlsSkipVerify,
Expand All @@ -86,12 +86,10 @@ func (e *emailTypeNotifier) GetDelay() time.Duration {
return e.delay
}

func (e *emailTypeNotifier) getSubject(c *cobra.Command) string {
subject := GetTitle(c)

func (e *emailTypeNotifier) getSubject(_ *cobra.Command, title string) string {
if e.SubjectTag != "" {
subject = e.SubjectTag + " " + subject
return e.SubjectTag + " " + title
}

return subject
return title
}
4 changes: 2 additions & 2 deletions pkg/notifications/gotify.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func getGotifyURL(flags *pflag.FlagSet) string {
return gotifyURL
}

func (n *gotifyTypeNotifier) GetURL(c *cobra.Command) (string, error) {
func (n *gotifyTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
apiURL, err := url.Parse(n.gotifyURL)
if err != nil {
return "", err
Expand All @@ -72,7 +72,7 @@ func (n *gotifyTypeNotifier) GetURL(c *cobra.Command) (string, error) {
Host: apiURL.Host,
Path: apiURL.Path,
DisableTLS: apiURL.Scheme == "http",
Title: GetTitle(c),
Title: title,
Token: n.gotifyAppToken,
}

Expand Down
7 changes: 4 additions & 3 deletions pkg/notifications/msteams.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package notifications

import (
"net/url"

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 (
Expand Down Expand Up @@ -37,7 +38,7 @@ func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.Con
return n
}

func (n *msTeamsTypeNotifier) GetURL(c *cobra.Command) (string, error) {
func (n *msTeamsTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
webhookURL, err := url.Parse(n.webHookURL)
if err != nil {
return "", err
Expand All @@ -49,7 +50,7 @@ func (n *msTeamsTypeNotifier) GetURL(c *cobra.Command) (string, error) {
}

config.Color = ColorHex
config.Title = GetTitle(c)
config.Title = title

return config.GetURL().String(), nil
}
29 changes: 18 additions & 11 deletions pkg/notifications/notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ func NewNotifier(c *cobra.Command) ty.Notifier {
tplString, _ := f.GetString("notification-template")
urls, _ := f.GetStringArray("notification-url")

urls, delay := AppendLegacyUrls(urls, c)
hostname := GetHostname(c)
urls, delay := AppendLegacyUrls(urls, c, GetTitle(hostname))

title := GetTitle(c)
return newShoutrrrNotifier(tplString, acceptedLogLevels, !reportTemplate, title, delay, urls...)
return newShoutrrrNotifier(tplString, acceptedLogLevels, !reportTemplate, hostname, delay, urls...)
}

// AppendLegacyUrls creates shoutrrr equivalent URLs from legacy notification flags
func AppendLegacyUrls(urls []string, cmd *cobra.Command) ([]string, time.Duration) {
func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string, time.Duration) {

// Parse types and create notifiers.
types, err := cmd.Flags().GetStringSlice("notifications")
Expand Down Expand Up @@ -69,7 +69,7 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command) ([]string, time.Duratio
continue
}

shoutrrrURL, err := legacyNotifier.GetURL(cmd)
shoutrrrURL, err := legacyNotifier.GetURL(cmd, title)
if err != nil {
log.Fatal("failed to create notification config: ", err)
}
Expand All @@ -85,20 +85,27 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command) ([]string, time.Duratio
}

// GetTitle returns a common notification title with hostname appended
func GetTitle(c *cobra.Command) (title string) {
title = "Watchtower updates"
func GetTitle(hostname string) string {
title := "Watchtower updates"
if hostname != "" {
title += " on " + hostname
}
return title
}

f := c.PersistentFlags()
// GetHostname returns the hostname as set by args or resolved from OS
func GetHostname(c *cobra.Command) string {

f := c.PersistentFlags()
hostname, _ := f.GetString("notifications-hostname")

if hostname != "" {
title += " on " + hostname
return hostname
} else if hostname, err := os.Hostname(); err == nil {
title += " on " + hostname
return hostname
}

return
return ""
}

// ColorHex is the default notification color used for services that support it (formatted as a CSS hex string)
Expand Down
37 changes: 32 additions & 5 deletions pkg/notifications/notifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,27 @@ var _ = Describe("notifications", func() {

Expect(notif.GetNames()).To(BeEmpty())
})
When("title is overriden in flag", func() {
It("should use the specified hostname in the title", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)

err := command.ParseFlags([]string{
"--notifications-hostname",
"test.host",
})
Expect(err).NotTo(HaveOccurred())
hostname := notifications.GetHostname(command)
title := notifications.GetTitle(hostname)
Expect(title).To(Equal("Watchtower updates on test.host"))
})
})
When("no hostname can be resolved", func() {
It("should use the default simple title", func() {
title := notifications.GetTitle("")
Expect(title).To(Equal("Watchtower updates"))
})
})
})
Describe("the slack notifier", func() {
// builderFn := notifications.NewSlackNotifier
Expand All @@ -39,7 +60,8 @@ var _ = Describe("notifications", func() {
channel := "123456789"
token := "abvsihdbau"
color := notifications.ColorInt
title := url.QueryEscape(notifications.GetTitle(command))
hostname := notifications.GetHostname(command)
title := url.QueryEscape(notifications.GetTitle(hostname))
expected := fmt.Sprintf("discord://%s@%s?color=0x%x&colordebug=0x0&colorerror=0x0&colorinfo=0x0&colorwarn=0x0&title=%s&username=watchtower", token, channel, color, title)
buildArgs := func(url string) []string {
return []string{
Expand Down Expand Up @@ -67,7 +89,8 @@ var _ = Describe("notifications", func() {
tokenB := "BBBBBBBBB"
tokenC := "123456789123456789123456"
color := url.QueryEscape(notifications.ColorHex)
title := url.QueryEscape(notifications.GetTitle(command))
hostname := notifications.GetHostname(command)
title := url.QueryEscape(notifications.GetTitle(hostname))
iconURL := "https://containrrr.dev/watchtower-sq180.png"
iconEmoji := "whale"

Expand Down Expand Up @@ -122,7 +145,8 @@ var _ = Describe("notifications", func() {

token := "aaa"
host := "shoutrrr.local"
title := url.QueryEscape(notifications.GetTitle(command))
hostname := notifications.GetHostname(command)
title := url.QueryEscape(notifications.GetTitle(hostname))

expectedOutput := fmt.Sprintf("gotify://%s/%s?title=%s", host, token, title)

Expand Down Expand Up @@ -150,7 +174,8 @@ var _ = Describe("notifications", func() {
tokenB := "33333333012222222222333333333344"
tokenC := "44444444-4444-4444-8444-cccccccccccc"
color := url.QueryEscape(notifications.ColorHex)
title := url.QueryEscape(notifications.GetTitle(command))
hostname := notifications.GetHostname(command)
title := url.QueryEscape(notifications.GetTitle(hostname))

hookURL := fmt.Sprintf("https://outlook.office.com/webhook/%s/IncomingWebhook/%s/%s", tokenA, tokenB, tokenC)
expectedOutput := fmt.Sprintf("teams://%s/%s/%s?color=%s&title=%s", tokenA, tokenB, tokenC, color, title)
Expand Down Expand Up @@ -241,7 +266,9 @@ func testURL(args []string, expectedURL string) {
err := command.ParseFlags(args)
Expect(err).NotTo(HaveOccurred())

urls, _ := notifications.AppendLegacyUrls([]string{}, command)
hostname := notifications.GetHostname(command)
title := notifications.GetTitle(hostname)
urls, _ := notifications.AppendLegacyUrls([]string{}, command, title)

Expect(err).NotTo(HaveOccurred())

Expand Down
12 changes: 9 additions & 3 deletions pkg/notifications/shoutrrr.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type shoutrrrTypeNotifier struct {
done chan bool
legacyTemplate bool
params *types.Params
hostname string
}

// GetScheme returns the scheme part of a Shoutrrr URL
Expand All @@ -78,10 +79,11 @@ func (n *shoutrrrTypeNotifier) GetNames() []string {
return names
}

func newShoutrrrNotifier(tplString string, acceptedLogLevels []log.Level, legacy bool, title string, delay time.Duration, urls ...string) t.Notifier {
func newShoutrrrNotifier(tplString string, acceptedLogLevels []log.Level, legacy bool, hostname string, delay time.Duration, urls ...string) t.Notifier {

notifier := createNotifier(urls, acceptedLogLevels, tplString, legacy)
notifier.params = &types.Params{"title": title}
notifier.hostname = hostname
notifier.params = &types.Params{"title": GetTitle(hostname)}
log.AddHook(notifier)

// Do the sending in a separate goroutine so we don't block the main process.
Expand Down Expand Up @@ -147,7 +149,9 @@ func (n *shoutrrrTypeNotifier) buildMessage(data Data) (string, error) {
}

func (n *shoutrrrTypeNotifier) sendEntries(entries []*log.Entry, report t.Report) {
msg, err := n.buildMessage(Data{entries, report})
title, _ := n.params.Title()
host := n.hostname
msg, err := n.buildMessage(Data{entries, report, title, host})

if msg == "" {
// Log in go func in case we entered from Fire to avoid stalling
Expand Down Expand Up @@ -240,4 +244,6 @@ func getShoutrrrTemplate(tplString string, legacy bool) (tpl *template.Template,
type Data struct {
Entries []*log.Entry
Report t.Report
Title string
Host string
}
20 changes: 20 additions & 0 deletions pkg/notifications/shoutrrr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ var mockDataAllFresh = Data{
}

func mockDataFromStates(states ...s.State) Data {
hostname := "Mock"
return Data{
Entries: legacyMockData.Entries,
Report: mocks.CreateMockProgressReport(states...),
Title: GetTitle(hostname),
Host: hostname,
}
}

Expand Down Expand Up @@ -177,6 +180,22 @@ var _ = Describe("Shoutrrr", func() {

})

When("using a template referencing Title", func() {
It("should contain the title in the output", func() {
expected := `Watchtower updates on Mock`
data := mockDataFromStates(s.UpdatedState)
Expect(getTemplatedResult(`{{ .Title }}`, false, data)).To(Equal(expected))
})
})

When("using a template referencing Host", func() {
It("should contain the hostname in the output", func() {
expected := `Mock`
data := mockDataFromStates(s.UpdatedState)
Expect(getTemplatedResult(`{{ .Host }}`, false, data)).To(Equal(expected))
})
})

Describe("the default template", func() {
When("all containers are fresh", func() {
It("should return an empty string", func() {
Expand Down Expand Up @@ -278,6 +297,7 @@ func sendNotificationsWithBlockingRouter(legacy bool) (*shoutrrrTypeNotifier, *b
done: make(chan bool),
Router: router,
legacyTemplate: legacy,
params: &types.Params{},
}

entry := &logrus.Entry{
Expand Down
6 changes: 3 additions & 3 deletions pkg/notifications/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
return n
}

func (s *slackTypeNotifier) GetURL(c *cobra.Command) (string, error) {
func (s *slackTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
trimmedURL := strings.TrimRight(s.HookURL, "/")
trimmedURL = strings.TrimLeft(trimmedURL, "https://")
parts := strings.Split(trimmedURL, "/")
Expand All @@ -52,7 +52,7 @@ func (s *slackTypeNotifier) GetURL(c *cobra.Command) (string, error) {
WebhookID: parts[len(parts)-3],
Token: parts[len(parts)-2],
Color: ColorInt,
Title: GetTitle(c),
Title: title,
SplitLines: true,
Username: s.Username,
}
Expand All @@ -65,7 +65,7 @@ func (s *slackTypeNotifier) GetURL(c *cobra.Command) (string, error) {
BotName: s.Username,
Color: ColorHex,
Channel: "webhook",
Title: GetTitle(c),
Title: title,
}

if s.IconURL != "" {
Expand Down
2 changes: 1 addition & 1 deletion pkg/types/convertible_notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

// ConvertibleNotifier is a notifier capable of creating a shoutrrr URL
type ConvertibleNotifier interface {
GetURL(c *cobra.Command) (string, error)
GetURL(c *cobra.Command, title string) (string, error)
}

// DelayNotifier is a notifier that might need to be delayed before sending notifications
Expand Down