mirror of
https://github.com/containrrr/watchtower.git
synced 2025-09-22 05:40:50 +02:00
feat(notifications): add general notification delay (#1246)
This commit is contained in:
parent
f79e4b5435
commit
a5c60a9fe6
4 changed files with 91 additions and 12 deletions
|
@ -26,6 +26,7 @@ comma-separated list of values to the `--notifications` option
|
||||||
|
|
||||||
- `--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.
|
- `--notifications-hostname` (env. `WATCHTOWER_NOTIFICATIONS_HOSTNAME`): Custom hostname specified in subject/title. Useful to override the operating system hostname.
|
||||||
|
- `--notifications-delay` (env. `WATCHTOWER_NOTIFICATION_DELAY`): Delay before sending notifications expressed in seconds.
|
||||||
- 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
|
||||||
|
|
|
@ -184,6 +184,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.IntP(
|
||||||
|
"notifications-delay",
|
||||||
|
"",
|
||||||
|
viper.GetInt("WATCHTOWER_NOTIFICATIONS_DELAY"),
|
||||||
|
"Delay before sending notifications, expressed in seconds")
|
||||||
|
|
||||||
flags.StringP(
|
flags.StringP(
|
||||||
"notifications-hostname",
|
"notifications-hostname",
|
||||||
"",
|
"",
|
||||||
|
|
|
@ -45,7 +45,7 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string
|
||||||
log.WithError(err).Fatal("could not read notifications argument")
|
log.WithError(err).Fatal("could not read notifications argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
delay := time.Duration(0)
|
legacyDelay := time.Duration(0)
|
||||||
|
|
||||||
for _, t := range types {
|
for _, t := range types {
|
||||||
|
|
||||||
|
@ -76,14 +76,29 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string
|
||||||
urls = append(urls, shoutrrrURL)
|
urls = append(urls, shoutrrrURL)
|
||||||
|
|
||||||
if delayNotifier, ok := legacyNotifier.(ty.DelayNotifier); ok {
|
if delayNotifier, ok := legacyNotifier.(ty.DelayNotifier); ok {
|
||||||
delay = delayNotifier.GetDelay()
|
legacyDelay = delayNotifier.GetDelay()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.WithField("URL", shoutrrrURL).Trace("created Shoutrrr URL from legacy notifier")
|
log.WithField("URL", shoutrrrURL).Trace("created Shoutrrr URL from legacy notifier")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delay := GetDelay(cmd, legacyDelay)
|
||||||
return urls, delay
|
return urls, delay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDelay returns the legacy delay if defined, otherwise the delay as set by args is returned
|
||||||
|
func GetDelay(c *cobra.Command, legacyDelay time.Duration) time.Duration {
|
||||||
|
if legacyDelay > 0 {
|
||||||
|
return legacyDelay
|
||||||
|
}
|
||||||
|
|
||||||
|
delay, _ := c.PersistentFlags().GetInt("notifications-delay")
|
||||||
|
if delay > 0 {
|
||||||
|
return time.Duration(delay) * time.Second
|
||||||
|
}
|
||||||
|
return time.Duration(0)
|
||||||
|
}
|
||||||
|
|
||||||
// GetTitle returns a common notification title with hostname appended
|
// GetTitle returns a common notification title with hostname appended
|
||||||
func GetTitle(hostname string) string {
|
func GetTitle(hostname string) string {
|
||||||
title := "Watchtower updates"
|
title := "Watchtower updates"
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containrrr/watchtower/cmd"
|
"github.com/containrrr/watchtower/cmd"
|
||||||
"github.com/containrrr/watchtower/internal/flags"
|
"github.com/containrrr/watchtower/internal/flags"
|
||||||
|
@ -49,6 +50,51 @@ var _ = Describe("notifications", func() {
|
||||||
Expect(title).To(Equal("Watchtower updates"))
|
Expect(title).To(Equal("Watchtower updates"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
When("no delay is defined", func() {
|
||||||
|
It("should use the default delay", func() {
|
||||||
|
command := cmd.NewRootCommand()
|
||||||
|
flags.RegisterNotificationFlags(command)
|
||||||
|
|
||||||
|
delay := notifications.GetDelay(command, time.Duration(0))
|
||||||
|
Expect(delay).To(Equal(time.Duration(0)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
When("delay is defined", func() {
|
||||||
|
It("should use the specified delay", func() {
|
||||||
|
command := cmd.NewRootCommand()
|
||||||
|
flags.RegisterNotificationFlags(command)
|
||||||
|
|
||||||
|
err := command.ParseFlags([]string{
|
||||||
|
"--notifications-delay",
|
||||||
|
"5",
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
delay := notifications.GetDelay(command, time.Duration(0))
|
||||||
|
Expect(delay).To(Equal(time.Duration(5) * time.Second))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
When("legacy delay is defined", func() {
|
||||||
|
It("should use the specified legacy delay", func() {
|
||||||
|
command := cmd.NewRootCommand()
|
||||||
|
flags.RegisterNotificationFlags(command)
|
||||||
|
delay := notifications.GetDelay(command, time.Duration(5)*time.Second)
|
||||||
|
Expect(delay).To(Equal(time.Duration(5) * time.Second))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
When("legacy delay and delay is defined", func() {
|
||||||
|
It("should use the specified legacy delay and ignore the specified delay", func() {
|
||||||
|
command := cmd.NewRootCommand()
|
||||||
|
flags.RegisterNotificationFlags(command)
|
||||||
|
|
||||||
|
err := command.ParseFlags([]string{
|
||||||
|
"--notifications-delay",
|
||||||
|
"0",
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
delay := notifications.GetDelay(command, time.Duration(7)*time.Second)
|
||||||
|
Expect(delay).To(Equal(time.Duration(7) * time.Second))
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
Describe("the slack notifier", func() {
|
Describe("the slack notifier", func() {
|
||||||
// builderFn := notifications.NewSlackNotifier
|
// builderFn := notifications.NewSlackNotifier
|
||||||
|
@ -74,11 +120,11 @@ var _ = Describe("notifications", func() {
|
||||||
|
|
||||||
It("should return a discord url when using a hook url with the domain discord.com", func() {
|
It("should return a discord url when using a hook url with the domain discord.com", func() {
|
||||||
hookURL := fmt.Sprintf("https://%s/api/webhooks/%s/%s/slack", "discord.com", channel, token)
|
hookURL := fmt.Sprintf("https://%s/api/webhooks/%s/%s/slack", "discord.com", channel, token)
|
||||||
testURL(buildArgs(hookURL), expected)
|
testURL(buildArgs(hookURL), expected, time.Duration(0))
|
||||||
})
|
})
|
||||||
It("should return a discord url when using a hook url with the domain discordapp.com", func() {
|
It("should return a discord url when using a hook url with the domain discordapp.com", func() {
|
||||||
hookURL := fmt.Sprintf("https://%s/api/webhooks/%s/%s/slack", "discordapp.com", channel, token)
|
hookURL := fmt.Sprintf("https://%s/api/webhooks/%s/%s/slack", "discordapp.com", channel, token)
|
||||||
testURL(buildArgs(hookURL), expected)
|
testURL(buildArgs(hookURL), expected, time.Duration(0))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
When("converting a slack service config into a shoutrrr url", func() {
|
When("converting a slack service config into a shoutrrr url", func() {
|
||||||
|
@ -99,6 +145,7 @@ var _ = Describe("notifications", func() {
|
||||||
|
|
||||||
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://hook:%s-%s-%s@webhook?botname=%s&color=%s&icon=%s&title=%s", tokenA, tokenB, tokenC, username, color, url.QueryEscape(iconURL), title)
|
expectedOutput := fmt.Sprintf("slack://hook:%s-%s-%s@webhook?botname=%s&color=%s&icon=%s&title=%s", tokenA, tokenB, tokenC, username, color, url.QueryEscape(iconURL), title)
|
||||||
|
expectedDelay := time.Duration(7) * time.Second
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--notifications",
|
"--notifications",
|
||||||
|
@ -109,9 +156,11 @@ var _ = Describe("notifications", func() {
|
||||||
username,
|
username,
|
||||||
"--notification-slack-icon-url",
|
"--notification-slack-icon-url",
|
||||||
iconURL,
|
iconURL,
|
||||||
|
"--notifications-delay",
|
||||||
|
fmt.Sprint(expectedDelay.Seconds()),
|
||||||
}
|
}
|
||||||
|
|
||||||
testURL(args, expectedOutput)
|
testURL(args, expectedOutput, expectedDelay)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -131,7 +180,7 @@ var _ = Describe("notifications", func() {
|
||||||
iconEmoji,
|
iconEmoji,
|
||||||
}
|
}
|
||||||
|
|
||||||
testURL(args, expectedOutput)
|
testURL(args, expectedOutput, time.Duration(0))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -159,7 +208,7 @@ var _ = Describe("notifications", func() {
|
||||||
token,
|
token,
|
||||||
}
|
}
|
||||||
|
|
||||||
testURL(args, expectedOutput)
|
testURL(args, expectedOutput, time.Duration(0))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -187,7 +236,7 @@ var _ = Describe("notifications", func() {
|
||||||
hookURL,
|
hookURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
testURL(args, expectedOutput)
|
testURL(args, expectedOutput, time.Duration(0))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -197,6 +246,8 @@ var _ = Describe("notifications", func() {
|
||||||
It("should set the from address in the URL", func() {
|
It("should set the from address in the URL", func() {
|
||||||
fromAddress := "lala@example.com"
|
fromAddress := "lala@example.com"
|
||||||
expectedOutput := buildExpectedURL("containrrrbot", "secret-password", "mail.containrrr.dev", 25, fromAddress, "mail@example.com", "Plain")
|
expectedOutput := buildExpectedURL("containrrrbot", "secret-password", "mail.containrrr.dev", 25, fromAddress, "mail@example.com", "Plain")
|
||||||
|
expectedDelay := time.Duration(7) * time.Second
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--notifications",
|
"--notifications",
|
||||||
"email",
|
"email",
|
||||||
|
@ -210,8 +261,10 @@ var _ = Describe("notifications", func() {
|
||||||
"secret-password",
|
"secret-password",
|
||||||
"--notification-email-server",
|
"--notification-email-server",
|
||||||
"mail.containrrr.dev",
|
"mail.containrrr.dev",
|
||||||
|
"--notifications-delay",
|
||||||
|
fmt.Sprint(expectedDelay.Seconds()),
|
||||||
}
|
}
|
||||||
testURL(args, expectedOutput)
|
testURL(args, expectedOutput, expectedDelay)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should return the expected URL", func() {
|
It("should return the expected URL", func() {
|
||||||
|
@ -219,6 +272,7 @@ var _ = Describe("notifications", func() {
|
||||||
fromAddress := "sender@example.com"
|
fromAddress := "sender@example.com"
|
||||||
toAddress := "receiver@example.com"
|
toAddress := "receiver@example.com"
|
||||||
expectedOutput := buildExpectedURL("containrrrbot", "secret-password", "mail.containrrr.dev", 25, fromAddress, toAddress, "Plain")
|
expectedOutput := buildExpectedURL("containrrrbot", "secret-password", "mail.containrrr.dev", 25, fromAddress, toAddress, "Plain")
|
||||||
|
expectedDelay := time.Duration(7) * time.Second
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--notifications",
|
"--notifications",
|
||||||
|
@ -233,9 +287,11 @@ var _ = Describe("notifications", func() {
|
||||||
"secret-password",
|
"secret-password",
|
||||||
"--notification-email-server",
|
"--notification-email-server",
|
||||||
"mail.containrrr.dev",
|
"mail.containrrr.dev",
|
||||||
|
"--notification-email-delay",
|
||||||
|
fmt.Sprint(expectedDelay.Seconds()),
|
||||||
}
|
}
|
||||||
|
|
||||||
testURL(args, expectedOutput)
|
testURL(args, expectedOutput, expectedDelay)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -257,7 +313,7 @@ func buildExpectedURL(username string, password string, host string, port int, f
|
||||||
url.QueryEscape(to))
|
url.QueryEscape(to))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testURL(args []string, expectedURL string) {
|
func testURL(args []string, expectedURL string, expectedDelay time.Duration) {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
command := cmd.NewRootCommand()
|
command := cmd.NewRootCommand()
|
||||||
|
@ -268,9 +324,10 @@ func testURL(args []string, expectedURL string) {
|
||||||
|
|
||||||
hostname := notifications.GetHostname(command)
|
hostname := notifications.GetHostname(command)
|
||||||
title := notifications.GetTitle(hostname)
|
title := notifications.GetTitle(hostname)
|
||||||
urls, _ := notifications.AppendLegacyUrls([]string{}, command, title)
|
urls, delay := notifications.AppendLegacyUrls([]string{}, command, title)
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(urls).To(ContainElement(expectedURL))
|
Expect(urls).To(ContainElement(expectedURL))
|
||||||
|
Expect(delay).To(Equal(expectedDelay))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue