fix tests, upgrade snakes

This commit is contained in:
nils måsén 2021-06-27 00:19:52 +02:00 committed by nils måsén
parent aa50cdf9bc
commit 1165f31ca0
15 changed files with 669 additions and 283 deletions

View file

@ -7,10 +7,11 @@ import (
"strings"
"time"
"github.com/containrrr/watchtower/internal/flags"
"github.com/containrrr/watchtower/pkg/registry"
"github.com/containrrr/watchtower/pkg/registry/digest"
t "github.com/containrrr/watchtower/pkg/types"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
@ -42,7 +43,7 @@ type Client interface {
// * DOCKER_HOST the docker-engine host to send api requests to
// * DOCKER_TLS_VERIFY whether to verify tls certificates
// * DOCKER_API_VERSION the minimum docker api version to work with
func NewClient(pullImages, includeStopped, reviveStopped, removeVolumes, includeRestarting bool, warnOnHeadFailed string) Client {
func NewClient(c *flags.WatchConfig) Client {
cli, err := sdkClient.NewClientWithOpts(sdkClient.FromEnv)
if err != nil {
@ -51,12 +52,12 @@ func NewClient(pullImages, includeStopped, reviveStopped, removeVolumes, include
return dockerClient{
api: cli,
pullImages: pullImages,
removeVolumes: removeVolumes,
includeStopped: includeStopped,
reviveStopped: reviveStopped,
includeRestarting: includeRestarting,
warnOnHeadFailed: warnOnHeadFailed,
pullImages: !c.NoPull,
removeVolumes: c.RemoveVolumes,
includeStopped: c.IncludeStopped,
reviveStopped: c.ReviveStopped,
includeRestarting: c.IncludeRestarting,
warnOnHeadFailed: c.WarnOnHeadFailed,
}
}
@ -337,7 +338,7 @@ func (client dockerClient) PullImage(ctx context.Context, container Container) e
return err
}
defer response.Close()
defer func() { _ = response.Close() }()
// the pull request will be aborted prematurely unless the response is read
if _, err = ioutil.ReadAll(response); err != nil {
log.Error(err)

View file

@ -1,10 +1,9 @@
package notifications
import (
"github.com/spf13/viper"
"time"
"github.com/spf13/cobra"
shoutrrrSmtp "github.com/containrrr/shoutrrr/pkg/services/smtp"
t "github.com/containrrr/watchtower/pkg/types"
log "github.com/sirupsen/logrus"
@ -25,18 +24,16 @@ type emailTypeNotifier struct {
delay time.Duration
}
func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
flags := c.PersistentFlags()
from, _ := flags.GetString("notification-email-from")
to, _ := flags.GetString("notification-email-to")
server, _ := flags.GetString("notification-email-server")
user, _ := flags.GetString("notification-email-server-user")
password, _ := flags.GetString("notification-email-server-password")
port, _ := flags.GetInt("notification-email-server-port")
tlsSkipVerify, _ := flags.GetBool("notification-email-server-tls-skip-verify")
delay, _ := flags.GetInt("notification-email-delay")
subjecttag, _ := flags.GetString("notification-email-subjecttag")
func newEmailNotifier() t.ConvertibleNotifier {
from := viper.GetString("notification-email-from")
to := viper.GetString("notification-email-to")
server := viper.GetString("notification-email-server")
user := viper.GetString("notification-email-server-user")
password := viper.GetString("notification-email-server-password")
port := viper.GetInt("notification-email-server-port")
tlsSkipVerify := viper.GetBool("notification-email-server-tls-skip-verify")
delay := viper.GetInt("notification-email-delay")
subjecttag := viper.GetString("notification-email-subjecttag")
n := &emailTypeNotifier{
entries: []*log.Entry{},
@ -47,7 +44,6 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
Password: password,
Port: port,
tlsSkipVerify: tlsSkipVerify,
logLevels: acceptedLogLevels,
delay: time.Duration(delay) * time.Second,
SubjectTag: subjecttag,
}
@ -55,14 +51,14 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
return n
}
func (e *emailTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
func (e *emailTypeNotifier) GetURL(title string) (string, error) {
conf := &shoutrrrSmtp.Config{
FromAddress: e.From,
FromName: "Watchtower",
ToAddresses: []string{e.To},
Port: uint16(e.Port),
Host: e.Server,
Subject: e.getSubject(c, title),
Subject: e.getSubject(title),
Username: e.User,
Password: e.Password,
UseStartTLS: !e.tlsSkipVerify,
@ -86,7 +82,7 @@ func (e *emailTypeNotifier) GetDelay() time.Duration {
return e.delay
}
func (e *emailTypeNotifier) getSubject(_ *cobra.Command, title string) string {
func (e *emailTypeNotifier) getSubject(title string) string {
if e.SubjectTag != "" {
return e.SubjectTag + " " + title
}

View file

@ -7,8 +7,7 @@ import (
shoutrrrGotify "github.com/containrrr/shoutrrr/pkg/services/gotify"
t "github.com/containrrr/watchtower/pkg/types"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
const (
@ -19,37 +18,34 @@ type gotifyTypeNotifier struct {
gotifyURL string
gotifyAppToken string
gotifyInsecureSkipVerify bool
logLevels []log.Level
}
func newGotifyNotifier(c *cobra.Command, levels []log.Level) t.ConvertibleNotifier {
flags := c.PersistentFlags()
func newGotifyNotifier() t.ConvertibleNotifier {
apiURL := getGotifyURL(flags)
token := getGotifyToken(flags)
apiURL := getGotifyURL()
token := getGotifyToken()
skipVerify, _ := flags.GetBool("notification-gotify-tls-skip-verify")
skipVerify := viper.GetBool("notification-gotify-tls-skip-verify")
n := &gotifyTypeNotifier{
gotifyURL: apiURL,
gotifyAppToken: token,
gotifyInsecureSkipVerify: skipVerify,
logLevels: levels,
}
return n
}
func getGotifyToken(flags *pflag.FlagSet) string {
gotifyToken, _ := flags.GetString("notification-gotify-token")
func getGotifyToken() string {
gotifyToken := viper.GetString("notification-gotify-token")
if len(gotifyToken) < 1 {
log.Fatal("Required argument --notification-gotify-token(cli) or WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN(env) is empty.")
}
return gotifyToken
}
func getGotifyURL(flags *pflag.FlagSet) string {
gotifyURL, _ := flags.GetString("notification-gotify-url")
func getGotifyURL() string {
gotifyURL := viper.GetString("notification-gotify-url")
if len(gotifyURL) < 1 {
log.Fatal("Required argument --notification-gotify-url(cli) or WATCHTOWER_NOTIFICATION_GOTIFY_URL(env) is empty.")
@ -62,7 +58,7 @@ func getGotifyURL(flags *pflag.FlagSet) string {
return gotifyURL
}
func (n *gotifyTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
func (n *gotifyTypeNotifier) GetURL(title string) (string, error) {
apiURL, err := url.Parse(n.gotifyURL)
if err != nil {
return "", err

View file

@ -6,7 +6,7 @@ import (
shoutrrrTeams "github.com/containrrr/shoutrrr/pkg/services/teams"
t "github.com/containrrr/watchtower/pkg/types"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
@ -15,22 +15,18 @@ const (
type msTeamsTypeNotifier struct {
webHookURL string
levels []log.Level
data bool
}
func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
func newMsTeamsNotifier() t.ConvertibleNotifier {
flags := cmd.PersistentFlags()
webHookURL, _ := flags.GetString("notification-msteams-hook")
webHookURL := viper.GetString("notification-msteams-hook")
if len(webHookURL) <= 0 {
log.Fatal("Required argument --notification-msteams-hook(cli) or WATCHTOWER_NOTIFICATION_MSTEAMS_HOOK_URL(env) is empty.")
}
withData, _ := flags.GetBool("notification-msteams-data")
withData := viper.GetBool("notification-msteams-data")
n := &msTeamsTypeNotifier{
levels: acceptedLogLevels,
webHookURL: webHookURL,
data: withData,
}
@ -38,7 +34,7 @@ func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.Con
return n
}
func (n *msTeamsTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
func (n *msTeamsTypeNotifier) GetURL(title string) (string, error) {
webhookURL, err := url.Parse(n.webHookURL)
if err != nil {
return "", err

View file

@ -7,14 +7,12 @@ import (
ty "github.com/containrrr/watchtower/pkg/types"
"github.com/johntdyer/slackrus"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// NewNotifier creates and returns a new Notifier, using global configuration.
func NewNotifier(c *cobra.Command) ty.Notifier {
f := c.PersistentFlags()
level, _ := f.GetString("notifications-level")
func NewNotifier() ty.Notifier {
level := viper.GetString("notifications-level")
logLevel, err := log.ParseLevel(level)
if err != nil {
log.Fatalf("Notifications invalid log level: %s", err.Error())
@ -26,24 +24,21 @@ func NewNotifier(c *cobra.Command) ty.Notifier {
log.Fatalf("Unsupported notification log level provided: %s", level)
}
reportTemplate, _ := f.GetBool("notification-report")
tplString, _ := f.GetString("notification-template")
urls, _ := f.GetStringArray("notification-url")
reportTemplate := viper.GetBool("notification-report")
tplString := viper.GetString("notification-template")
urls := viper.GetStringSlice("notification-url")
hostname := GetHostname(c)
urls, delay := AppendLegacyUrls(urls, c, GetTitle(hostname))
hostname := GetHostname()
urls, delay := AppendLegacyUrls(urls, GetTitle(hostname))
return newShoutrrrNotifier(tplString, acceptedLogLevels, !reportTemplate, hostname, delay, urls...)
}
// AppendLegacyUrls creates shoutrrr equivalent URLs from legacy notification flags
func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string, time.Duration) {
func AppendLegacyUrls(urls []string, title string) ([]string, time.Duration) {
// Parse types and create notifiers.
types, err := cmd.Flags().GetStringSlice("notifications")
if err != nil {
log.WithError(err).Fatal("could not read notifications argument")
}
types := viper.GetStringSlice("notifications")
legacyDelay := time.Duration(0)
@ -54,13 +49,13 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string
switch t {
case emailType:
legacyNotifier = newEmailNotifier(cmd, []log.Level{})
legacyNotifier = newEmailNotifier()
case slackType:
legacyNotifier = newSlackNotifier(cmd, []log.Level{})
legacyNotifier = newSlackNotifier()
case msTeamsType:
legacyNotifier = newMsTeamsNotifier(cmd, []log.Level{})
legacyNotifier = newMsTeamsNotifier()
case gotifyType:
legacyNotifier = newGotifyNotifier(cmd, []log.Level{})
legacyNotifier = newGotifyNotifier()
case shoutrrrType:
continue
default:
@ -69,7 +64,7 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string
continue
}
shoutrrrURL, err := legacyNotifier.GetURL(cmd, title)
shoutrrrURL, err := legacyNotifier.GetURL(title)
if err != nil {
log.Fatal("failed to create notification config: ", err)
}
@ -82,17 +77,17 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string
log.WithField("URL", shoutrrrURL).Trace("created Shoutrrr URL from legacy notifier")
}
delay := GetDelay(cmd, legacyDelay)
delay := GetDelay(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 {
func GetDelay(legacyDelay time.Duration) time.Duration {
if legacyDelay > 0 {
return legacyDelay
}
delay, _ := c.PersistentFlags().GetInt("notifications-delay")
delay := viper.GetInt("notifications-delay")
if delay > 0 {
return time.Duration(delay) * time.Second
}
@ -109,10 +104,8 @@ func GetTitle(hostname string) string {
}
// GetHostname returns the hostname as set by args or resolved from OS
func GetHostname(c *cobra.Command) string {
f := c.PersistentFlags()
hostname, _ := f.GetString("notifications-hostname")
func GetHostname() string {
hostname := viper.GetString("notifications-hostname")
if hostname != "" {
return hostname

View file

@ -19,13 +19,14 @@ var _ = Describe("notifications", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
flags.BindViperFlags(command)
err := command.ParseFlags([]string{
"--notifications",
"shoutrrr",
})
Expect(err).NotTo(HaveOccurred())
notif := notifications.NewNotifier(command)
notif := notifications.NewNotifier()
Expect(notif.GetNames()).To(BeEmpty())
})
@ -33,13 +34,14 @@ var _ = Describe("notifications", func() {
It("should use the specified hostname in the title", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
flags.BindViperFlags(command)
err := command.ParseFlags([]string{
"--notifications-hostname",
"test.host",
})
Expect(err).NotTo(HaveOccurred())
hostname := notifications.GetHostname(command)
hostname := notifications.GetHostname()
title := notifications.GetTitle(hostname)
Expect(title).To(Equal("Watchtower updates on test.host"))
})
@ -54,8 +56,9 @@ var _ = Describe("notifications", func() {
It("should use the default delay", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
flags.BindViperFlags(command)
delay := notifications.GetDelay(command, time.Duration(0))
delay := notifications.GetDelay(time.Duration(0))
Expect(delay).To(Equal(time.Duration(0)))
})
})
@ -63,13 +66,14 @@ var _ = Describe("notifications", func() {
It("should use the specified delay", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
flags.BindViperFlags(command)
err := command.ParseFlags([]string{
"--notifications-delay",
"5",
})
Expect(err).NotTo(HaveOccurred())
delay := notifications.GetDelay(command, time.Duration(0))
delay := notifications.GetDelay(time.Duration(0))
Expect(delay).To(Equal(time.Duration(5) * time.Second))
})
})
@ -77,7 +81,8 @@ var _ = Describe("notifications", func() {
It("should use the specified legacy delay", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
delay := notifications.GetDelay(command, time.Duration(5)*time.Second)
flags.BindViperFlags(command)
delay := notifications.GetDelay(time.Duration(5) * time.Second)
Expect(delay).To(Equal(time.Duration(5) * time.Second))
})
})
@ -85,13 +90,14 @@ var _ = Describe("notifications", func() {
It("should use the specified legacy delay and ignore the specified delay", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
flags.BindViperFlags(command)
err := command.ParseFlags([]string{
"--notifications-delay",
"0",
})
Expect(err).NotTo(HaveOccurred())
delay := notifications.GetDelay(command, time.Duration(7)*time.Second)
delay := notifications.GetDelay(time.Duration(7) * time.Second)
Expect(delay).To(Equal(time.Duration(7) * time.Second))
})
})
@ -100,9 +106,6 @@ var _ = Describe("notifications", func() {
// builderFn := notifications.NewSlackNotifier
When("passing a discord url to the slack notifier", func() {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags()
channel := "123456789"
token := "abvsihdbau"
color := notifications.ColorInt
@ -135,7 +138,7 @@ var _ = Describe("notifications", func() {
tokenB := "BBBBBBBBB"
tokenC := "123456789123456789123456"
color := url.QueryEscape(notifications.ColorHex)
hostname := notifications.GetHostname(command)
hostname := notifications.GetHostname()
title := url.QueryEscape(notifications.GetTitle(hostname))
iconURL := "https://containrrr.dev/watchtower-sq180.png"
iconEmoji := "whale"
@ -189,9 +192,6 @@ var _ = Describe("notifications", func() {
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()
token := "aaa"
host := "shoutrrr.local"
hostname := notifications.GetHostname()
@ -216,14 +216,11 @@ var _ = Describe("notifications", func() {
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)
hostname := notifications.GetHostname(command)
hostname := notifications.GetHostname()
title := url.QueryEscape(notifications.GetTitle(hostname))
hookURL := fmt.Sprintf("https://outlook.office.com/webhook/%s/IncomingWebhook/%s/%s", tokenA, tokenB, tokenC)
@ -318,16 +315,17 @@ func testURL(args []string, expectedURL string, expectedDelay time.Duration) {
command := cmd.NewRootCommand()
flags.RegisterNotificationFlags(command)
flags.BindViperFlags(command)
err := command.ParseFlags(args)
Expect(err).NotTo(HaveOccurred())
ExpectWithOffset(1, err).NotTo(HaveOccurred())
hostname := notifications.GetHostname(command)
hostname := notifications.GetHostname()
title := notifications.GetTitle(hostname)
urls, delay := notifications.AppendLegacyUrls([]string{}, command, title)
urls, delay := notifications.AppendLegacyUrls([]string{}, title)
Expect(err).NotTo(HaveOccurred())
ExpectWithOffset(1, err).NotTo(HaveOccurred())
Expect(urls).To(ContainElement(expectedURL))
Expect(delay).To(Equal(expectedDelay))
ExpectWithOffset(1, urls).To(ContainElement(expectedURL))
ExpectWithOffset(1, delay).To(Equal(expectedDelay))
}

View file

@ -8,7 +8,7 @@ import (
t "github.com/containrrr/watchtower/pkg/types"
"github.com/johntdyer/slackrus"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
@ -19,14 +19,12 @@ type slackTypeNotifier struct {
slackrus.SlackrusHook
}
func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
flags := c.PersistentFlags()
hookURL, _ := flags.GetString("notification-slack-hook-url")
userName, _ := flags.GetString("notification-slack-identifier")
channel, _ := flags.GetString("notification-slack-channel")
emoji, _ := flags.GetString("notification-slack-icon-emoji")
iconURL, _ := flags.GetString("notification-slack-icon-url")
func newSlackNotifier() t.ConvertibleNotifier {
hookURL := viper.GetString("notification-slack-hook-url")
userName := viper.GetString("notification-slack-identifier")
channel := viper.GetString("notification-slack-channel")
emoji := viper.GetString("notification-slack-icon-emoji")
iconURL := viper.GetString("notification-slack-icon-url")
n := &slackTypeNotifier{
SlackrusHook: slackrus.SlackrusHook{
@ -35,13 +33,13 @@ func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
Channel: channel,
IconEmoji: emoji,
IconURL: iconURL,
AcceptedLevels: acceptedLogLevels,
AcceptedLevels: []log.Level{},
},
}
return n
}
func (s *slackTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
func (s *slackTypeNotifier) GetURL(title string) (string, error) {
trimmedURL := strings.TrimRight(s.HookURL, "/")
trimmedURL = strings.TrimLeft(trimmedURL, "https://")
parts := strings.Split(trimmedURL, "/")

View file

@ -1,16 +1,15 @@
package types
import (
"github.com/spf13/cobra"
"time"
)
// ConvertibleNotifier is a notifier capable of creating a shoutrrr URL
type ConvertibleNotifier interface {
GetURL(c *cobra.Command, title string) (string, error)
GetURL(title string) (string, error)
}
// DelayNotifier is a notifier that might need to be delayed before sending notifications
type DelayNotifier interface {
GetDelay() time.Duration
}
}