You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
watchtower/pkg/notifications/notifier_test.go

371 lines
12 KiB
Go

package notifications_test
import (
"fmt"
"net/url"
"os"
"time"
"github.com/containrrr/watchtower/cmd"
"github.com/containrrr/watchtower/internal/flags"
"github.com/containrrr/watchtower/pkg/notifications"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("notifications", func() {
Describe("the notifier", func() {
When("only empty notifier types are provided", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
err := command.ParseFlags([]string{
"--notifications",
"shoutrrr",
})
Expect(err).NotTo(HaveOccurred())
notif := notifications.NewNotifier(command)
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())
data := notifications.GetTemplateData(command)
title := data.Title
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"))
})
})
When("title tag is set", func() {
It("should use the prefix in the title", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
Expect(command.ParseFlags([]string{
"--notification-title-tag",
"PREFIX",
})).To(Succeed())
data := notifications.GetTemplateData(command)
Expect(data.Title).To(HavePrefix("[PREFIX]"))
})
})
When("legacy email tag is set", func() {
It("should use the prefix in the title", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
Expect(command.ParseFlags([]string{
"--notification-email-subjecttag",
"PREFIX",
})).To(Succeed())
data := notifications.GetTemplateData(command)
Expect(data.Title).To(HavePrefix("[PREFIX]"))
})
})
When("the skip title flag is set", func() {
It("should return an empty title", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
Expect(command.ParseFlags([]string{
"--notification-skip-title",
})).To(Succeed())
data := notifications.GetTemplateData(command)
Expect(data.Title).To(BeEmpty())
})
})
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
When("passing a discord url to the slack notifier", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
channel := "123456789"
token := "abvsihdbau"
color := notifications.ColorInt
data := notifications.GetTemplateData(command)
title := url.QueryEscape(data.Title)
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{
"--notifications",
"slack",
"--notification-slack-hook-url",
url,
}
}
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, 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, time.Duration(0))
})
})
When("converting a slack service config into a shoutrrr url", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
username := "containrrrbot"
tokenA := "AAAAAAAAA"
tokenB := "BBBBBBBBB"
tokenC := "123456789123456789123456"
color := url.QueryEscape(notifications.ColorHex)
data := notifications.GetTemplateData(command)
title := url.QueryEscape(data.Title)
iconURL := "https://containrrr.dev/watchtower-sq180.png"
iconEmoji := "whale"
When("icon URL is specified", func() {
It("should return the expected URL", 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",
"slack",
"--notification-slack-hook-url",
hookURL,
"--notification-slack-identifier",
username,
"--notification-slack-icon-url",
iconURL,
"--notifications-delay",
fmt.Sprint(expectedDelay.Seconds()),
}
testURL(args, expectedOutput, expectedDelay)
})
})
When("icon emoji is specified", func() {
It("should return the expected URL", 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, iconEmoji, title)
args := []string{
"--notifications",
"slack",
"--notification-slack-hook-url",
hookURL,
"--notification-slack-identifier",
username,
"--notification-slack-icon-emoji",
iconEmoji,
}
testURL(args, expectedOutput, time.Duration(0))
})
})
})
})
Describe("the gotify notifier", func() {
When("converting a gotify service config into a shoutrrr url", func() {
It("should return the expected URL", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
token := "aaa"
host := "shoutrrr.local"
data := notifications.GetTemplateData(command)
title := url.QueryEscape(data.Title)
expectedOutput := fmt.Sprintf("gotify://%s/%s?title=%s", host, token, title)
args := []string{
"--notifications",
"gotify",
"--notification-gotify-url",
fmt.Sprintf("https://%s", host),
"--notification-gotify-token",
token,
}
testURL(args, expectedOutput, time.Duration(0))
})
})
})
Describe("the teams notifier", func() {
When("converting a teams service config into a shoutrrr 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"
tokenB := "33333333012222222222333333333344"
tokenC := "44444444-4444-4444-8444-cccccccccccc"
color := url.QueryEscape(notifications.ColorHex)
data := notifications.GetTemplateData(command)
title := url.QueryEscape(data.Title)
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)
args := []string{
"--notifications",
"msteams",
"--notification-msteams-hook",
hookURL,
}
testURL(args, expectedOutput, time.Duration(0))
})
})
})
Describe("the email notifier", func() {
When("converting an email service config into a shoutrrr url", 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",
"--notification-email-from",
fromAddress,
"--notification-email-to",
"mail@example.com",
"--notification-email-server-user",
"containrrrbot",
"--notification-email-server-password",
"secret-password",
"--notification-email-server",
"mail.containrrr.dev",
"--notifications-delay",
fmt.Sprint(expectedDelay.Seconds()),
}
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",
"email",
"--notification-email-from",
fromAddress,
"--notification-email-to",
toAddress,
"--notification-email-server-user",
"containrrrbot",
"--notification-email-server-password",
"secret-password",
"--notification-email-server",
"mail.containrrr.dev",
"--notification-email-delay",
fmt.Sprint(expectedDelay.Seconds()),
}
testURL(args, expectedOutput, expectedDelay)
})
})
})
})
func buildExpectedURL(username string, password string, host string, port int, from string, to string, auth string) string {
hostname, err := os.Hostname()
Expect(err).NotTo(HaveOccurred())
subject := fmt.Sprintf("Watchtower updates on %s", hostname)
var template = "smtp://%s:%s@%s:%d/?auth=%s&fromaddress=%s&fromname=Watchtower&subject=%s&toaddresses=%s"
return fmt.Sprintf(template,
url.QueryEscape(username),
url.QueryEscape(password),
host, port, auth,
url.QueryEscape(from),
url.QueryEscape(subject),
url.QueryEscape(to))
}
func testURL(args []string, expectedURL string, expectedDelay time.Duration) {
defer GinkgoRecover()
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
Expect(command.ParseFlags(args)).To(Succeed())
data := notifications.GetTemplateData(command)
urls, delay := notifications.AppendLegacyUrls([]string{}, command, data.Title)
Expect(urls).To(ContainElement(expectedURL))
Expect(delay).To(Equal(expectedDelay))
}