diff --git a/docs/notifications.md b/docs/notifications.md index b0e2adf01..f78386669 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -25,6 +25,7 @@ comma-separated list of values to the `--notifications` option ## Settings - `--notifications-level` (env. `WATCHTOWER_NOTIFICATIONS_LEVEL`): Controls the log level which is used for the notifications. If omitted, the default log level is `info`. Possible values are: `panic`, `fatal`, `error`, `warn`, `info`, `debug` or `trace`. +- `--notifications-hostname` (env. `WATCHTOWER_NOTIFICATIONS_HOSTNAME`): Custom hostname specified in subject/title. Useful to override the operating system hostname. - Watchtower will post a notification every time it is started. This behavior [can be changed](https://containrrr.github.io/watchtower/arguments/#without_sending_a_startup_message) with an argument. ## Available services diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 177073df1..9df42c1c6 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -185,6 +185,12 @@ func RegisterNotificationFlags(rootCmd *cobra.Command) { viper.GetString("WATCHTOWER_NOTIFICATIONS_LEVEL"), "The log level used for sending notifications. Possible values: panic, fatal, error, warn, info or debug") + flags.StringP( + "notifications-hostname", + "", + viper.GetString("WATCHTOWER_NOTIFICATIONS_HOSTNAME"), + "Custom hostname for notification titles") + flags.StringP( "notification-email-from", "", diff --git a/pkg/notifications/email.go b/pkg/notifications/email.go index 4984139b1..e26ca97d4 100644 --- a/pkg/notifications/email.go +++ b/pkg/notifications/email.go @@ -60,14 +60,14 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert return n } -func (e *emailTypeNotifier) GetURL() (string, error) { +func (e *emailTypeNotifier) GetURL(c *cobra.Command) (string, error) { conf := &shoutrrrSmtp.Config{ FromAddress: e.From, FromName: "Watchtower", ToAddresses: []string{e.To}, Port: uint16(e.Port), Host: e.Server, - Subject: e.getSubject(), + Subject: e.getSubject(c), Username: e.User, Password: e.Password, UseStartTLS: !e.tlsSkipVerify, @@ -87,8 +87,8 @@ func (e *emailTypeNotifier) GetURL() (string, error) { return conf.GetURL().String(), nil } -func (e *emailTypeNotifier) getSubject() string { - subject := GetTitle() +func (e *emailTypeNotifier) getSubject(c *cobra.Command) string { + subject := GetTitle(c) if e.SubjectTag != "" { subject = e.SubjectTag + " " + subject diff --git a/pkg/notifications/gotify.go b/pkg/notifications/gotify.go index 7a6009bd3..85f59b1c0 100644 --- a/pkg/notifications/gotify.go +++ b/pkg/notifications/gotify.go @@ -67,7 +67,7 @@ func getGotifyURL(flags *pflag.FlagSet) string { return gotifyURL } -func (n *gotifyTypeNotifier) GetURL() (string, error) { +func (n *gotifyTypeNotifier) GetURL(c *cobra.Command) (string, error) { apiURL, err := url.Parse(n.gotifyURL) if err != nil { return "", err @@ -77,7 +77,7 @@ func (n *gotifyTypeNotifier) GetURL() (string, error) { Host: apiURL.Host, Path: apiURL.Path, DisableTLS: apiURL.Scheme == "http", - Title: GetTitle(), + Title: GetTitle(c), Token: n.gotifyAppToken, } diff --git a/pkg/notifications/msteams.go b/pkg/notifications/msteams.go index 6c4722943..282ce05ba 100644 --- a/pkg/notifications/msteams.go +++ b/pkg/notifications/msteams.go @@ -42,7 +42,7 @@ func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.Con return n } -func (n *msTeamsTypeNotifier) GetURL() (string, error) { +func (n *msTeamsTypeNotifier) GetURL(c *cobra.Command) (string, error) { webhookURL, err := url.Parse(n.webHookURL) if err != nil { return "", err @@ -54,7 +54,7 @@ func (n *msTeamsTypeNotifier) GetURL() (string, error) { } config.Color = ColorHex - config.Title = GetTitle() + config.Title = GetTitle(c) return config.GetURL().String(), nil } diff --git a/pkg/notifications/notifier.go b/pkg/notifications/notifier.go index c4e962fa2..358c5f33e 100644 --- a/pkg/notifications/notifier.go +++ b/pkg/notifications/notifier.go @@ -98,7 +98,7 @@ func (n *Notifier) getNotificationTypes(cmd *cobra.Command, levels []log.Level, continue } - shoutrrrURL, err := legacyNotifier.GetURL() + shoutrrrURL, err := legacyNotifier.GetURL(cmd) if err != nil { log.Fatal("failed to create notification config:", err) } @@ -139,10 +139,16 @@ func (n *Notifier) Close() { } // GetTitle returns a common notification title with hostname appended -func GetTitle() (title string) { +func GetTitle(c *cobra.Command) (title string) { title = "Watchtower updates" - if hostname, err := os.Hostname(); err == nil { + f := c.PersistentFlags() + + hostname, _ := f.GetString("notifications-hostname") + + if hostname != "" { + title += " on " + hostname + } else if hostname, err := os.Hostname(); err == nil { title += " on " + hostname } diff --git a/pkg/notifications/notifier_test.go b/pkg/notifications/notifier_test.go index ba6657ad6..f95ecbc2e 100644 --- a/pkg/notifications/notifier_test.go +++ b/pkg/notifications/notifier_test.go @@ -43,10 +43,13 @@ var _ = Describe("notifications", func() { builderFn := notifications.NewSlackNotifier When("passing a discord url to the slack notifier", func() { + command := cmd.NewRootCommand() + flags.RegisterNotificationFlags(command) + channel := "123456789" token := "abvsihdbau" color := notifications.ColorInt - title := url.QueryEscape(notifications.GetTitle()) + title := url.QueryEscape(notifications.GetTitle(command)) expected := fmt.Sprintf("discord://%s@%s?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{ @@ -69,13 +72,15 @@ var _ = Describe("notifications", func() { When("converting a slack service config into a shoutrrr url", func() { It("should return the expected URL", func() { + command := cmd.NewRootCommand() + flags.RegisterNotificationFlags(command) username := "containrrrbot" tokenA := "aaa" tokenB := "bbb" tokenC := "ccc" color := url.QueryEscape(notifications.ColorHex) - title := url.QueryEscape(notifications.GetTitle()) + title := url.QueryEscape(notifications.GetTitle(command)) hookURL := fmt.Sprintf("https://hooks.slack.com/services/%s/%s/%s", tokenA, tokenB, tokenC) expectedOutput := fmt.Sprintf("slack://%s@%s/%s/%s?color=%s&title=%s", username, tokenA, tokenB, tokenC, color, title) @@ -97,9 +102,12 @@ var _ = Describe("notifications", func() { builderFn := notifications.NewGotifyNotifier It("should return the expected URL", func() { + command := cmd.NewRootCommand() + flags.RegisterNotificationFlags(command) + token := "aaa" host := "shoutrrr.local" - title := url.QueryEscape(notifications.GetTitle()) + title := url.QueryEscape(notifications.GetTitle(command)) expectedOutput := fmt.Sprintf("gotify://%s/%s?title=%s", host, token, title) @@ -120,12 +128,14 @@ var _ = Describe("notifications", func() { builderFn := notifications.NewMsTeamsNotifier It("should return the expected URL", func() { + command := cmd.NewRootCommand() + flags.RegisterNotificationFlags(command) 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()) + title := url.QueryEscape(notifications.GetTitle(command)) 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) @@ -215,7 +225,7 @@ func testURL(builder builderFn, args []string, expectedURL string) { Expect(err).NotTo(HaveOccurred()) notifier := builder(command, []log.Level{}) - actualURL, err := notifier.GetURL() + actualURL, err := notifier.GetURL(command) Expect(err).NotTo(HaveOccurred()) diff --git a/pkg/notifications/slack.go b/pkg/notifications/slack.go index b3df119d4..63cb44ce2 100644 --- a/pkg/notifications/slack.go +++ b/pkg/notifications/slack.go @@ -46,7 +46,7 @@ func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert return n } -func (s *slackTypeNotifier) GetURL() (string, error) { +func (s *slackTypeNotifier) GetURL(c *cobra.Command) (string, error) { trimmedURL := strings.TrimRight(s.HookURL, "/") trimmedURL = strings.TrimLeft(trimmedURL, "https://") parts := strings.Split(trimmedURL, "/") @@ -57,7 +57,7 @@ func (s *slackTypeNotifier) GetURL() (string, error) { Channel: parts[len(parts)-3], Token: parts[len(parts)-2], Color: ColorInt, - Title: GetTitle(), + Title: GetTitle(c), SplitLines: true, Username: s.Username, } @@ -71,7 +71,7 @@ func (s *slackTypeNotifier) GetURL() (string, error) { BotName: s.Username, Token: tokens, Color: ColorHex, - Title: GetTitle(), + Title: GetTitle(c), } return conf.GetURL().String(), nil diff --git a/pkg/types/convertible_notifier.go b/pkg/types/convertible_notifier.go index 2614d1213..87f8659cb 100644 --- a/pkg/types/convertible_notifier.go +++ b/pkg/types/convertible_notifier.go @@ -1,6 +1,8 @@ package types +import "github.com/spf13/cobra" + // ConvertibleNotifier is a notifier capable of creating a shoutrrr URL type ConvertibleNotifier interface { - GetURL() (string, error) + GetURL(c *cobra.Command) (string, error) }