mirror of
https://github.com/containrrr/watchtower.git
synced 2025-12-13 21:56:38 +01:00
feat: allow hostname override for notifiers (#994)
* feat: allow hostname override for email notifier As it currently stands all notifiers utilise `os.Hostname` to populate their titles/subjects. When utilising Docker with a bridged network if you set the hostname for a container to an external DNS hostname Docker's internal DNS resolver will override said hostname for all containers within the bridged network. This change allows a user to specify what hostname should be represented in the email notifications without having to change the `os.Hostname`. * feat: allow custom hostname for all notifiers * docs: adjust notification hostname flag
This commit is contained in:
parent
f508c92ae0
commit
dc12a1ac7f
9 changed files with 45 additions and 20 deletions
|
|
@ -25,6 +25,7 @@ comma-separated list of values to the `--notifications` option
|
||||||
## Settings
|
## 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-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.
|
- 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
|
## Available services
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,12 @@ func RegisterNotificationFlags(rootCmd *cobra.Command) {
|
||||||
viper.GetString("WATCHTOWER_NOTIFICATIONS_LEVEL"),
|
viper.GetString("WATCHTOWER_NOTIFICATIONS_LEVEL"),
|
||||||
"The log level used for sending notifications. Possible values: panic, fatal, error, warn, info or debug")
|
"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(
|
flags.StringP(
|
||||||
"notification-email-from",
|
"notification-email-from",
|
||||||
"",
|
"",
|
||||||
|
|
|
||||||
|
|
@ -60,14 +60,14 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emailTypeNotifier) GetURL() (string, error) {
|
func (e *emailTypeNotifier) GetURL(c *cobra.Command) (string, error) {
|
||||||
conf := &shoutrrrSmtp.Config{
|
conf := &shoutrrrSmtp.Config{
|
||||||
FromAddress: e.From,
|
FromAddress: e.From,
|
||||||
FromName: "Watchtower",
|
FromName: "Watchtower",
|
||||||
ToAddresses: []string{e.To},
|
ToAddresses: []string{e.To},
|
||||||
Port: uint16(e.Port),
|
Port: uint16(e.Port),
|
||||||
Host: e.Server,
|
Host: e.Server,
|
||||||
Subject: e.getSubject(),
|
Subject: e.getSubject(c),
|
||||||
Username: e.User,
|
Username: e.User,
|
||||||
Password: e.Password,
|
Password: e.Password,
|
||||||
UseStartTLS: !e.tlsSkipVerify,
|
UseStartTLS: !e.tlsSkipVerify,
|
||||||
|
|
@ -87,8 +87,8 @@ func (e *emailTypeNotifier) GetURL() (string, error) {
|
||||||
return conf.GetURL().String(), nil
|
return conf.GetURL().String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emailTypeNotifier) getSubject() string {
|
func (e *emailTypeNotifier) getSubject(c *cobra.Command) string {
|
||||||
subject := GetTitle()
|
subject := GetTitle(c)
|
||||||
|
|
||||||
if e.SubjectTag != "" {
|
if e.SubjectTag != "" {
|
||||||
subject = e.SubjectTag + " " + subject
|
subject = e.SubjectTag + " " + subject
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ func getGotifyURL(flags *pflag.FlagSet) string {
|
||||||
return gotifyURL
|
return gotifyURL
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *gotifyTypeNotifier) GetURL() (string, error) {
|
func (n *gotifyTypeNotifier) GetURL(c *cobra.Command) (string, error) {
|
||||||
apiURL, err := url.Parse(n.gotifyURL)
|
apiURL, err := url.Parse(n.gotifyURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -77,7 +77,7 @@ func (n *gotifyTypeNotifier) GetURL() (string, error) {
|
||||||
Host: apiURL.Host,
|
Host: apiURL.Host,
|
||||||
Path: apiURL.Path,
|
Path: apiURL.Path,
|
||||||
DisableTLS: apiURL.Scheme == "http",
|
DisableTLS: apiURL.Scheme == "http",
|
||||||
Title: GetTitle(),
|
Title: GetTitle(c),
|
||||||
Token: n.gotifyAppToken,
|
Token: n.gotifyAppToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.Con
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *msTeamsTypeNotifier) GetURL() (string, error) {
|
func (n *msTeamsTypeNotifier) GetURL(c *cobra.Command) (string, error) {
|
||||||
webhookURL, err := url.Parse(n.webHookURL)
|
webhookURL, err := url.Parse(n.webHookURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -54,7 +54,7 @@ func (n *msTeamsTypeNotifier) GetURL() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Color = ColorHex
|
config.Color = ColorHex
|
||||||
config.Title = GetTitle()
|
config.Title = GetTitle(c)
|
||||||
|
|
||||||
return config.GetURL().String(), nil
|
return config.GetURL().String(), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ func (n *Notifier) getNotificationTypes(cmd *cobra.Command, levels []log.Level,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
shoutrrrURL, err := legacyNotifier.GetURL()
|
shoutrrrURL, err := legacyNotifier.GetURL(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("failed to create notification config:", err)
|
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
|
// GetTitle returns a common notification title with hostname appended
|
||||||
func GetTitle() (title string) {
|
func GetTitle(c *cobra.Command) (title string) {
|
||||||
title = "Watchtower updates"
|
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
|
title += " on " + hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,13 @@ var _ = Describe("notifications", func() {
|
||||||
builderFn := notifications.NewSlackNotifier
|
builderFn := notifications.NewSlackNotifier
|
||||||
|
|
||||||
When("passing a discord url to the slack notifier", func() {
|
When("passing a discord url to the slack notifier", func() {
|
||||||
|
command := cmd.NewRootCommand()
|
||||||
|
flags.RegisterNotificationFlags(command)
|
||||||
|
|
||||||
channel := "123456789"
|
channel := "123456789"
|
||||||
token := "abvsihdbau"
|
token := "abvsihdbau"
|
||||||
color := notifications.ColorInt
|
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)
|
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 {
|
buildArgs := func(url string) []string {
|
||||||
return []string{
|
return []string{
|
||||||
|
|
@ -69,13 +72,15 @@ var _ = Describe("notifications", func() {
|
||||||
When("converting a slack service config into a shoutrrr url", func() {
|
When("converting a slack service config into a shoutrrr url", func() {
|
||||||
|
|
||||||
It("should return the expected URL", func() {
|
It("should return the expected URL", func() {
|
||||||
|
command := cmd.NewRootCommand()
|
||||||
|
flags.RegisterNotificationFlags(command)
|
||||||
|
|
||||||
username := "containrrrbot"
|
username := "containrrrbot"
|
||||||
tokenA := "aaa"
|
tokenA := "aaa"
|
||||||
tokenB := "bbb"
|
tokenB := "bbb"
|
||||||
tokenC := "ccc"
|
tokenC := "ccc"
|
||||||
color := url.QueryEscape(notifications.ColorHex)
|
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)
|
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)
|
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
|
builderFn := notifications.NewGotifyNotifier
|
||||||
|
|
||||||
It("should return the expected URL", func() {
|
It("should return the expected URL", func() {
|
||||||
|
command := cmd.NewRootCommand()
|
||||||
|
flags.RegisterNotificationFlags(command)
|
||||||
|
|
||||||
token := "aaa"
|
token := "aaa"
|
||||||
host := "shoutrrr.local"
|
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)
|
expectedOutput := fmt.Sprintf("gotify://%s/%s?title=%s", host, token, title)
|
||||||
|
|
||||||
|
|
@ -120,12 +128,14 @@ var _ = Describe("notifications", func() {
|
||||||
builderFn := notifications.NewMsTeamsNotifier
|
builderFn := notifications.NewMsTeamsNotifier
|
||||||
|
|
||||||
It("should return the expected URL", func() {
|
It("should return the expected URL", func() {
|
||||||
|
command := cmd.NewRootCommand()
|
||||||
|
flags.RegisterNotificationFlags(command)
|
||||||
|
|
||||||
tokenA := "11111111-4444-4444-8444-cccccccccccc@22222222-4444-4444-8444-cccccccccccc"
|
tokenA := "11111111-4444-4444-8444-cccccccccccc@22222222-4444-4444-8444-cccccccccccc"
|
||||||
tokenB := "33333333012222222222333333333344"
|
tokenB := "33333333012222222222333333333344"
|
||||||
tokenC := "44444444-4444-4444-8444-cccccccccccc"
|
tokenC := "44444444-4444-4444-8444-cccccccccccc"
|
||||||
color := url.QueryEscape(notifications.ColorHex)
|
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)
|
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)
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
notifier := builder(command, []log.Level{})
|
notifier := builder(command, []log.Level{})
|
||||||
actualURL, err := notifier.GetURL()
|
actualURL, err := notifier.GetURL(command)
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *slackTypeNotifier) GetURL() (string, error) {
|
func (s *slackTypeNotifier) GetURL(c *cobra.Command) (string, error) {
|
||||||
trimmedURL := strings.TrimRight(s.HookURL, "/")
|
trimmedURL := strings.TrimRight(s.HookURL, "/")
|
||||||
trimmedURL = strings.TrimLeft(trimmedURL, "https://")
|
trimmedURL = strings.TrimLeft(trimmedURL, "https://")
|
||||||
parts := strings.Split(trimmedURL, "/")
|
parts := strings.Split(trimmedURL, "/")
|
||||||
|
|
@ -57,7 +57,7 @@ func (s *slackTypeNotifier) GetURL() (string, error) {
|
||||||
Channel: parts[len(parts)-3],
|
Channel: parts[len(parts)-3],
|
||||||
Token: parts[len(parts)-2],
|
Token: parts[len(parts)-2],
|
||||||
Color: ColorInt,
|
Color: ColorInt,
|
||||||
Title: GetTitle(),
|
Title: GetTitle(c),
|
||||||
SplitLines: true,
|
SplitLines: true,
|
||||||
Username: s.Username,
|
Username: s.Username,
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +71,7 @@ func (s *slackTypeNotifier) GetURL() (string, error) {
|
||||||
BotName: s.Username,
|
BotName: s.Username,
|
||||||
Token: tokens,
|
Token: tokens,
|
||||||
Color: ColorHex,
|
Color: ColorHex,
|
||||||
Title: GetTitle(),
|
Title: GetTitle(c),
|
||||||
}
|
}
|
||||||
|
|
||||||
return conf.GetURL().String(), nil
|
return conf.GetURL().String(), nil
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
// ConvertibleNotifier is a notifier capable of creating a shoutrrr URL
|
// ConvertibleNotifier is a notifier capable of creating a shoutrrr URL
|
||||||
type ConvertibleNotifier interface {
|
type ConvertibleNotifier interface {
|
||||||
GetURL() (string, error)
|
GetURL(c *cobra.Command) (string, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue