Skip to content

Commit

Permalink
feat(notifications): add general notification delay (#1246)
Browse files Browse the repository at this point in the history
  • Loading branch information
lazou committed Mar 9, 2022
1 parent f79e4b5 commit a5c60a9
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 12 deletions.
1 change: 1 addition & 0 deletions docs/notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-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.

## Available services
Expand Down
6 changes: 6 additions & 0 deletions internal/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,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.IntP(
"notifications-delay",
"",
viper.GetInt("WATCHTOWER_NOTIFICATIONS_DELAY"),
"Delay before sending notifications, expressed in seconds")

flags.StringP(
"notifications-hostname",
"",
Expand Down
19 changes: 17 additions & 2 deletions pkg/notifications/notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string
log.WithError(err).Fatal("could not read notifications argument")
}

delay := time.Duration(0)
legacyDelay := time.Duration(0)

for _, t := range types {

Expand Down Expand Up @@ -76,14 +76,29 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string
urls = append(urls, shoutrrrURL)

if delayNotifier, ok := legacyNotifier.(ty.DelayNotifier); ok {
delay = delayNotifier.GetDelay()
legacyDelay = delayNotifier.GetDelay()
}

log.WithField("URL", shoutrrrURL).Trace("created Shoutrrr URL from legacy notifier")
}

delay := GetDelay(cmd, legacyDelay)
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
func GetTitle(hostname string) string {
title := "Watchtower updates"
Expand Down
77 changes: 67 additions & 10 deletions pkg/notifications/notifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"net/url"
"os"
"time"

"github.com/containrrr/watchtower/cmd"
"github.com/containrrr/watchtower/internal/flags"
Expand Down Expand Up @@ -49,6 +50,51 @@ var _ = Describe("notifications", func() {
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() {
// builderFn := notifications.NewSlackNotifier
Expand All @@ -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() {
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() {
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() {
Expand All @@ -99,6 +145,7 @@ var _ = Describe("notifications", func() {

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)
expectedDelay := time.Duration(7) * time.Second

args := []string{
"--notifications",
Expand All @@ -109,9 +156,11 @@ var _ = Describe("notifications", func() {
username,
"--notification-slack-icon-url",
iconURL,
"--notifications-delay",
fmt.Sprint(expectedDelay.Seconds()),
}

testURL(args, expectedOutput)
testURL(args, expectedOutput, expectedDelay)
})
})

Expand All @@ -131,7 +180,7 @@ var _ = Describe("notifications", func() {
iconEmoji,
}

testURL(args, expectedOutput)
testURL(args, expectedOutput, time.Duration(0))
})
})
})
Expand Down Expand Up @@ -159,7 +208,7 @@ var _ = Describe("notifications", func() {
token,
}

testURL(args, expectedOutput)
testURL(args, expectedOutput, time.Duration(0))
})
})
})
Expand Down Expand Up @@ -187,7 +236,7 @@ var _ = Describe("notifications", func() {
hookURL,
}

testURL(args, expectedOutput)
testURL(args, expectedOutput, time.Duration(0))
})
})
})
Expand All @@ -197,6 +246,8 @@ var _ = Describe("notifications", func() {
It("should set the from address in the URL", func() {
fromAddress := "lala@example.com"
expectedOutput := buildExpectedURL("containrrrbot", "secret-password", "mail.containrrr.dev", 25, fromAddress, "mail@example.com", "Plain")
expectedDelay := time.Duration(7) * time.Second

args := []string{
"--notifications",
"email",
Expand All @@ -210,15 +261,18 @@ var _ = Describe("notifications", func() {
"secret-password",
"--notification-email-server",
"mail.containrrr.dev",
"--notifications-delay",
fmt.Sprint(expectedDelay.Seconds()),
}
testURL(args, expectedOutput)
testURL(args, expectedOutput, expectedDelay)
})

It("should return the expected URL", func() {

fromAddress := "sender@example.com"
toAddress := "receiver@example.com"
expectedOutput := buildExpectedURL("containrrrbot", "secret-password", "mail.containrrr.dev", 25, fromAddress, toAddress, "Plain")
expectedDelay := time.Duration(7) * time.Second

args := []string{
"--notifications",
Expand All @@ -233,9 +287,11 @@ var _ = Describe("notifications", func() {
"secret-password",
"--notification-email-server",
"mail.containrrr.dev",
"--notification-email-delay",
fmt.Sprint(expectedDelay.Seconds()),
}

testURL(args, expectedOutput)
testURL(args, expectedOutput, expectedDelay)
})
})
})
Expand All @@ -257,7 +313,7 @@ func buildExpectedURL(username string, password string, host string, port int, f
url.QueryEscape(to))
}

func testURL(args []string, expectedURL string) {
func testURL(args []string, expectedURL string, expectedDelay time.Duration) {
defer GinkgoRecover()

command := cmd.NewRootCommand()
Expand All @@ -268,9 +324,10 @@ func testURL(args []string, expectedURL string) {

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

Expect(err).NotTo(HaveOccurred())

Expect(urls).To(ContainElement(expectedURL))
Expect(delay).To(Equal(expectedDelay))
}

0 comments on commit a5c60a9

Please sign in to comment.