mirror of
https://github.com/containrrr/watchtower.git
synced 2025-09-21 21:30:48 +02:00
const flags
This commit is contained in:
parent
1165f31ca0
commit
f198473714
14 changed files with 607 additions and 492 deletions
31
cmd/root.go
31
cmd/root.go
|
@ -2,6 +2,7 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/containrrr/watchtower/internal/config"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -11,7 +12,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containrrr/watchtower/internal/actions"
|
"github.com/containrrr/watchtower/internal/actions"
|
||||||
"github.com/containrrr/watchtower/internal/flags"
|
|
||||||
"github.com/containrrr/watchtower/internal/meta"
|
"github.com/containrrr/watchtower/internal/meta"
|
||||||
"github.com/containrrr/watchtower/pkg/api"
|
"github.com/containrrr/watchtower/pkg/api"
|
||||||
apiMetrics "github.com/containrrr/watchtower/pkg/api/metrics"
|
apiMetrics "github.com/containrrr/watchtower/pkg/api/metrics"
|
||||||
|
@ -31,7 +31,7 @@ import (
|
||||||
var (
|
var (
|
||||||
client container.Client
|
client container.Client
|
||||||
notifier t.Notifier
|
notifier t.Notifier
|
||||||
c flags.WatchConfig
|
c config.WatchConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
var rootCmd = NewRootCommand()
|
var rootCmd = NewRootCommand()
|
||||||
|
@ -51,11 +51,10 @@ func NewRootCommand() *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flags.RegisterDockerFlags(rootCmd)
|
config.RegisterDockerOptions(rootCmd)
|
||||||
flags.RegisterSystemFlags(rootCmd)
|
config.RegisterSystemOptions(rootCmd)
|
||||||
flags.RegisterNotificationFlags(rootCmd)
|
config.RegisterNotificationOptions(rootCmd)
|
||||||
flags.SetEnvBindings()
|
config.BindViperFlags(rootCmd)
|
||||||
flags.BindViperFlags(rootCmd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the root func and exit in case of errors
|
// Execute the root func and exit in case of errors
|
||||||
|
@ -69,7 +68,7 @@ func Execute() {
|
||||||
func PreRun(_ *cobra.Command, _ []string) {
|
func PreRun(_ *cobra.Command, _ []string) {
|
||||||
|
|
||||||
// First apply all the settings that affect the output
|
// First apply all the settings that affect the output
|
||||||
if viper.GetBool("no-color") {
|
if config.GetBool(config.NoColor) {
|
||||||
log.SetFormatter(&log.TextFormatter{
|
log.SetFormatter(&log.TextFormatter{
|
||||||
DisableColors: true,
|
DisableColors: true,
|
||||||
})
|
})
|
||||||
|
@ -80,19 +79,19 @@ func PreRun(_ *cobra.Command, _ []string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if viper.GetBool("debug") {
|
if config.GetBool(config.Debug) {
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
}
|
}
|
||||||
if viper.GetBool("trace") {
|
if config.GetBool(config.Trace) {
|
||||||
log.SetLevel(log.TraceLevel)
|
log.SetLevel(log.TraceLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
interval := viper.GetInt("interval")
|
interval := config.GetInt(config.Interval)
|
||||||
|
|
||||||
// If empty, set schedule using interval helper value
|
// If empty, set schedule using interval helper value
|
||||||
if viper.GetString("schedule") == "" {
|
if config.GetString(config.Schedule) == "" {
|
||||||
viper.Set("schedule", fmt.Sprintf("@every %ds", interval))
|
viper.Set(string(config.Schedule), fmt.Sprintf("@every %ds", interval))
|
||||||
} else if interval != flags.DefaultInterval {
|
} else if interval != config.DefaultInterval {
|
||||||
log.Fatal("only schedule or interval can be defined, not both")
|
log.Fatal("only schedule or interval can be defined, not both")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +101,7 @@ func PreRun(_ *cobra.Command, _ []string) {
|
||||||
log.Fatalf("unable to decode into struct, %v", err)
|
log.Fatalf("unable to decode into struct, %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
flags.GetSecretsFromFiles()
|
config.GetSecretsFromFiles()
|
||||||
|
|
||||||
if c.Timeout <= 0 {
|
if c.Timeout <= 0 {
|
||||||
log.Fatal("Please specify a positive value for timeout value.")
|
log.Fatal("Please specify a positive value for timeout value.")
|
||||||
|
@ -110,7 +109,7 @@ func PreRun(_ *cobra.Command, _ []string) {
|
||||||
|
|
||||||
log.Debugf("Using scope %v", c.Scope)
|
log.Debugf("Using scope %v", c.Scope)
|
||||||
|
|
||||||
if err = flags.EnvConfig(); err != nil {
|
if err = config.EnvConfig(); err != nil {
|
||||||
log.Fatalf("failed to setup environment variables: %v", err)
|
log.Fatalf("failed to setup environment variables: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
89
internal/config/config.go
Normal file
89
internal/config/config.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BindViperFlags binds the cmd PFlags to the viper configuration
|
||||||
|
func BindViperFlags(cmd *cobra.Command) {
|
||||||
|
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
|
||||||
|
log.Fatalf("failed to bind flags: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnvConfig translates the command-line options into environment variables
|
||||||
|
// that will initialize the api client
|
||||||
|
func EnvConfig() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
host := GetString(DockerHost)
|
||||||
|
tls := GetBool(DockerTlSVerify)
|
||||||
|
version := GetString(DockerApiVersion)
|
||||||
|
if err = setEnvOptStr("DOCKER_HOST", host); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = setEnvOptBool("DOCKER_TLS_VERIFY", tls); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = setEnvOptStr("DOCKER_API_VERSION", version); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setEnvOptStr(env string, opt string) error {
|
||||||
|
if opt == "" || opt == os.Getenv(env) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := os.Setenv(env, opt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setEnvOptBool(env string, opt bool) error {
|
||||||
|
if opt {
|
||||||
|
return setEnvOptStr(env, "1")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSecretsFromFiles checks if passwords/tokens/webhooks have been passed as a file instead of plaintext.
|
||||||
|
// If so, the value of the flag will be replaced with the contents of the file.
|
||||||
|
func GetSecretsFromFiles() {
|
||||||
|
secrets := []string{
|
||||||
|
string(NotificationEmailServerPassword),
|
||||||
|
string(NotificationSlackHookUrl),
|
||||||
|
string(NotificationMsteamsHook),
|
||||||
|
string(NotificationGotifyToken),
|
||||||
|
}
|
||||||
|
for _, secret := range secrets {
|
||||||
|
getSecretFromFile(secret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSecretFromFile will check if the flag contains a reference to a file; if it does, replaces the value of the flag with the contents of the file.
|
||||||
|
func getSecretFromFile(secret string) {
|
||||||
|
value := viper.GetString(secret)
|
||||||
|
if value != "" && isFile(value) {
|
||||||
|
file, err := ioutil.ReadFile(value)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
viper.Set(secret, strings.TrimSpace(string(file)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFile(s string) bool {
|
||||||
|
_, err := os.Stat(s)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package flags
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
func TestEnvConfig_Defaults(t *testing.T) {
|
func TestEnvConfig_Defaults(t *testing.T) {
|
||||||
cmd := new(cobra.Command)
|
cmd := new(cobra.Command)
|
||||||
RegisterDockerFlags(cmd)
|
RegisterDockerOptions(cmd)
|
||||||
BindViperFlags(cmd)
|
BindViperFlags(cmd)
|
||||||
|
|
||||||
err := EnvConfig()
|
err := EnvConfig()
|
||||||
|
@ -27,7 +27,7 @@ func TestEnvConfig_Defaults(t *testing.T) {
|
||||||
|
|
||||||
func TestEnvConfig_Custom(t *testing.T) {
|
func TestEnvConfig_Custom(t *testing.T) {
|
||||||
cmd := new(cobra.Command)
|
cmd := new(cobra.Command)
|
||||||
RegisterDockerFlags(cmd)
|
RegisterDockerOptions(cmd)
|
||||||
BindViperFlags(cmd)
|
BindViperFlags(cmd)
|
||||||
|
|
||||||
err := cmd.ParseFlags([]string{"--host", "some-custom-docker-host", "--tlsverify", "--api-version", "1.99"})
|
err := cmd.ParseFlags([]string{"--host", "some-custom-docker-host", "--tlsverify", "--api-version", "1.99"})
|
||||||
|
@ -75,9 +75,9 @@ func TestGetSecretsFromFilesWithFile(t *testing.T) {
|
||||||
|
|
||||||
func testGetSecretsFromFiles(t *testing.T, flagName string, expected string) {
|
func testGetSecretsFromFiles(t *testing.T, flagName string, expected string) {
|
||||||
cmd := new(cobra.Command)
|
cmd := new(cobra.Command)
|
||||||
RegisterNotificationFlags(cmd)
|
RegisterNotificationOptions(cmd)
|
||||||
BindViperFlags(cmd)
|
BindViperFlags(cmd)
|
||||||
SetEnvBindings()
|
|
||||||
GetSecretsFromFiles()
|
GetSecretsFromFiles()
|
||||||
value := viper.GetString(flagName)
|
value := viper.GetString(flagName)
|
||||||
|
|
||||||
|
@ -87,8 +87,8 @@ func testGetSecretsFromFiles(t *testing.T, flagName string, expected string) {
|
||||||
func TestHTTPAPIPeriodicPollsFlag(t *testing.T) {
|
func TestHTTPAPIPeriodicPollsFlag(t *testing.T) {
|
||||||
cmd := new(cobra.Command)
|
cmd := new(cobra.Command)
|
||||||
|
|
||||||
RegisterDockerFlags(cmd)
|
RegisterDockerOptions(cmd)
|
||||||
RegisterSystemFlags(cmd)
|
RegisterSystemOptions(cmd)
|
||||||
|
|
||||||
err := cmd.ParseFlags([]string{"--http-api-periodic-polls"})
|
err := cmd.ParseFlags([]string{"--http-api-periodic-polls"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -98,3 +98,32 @@ func TestHTTPAPIPeriodicPollsFlag(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, true, periodicPolls)
|
assert.Equal(t, true, periodicPolls)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEnvVariablesMapToFlags(t *testing.T) {
|
||||||
|
|
||||||
|
viper.Reset()
|
||||||
|
cmd := new(cobra.Command)
|
||||||
|
|
||||||
|
RegisterDockerOptions(cmd)
|
||||||
|
RegisterSystemOptions(cmd)
|
||||||
|
RegisterNotificationOptions(cmd)
|
||||||
|
BindViperFlags(cmd)
|
||||||
|
|
||||||
|
//for _, opt := range stringConfOpts {
|
||||||
|
// value := opt.key
|
||||||
|
// assert.Nil(t, os.Setenv(opt.env, value))
|
||||||
|
// assert.Equal(t, value, viper.GetString(opt.key))
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//for _, opt := range intConfOpts {
|
||||||
|
// value := len(opt.key)
|
||||||
|
// assert.Nil(t, os.Setenv(opt.env, fmt.Sprint(value)))
|
||||||
|
// assert.Equal(t, value, viper.GetInt(opt.key))
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//for _, opt := range boolConfOpts {
|
||||||
|
// assert.Nil(t, os.Setenv(opt.env, fmt.Sprint(true)))
|
||||||
|
// assert.True(t, viper.GetBool(opt.key))
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package flags
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
78
internal/config/keys.go
Normal file
78
internal/config/keys.go
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
type stringConfKey string
|
||||||
|
type boolConfKey string
|
||||||
|
type intConfKey string
|
||||||
|
type durationConfKey string
|
||||||
|
type sliceConfKey string
|
||||||
|
|
||||||
|
//
|
||||||
|
const (
|
||||||
|
NoPull boolConfKey = "no-pull"
|
||||||
|
NoRestart boolConfKey = "no-restart"
|
||||||
|
NoStartupMessage boolConfKey = "no-startup-message"
|
||||||
|
Cleanup boolConfKey = "cleanup"
|
||||||
|
RemoveVolumes boolConfKey = "remove-volumes"
|
||||||
|
LabelEnable boolConfKey = "label-enable"
|
||||||
|
Debug boolConfKey = "debug"
|
||||||
|
Trace boolConfKey = "trace"
|
||||||
|
MonitorOnly boolConfKey = "monitor-only"
|
||||||
|
RunOnce boolConfKey = "run-once"
|
||||||
|
IncludeRestarting boolConfKey = "include-restarting"
|
||||||
|
IncludeStopped boolConfKey = "include-stopped"
|
||||||
|
ReviveStopped boolConfKey = "revive-stopped"
|
||||||
|
EnableLifecycleHooks boolConfKey = "enable-lifecycle-hooks"
|
||||||
|
RollingRestart boolConfKey = "rolling-restart"
|
||||||
|
WarnOnHeadFailure stringConfKey = "warn-on-head-failure"
|
||||||
|
|
||||||
|
HttpApiUpdate boolConfKey = "http-api-update"
|
||||||
|
HttpApiMetrics boolConfKey = "http-api-metrics"
|
||||||
|
HttpApiPeriodicPolls boolConfKey = "http-api-periodic-polls"
|
||||||
|
HttpApiToken stringConfKey = "HttpApiToken"
|
||||||
|
|
||||||
|
NoColor boolConfKey = "no-color"
|
||||||
|
|
||||||
|
NotificationGotifyTlsSkipVerify boolConfKey = "notification-gotify-tls-skip-verify"
|
||||||
|
|
||||||
|
Schedule stringConfKey = "schedule"
|
||||||
|
Interval intConfKey = "interval"
|
||||||
|
|
||||||
|
StopTimeout durationConfKey = "stop-timeout"
|
||||||
|
|
||||||
|
Scope stringConfKey = "Scope"
|
||||||
|
|
||||||
|
/* Docker v*/
|
||||||
|
DockerHost stringConfKey = "host"
|
||||||
|
DockerApiVersion stringConfKey = "api-version"
|
||||||
|
DockerTlSVerify boolConfKey = "tlsverify"
|
||||||
|
|
||||||
|
Notifications sliceConfKey = "notifications"
|
||||||
|
NotificationsLevel stringConfKey = "notifications-level"
|
||||||
|
NotificationsDelay intConfKey = "notifications-delay"
|
||||||
|
NotificationsHostname stringConfKey = "notifications-hostname"
|
||||||
|
NotificationTemplate stringConfKey = "notification-template"
|
||||||
|
NotificationReport boolConfKey = "notification-report"
|
||||||
|
NotificationUrl sliceConfKey = "notification-url"
|
||||||
|
|
||||||
|
NotificationEmailFrom stringConfKey = "notification-email-from"
|
||||||
|
NotificationEmailTo stringConfKey = "notification-email-to"
|
||||||
|
NotificationEmailServer stringConfKey = "notification-email-server"
|
||||||
|
NotificationEmailServerUser stringConfKey = "notification-email-server-user"
|
||||||
|
NotificationEmailServerPassword stringConfKey = "notification-email-server-password"
|
||||||
|
NotificationEmailSubjecttag stringConfKey = "notification-email-subjecttag"
|
||||||
|
NotificationEmailDelay intConfKey = "notification-email-delay"
|
||||||
|
NotificationEmailServerPort intConfKey = "notification-email-server-port"
|
||||||
|
NotificationEmailServerTlsSkipVerify boolConfKey = "notification-email-server-tls-skip-verify"
|
||||||
|
|
||||||
|
NotificationSlackHookUrl stringConfKey = "notification-slack-hook-url"
|
||||||
|
NotificationSlackIdentifier stringConfKey = "notification-slack-identifier"
|
||||||
|
NotificationSlackChannel stringConfKey = "notification-slack-channel"
|
||||||
|
NotificationSlackIconEmoji stringConfKey = "notification-slack-icon-emoji"
|
||||||
|
NotificationSlackIconUrl stringConfKey = "notification-slack-icon-url"
|
||||||
|
|
||||||
|
NotificationMsteamsHook stringConfKey = "notification-msteams-hook"
|
||||||
|
NotificationMsteamsData boolConfKey = "notification-msteams-data"
|
||||||
|
|
||||||
|
NotificationGotifyUrl stringConfKey = "notification-gotify-url"
|
||||||
|
NotificationGotifyToken stringConfKey = "notification-gotify-token"
|
||||||
|
)
|
86
internal/config/legacy.go
Normal file
86
internal/config/legacy.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterLegacyNotificationFlags registers all the flags related to the old notification system
|
||||||
|
func RegisterLegacyNotificationFlags(flags *pflag.FlagSet) {
|
||||||
|
ob := OptBuilder(flags)
|
||||||
|
// Hide all legacy notification flags from the `--help` to reduce clutter
|
||||||
|
ob.Hide = true
|
||||||
|
|
||||||
|
ob.String(NotificationEmailFrom, "",
|
||||||
|
"Address to send notification emails from", "WATCHTOWER_NOTIFICATION_EMAIL_FROM")
|
||||||
|
|
||||||
|
ob.String(NotificationEmailTo, "",
|
||||||
|
"Address to send notification emails to", "WATCHTOWER_NOTIFICATION_EMAIL_TO")
|
||||||
|
|
||||||
|
ob.Int(NotificationEmailDelay, 0,
|
||||||
|
"Delay before sending notifications, expressed in seconds", "WATCHTOWER_NOTIFICATION_EMAIL_DELAY")
|
||||||
|
_ = ob.Flags.MarkDeprecated(string(NotificationEmailDelay),
|
||||||
|
"use "+string(NotificationsDelay)+" instead")
|
||||||
|
|
||||||
|
ob.String(NotificationEmailServer, "",
|
||||||
|
"SMTP server to send notification emails through", "WATCHTOWER_NOTIFICATION_EMAIL_SERVER")
|
||||||
|
|
||||||
|
ob.Int(NotificationEmailServerPort, 25,
|
||||||
|
"SMTP server port to send notification emails through", "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT")
|
||||||
|
|
||||||
|
ob.Bool(NotificationEmailServerTlsSkipVerify, false,
|
||||||
|
`Controls whether watchtower verifies the SMTP server's certificate chain and host name.
|
||||||
|
Should only be used for testing.`,
|
||||||
|
"WATCHTOWER_NOTIFICATION_EMAIL_SERVER_TLS_SKIP_VERIFY")
|
||||||
|
|
||||||
|
ob.String(NotificationEmailServerUser, "",
|
||||||
|
"SMTP server user for sending notifications",
|
||||||
|
"WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER")
|
||||||
|
|
||||||
|
ob.String(NotificationEmailServerPassword, "",
|
||||||
|
"SMTP server password for sending notifications",
|
||||||
|
"WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD")
|
||||||
|
|
||||||
|
ob.String(NotificationEmailSubjecttag, "",
|
||||||
|
"Subject prefix tag for notifications via mail",
|
||||||
|
"WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG")
|
||||||
|
|
||||||
|
ob.String(NotificationSlackHookUrl, "",
|
||||||
|
"The Slack Hook URL to send notifications to",
|
||||||
|
"WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL")
|
||||||
|
|
||||||
|
ob.String(NotificationSlackIdentifier, "watchtower",
|
||||||
|
"A string which will be used to identify the messages coming from this watchtower instance",
|
||||||
|
"WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER")
|
||||||
|
|
||||||
|
ob.String(NotificationSlackChannel, "",
|
||||||
|
"A string which overrides the webhook's default channel. Example: #my-custom-channel",
|
||||||
|
"WATCHTOWER_NOTIFICATION_SLACK_CHANNEL")
|
||||||
|
|
||||||
|
ob.String(NotificationSlackIconEmoji, "",
|
||||||
|
"An emoji code string to use in place of the default icon",
|
||||||
|
"WATCHTOWER_NOTIFICATION_SLACK_ICON_EMOJI")
|
||||||
|
|
||||||
|
ob.String(NotificationSlackIconUrl, "",
|
||||||
|
"An icon image URL string to use in place of the default icon",
|
||||||
|
"WATCHTOWER_NOTIFICATION_SLACK_ICON_URL")
|
||||||
|
|
||||||
|
ob.String(NotificationMsteamsHook, "",
|
||||||
|
"The MSTeams WebHook URL to send notifications to",
|
||||||
|
"WATCHTOWER_NOTIFICATION_MSTEAMS_HOOK_URL")
|
||||||
|
|
||||||
|
ob.Bool(NotificationMsteamsData, false,
|
||||||
|
"The MSTeams notifier will try to extract log entry fields as MSTeams message facts",
|
||||||
|
"WATCHTOWER_NOTIFICATION_MSTEAMS_USE_LOG_DATA")
|
||||||
|
|
||||||
|
ob.String(NotificationGotifyUrl, "",
|
||||||
|
"The Gotify URL to send notifications to", "WATCHTOWER_NOTIFICATION_GOTIFY_URL")
|
||||||
|
|
||||||
|
ob.String(NotificationGotifyToken, "",
|
||||||
|
"The Gotify Application required to query the Gotify API", "WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN")
|
||||||
|
|
||||||
|
ob.Bool(NotificationGotifyTlsSkipVerify, false,
|
||||||
|
`Controls whether watchtower verifies the Gotify server's certificate chain and host name.
|
||||||
|
Should only be used for testing.`,
|
||||||
|
"WATCHTOWER_NOTIFICATION_GOTIFY_TLS_SKIP_VERIFY")
|
||||||
|
|
||||||
|
}
|
87
internal/config/options.go
Normal file
87
internal/config/options.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type optBuilder struct {
|
||||||
|
Flags *pflag.FlagSet
|
||||||
|
Hide bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func OptBuilder(flags *pflag.FlagSet) *optBuilder {
|
||||||
|
return &optBuilder{
|
||||||
|
Flags: flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optBuilder) register(key string, env string) {
|
||||||
|
_ = viper.BindEnv(key, env)
|
||||||
|
if ob.Hide {
|
||||||
|
_ = ob.Flags.MarkHidden(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optBuilder) StringP(key stringConfKey, short string, defaultValue string, usage string, env string) {
|
||||||
|
ob.Flags.StringP(string(key), short, defaultValue, usage)
|
||||||
|
ob.register(string(key), env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optBuilder) BoolP(key boolConfKey, short string, defaultValue bool, usage string, env string) {
|
||||||
|
ob.Flags.BoolP(string(key), short, defaultValue, usage)
|
||||||
|
ob.register(string(key), env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optBuilder) IntP(key intConfKey, short string, defaultValue int, usage string, env string) {
|
||||||
|
ob.Flags.IntP(string(key), short, defaultValue, usage)
|
||||||
|
ob.register(string(key), env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optBuilder) DurationP(key durationConfKey, short string, defaultValue time.Duration, usage string, env string) {
|
||||||
|
ob.Flags.DurationP(string(key), short, defaultValue, usage)
|
||||||
|
ob.register(string(key), env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optBuilder) String(key stringConfKey, defaultValue string, usage string, env string) {
|
||||||
|
ob.StringP(key, "", defaultValue, usage, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optBuilder) Bool(key boolConfKey, defaultValue bool, usage string, env string) {
|
||||||
|
ob.BoolP(key, "", defaultValue, usage, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optBuilder) Int(key intConfKey, defaultValue int, usage string, env string) {
|
||||||
|
ob.IntP(key, "", defaultValue, usage, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optBuilder) StringArray(key sliceConfKey, defaultValue []string, usage string, env string) {
|
||||||
|
ob.Flags.StringArray(string(key), defaultValue, usage)
|
||||||
|
ob.register(string(key), env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optBuilder) StringSliceP(key sliceConfKey, short string, defaultValue []string, usage string, env string) {
|
||||||
|
ob.Flags.StringSliceP(string(key), short, defaultValue, usage)
|
||||||
|
ob.register(string(key), env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetString(key stringConfKey) string {
|
||||||
|
return viper.GetString(string(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBool(key boolConfKey) bool {
|
||||||
|
return viper.GetBool(string(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInt(key intConfKey) int {
|
||||||
|
return viper.GetInt(string(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDuration(key durationConfKey) time.Duration {
|
||||||
|
return viper.GetDuration(string(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSlice(key sliceConfKey) []string {
|
||||||
|
return viper.GetStringSlice(string(key))
|
||||||
|
}
|
179
internal/config/reg_options.go
Normal file
179
internal/config/reg_options.go
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DockerAPIMinVersion is the minimum version of the docker api required to
|
||||||
|
// use watchtower
|
||||||
|
const DockerAPIMinVersion string = "1.25"
|
||||||
|
|
||||||
|
// DefaultInterval is the default time between the start of update checks
|
||||||
|
const DefaultInterval = int(time.Hour * 24 / time.Second)
|
||||||
|
|
||||||
|
// RegisterDockerOptions that are used directly by the docker api client
|
||||||
|
func RegisterDockerOptions(rootCmd *cobra.Command) {
|
||||||
|
ob := OptBuilder(rootCmd.PersistentFlags())
|
||||||
|
|
||||||
|
ob.StringP(DockerHost, "H", "unix:///var/run/docker.sock",
|
||||||
|
"daemon socket to connect to",
|
||||||
|
"DOCKER_HOST")
|
||||||
|
|
||||||
|
ob.BoolP(DockerTlSVerify, "v", false,
|
||||||
|
"use TLS and verify the remote",
|
||||||
|
"DOCKER_TLS_VERIFY")
|
||||||
|
|
||||||
|
ob.StringP(DockerApiVersion, "a", DockerAPIMinVersion,
|
||||||
|
"api version to use by docker client",
|
||||||
|
"DOCKER_API_VERSION")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterSystemOptions that are used by watchtower to modify the program flow
|
||||||
|
func RegisterSystemOptions(rootCmd *cobra.Command) {
|
||||||
|
ob := OptBuilder(rootCmd.PersistentFlags())
|
||||||
|
|
||||||
|
ob.IntP(Interval, "i", DefaultInterval,
|
||||||
|
"poll interval (in seconds)",
|
||||||
|
"WATCHTOWER_POLL_INTERVAL")
|
||||||
|
|
||||||
|
ob.StringP(Schedule, "s", "",
|
||||||
|
"The cron expression which defines when to update",
|
||||||
|
"WATCHTOWER_SCHEDULE")
|
||||||
|
|
||||||
|
ob.DurationP(StopTimeout, "t", time.Second*10,
|
||||||
|
"Timeout before a container is forcefully stopped",
|
||||||
|
"WATCHTOWER_TIMEOUT")
|
||||||
|
|
||||||
|
ob.Bool(NoPull, false,
|
||||||
|
"Do not pull any new images",
|
||||||
|
"WATCHTOWER_NO_PULL")
|
||||||
|
|
||||||
|
ob.Bool(NoRestart, false,
|
||||||
|
"Do not restart any containers",
|
||||||
|
"WATCHTOWER_NO_RESTART")
|
||||||
|
|
||||||
|
ob.Bool(NoStartupMessage, false,
|
||||||
|
"Prevents watchtower from sending a startup message",
|
||||||
|
"WATCHTOWER_NO_STARTUP_MESSAGE")
|
||||||
|
|
||||||
|
ob.BoolP(Cleanup, "c", false,
|
||||||
|
"Remove previously used images after updating",
|
||||||
|
"WATCHTOWER_CLEANUP")
|
||||||
|
|
||||||
|
ob.BoolP(RemoveVolumes,
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
"Remove attached volumes before updating",
|
||||||
|
"WATCHTOWER_REMOVE_VOLUMES")
|
||||||
|
|
||||||
|
ob.BoolP(LabelEnable,
|
||||||
|
"e",
|
||||||
|
false,
|
||||||
|
"Watch containers where the com.centurylinklabs.watchtower.enable label is true",
|
||||||
|
"WATCHTOWER_LABEL_ENABLE")
|
||||||
|
|
||||||
|
ob.BoolP(Debug,
|
||||||
|
"d",
|
||||||
|
false,
|
||||||
|
"Enable debug mode with verbose logging",
|
||||||
|
"WATCHTOWER_DEBUG")
|
||||||
|
|
||||||
|
ob.Bool(Trace,
|
||||||
|
false,
|
||||||
|
"Enable trace mode with very verbose logging - caution, exposes credentials",
|
||||||
|
"WATCHTOWER_TRACE")
|
||||||
|
|
||||||
|
ob.BoolP(MonitorOnly, "m", false,
|
||||||
|
"Will only monitor for new images, not update the containers",
|
||||||
|
"WATCHTOWER_MONITOR_ONLY")
|
||||||
|
|
||||||
|
ob.BoolP(RunOnce, "R", false,
|
||||||
|
"Run once now and exit",
|
||||||
|
"WATCHTOWER_RUN_ONCE")
|
||||||
|
|
||||||
|
ob.BoolP(IncludeRestarting, "", false,
|
||||||
|
"Will also include restarting containers",
|
||||||
|
"WATCHTOWER_INCLUDE_RESTARTING")
|
||||||
|
|
||||||
|
ob.BoolP(IncludeStopped, "S", false,
|
||||||
|
"Will also include created and exited containers",
|
||||||
|
"WATCHTOWER_INCLUDE_STOPPED")
|
||||||
|
|
||||||
|
ob.Bool(ReviveStopped, false,
|
||||||
|
"Will also start stopped containers that were updated, if include-stopped is active",
|
||||||
|
"WATCHTOWER_REVIVE_STOPPED")
|
||||||
|
|
||||||
|
ob.Bool(EnableLifecycleHooks, false,
|
||||||
|
"Enable the execution of commands triggered by pre- and post-update lifecycle hooks",
|
||||||
|
"WATCHTOWER_LIFECYCLE_HOOKS")
|
||||||
|
|
||||||
|
ob.Bool(RollingRestart, false,
|
||||||
|
"Restart containers one at a time",
|
||||||
|
"WATCHTOWER_ROLLING_RESTART")
|
||||||
|
|
||||||
|
ob.Bool(HttpApiUpdate, false,
|
||||||
|
"Runs Watchtower in HTTP API mode, so that image updates must to be triggered by a request",
|
||||||
|
"WATCHTOWER_HTTP_API_UPDATE")
|
||||||
|
|
||||||
|
ob.Bool(HttpApiMetrics, false,
|
||||||
|
"Runs Watchtower with the Prometheus metrics API enabled",
|
||||||
|
"WATCHTOWER_HTTP_API_METRICS")
|
||||||
|
|
||||||
|
ob.String(HttpApiToken, "",
|
||||||
|
"Sets an authentication token to HTTP API requests.",
|
||||||
|
"WATCHTOWER_HTTP_API_TOKEN")
|
||||||
|
|
||||||
|
ob.Bool(HttpApiPeriodicPolls, false,
|
||||||
|
"Also run periodic updates (specified with --interval and --schedule) if HTTP API is enabled",
|
||||||
|
"WATCHTOWER_HTTP_API_PERIODIC_POLLS")
|
||||||
|
|
||||||
|
// https://no-color.org/
|
||||||
|
ob.Bool(NoColor, false,
|
||||||
|
"Disable ANSI color escape codes in log output",
|
||||||
|
"NO_COLOR")
|
||||||
|
|
||||||
|
ob.String(Scope, "",
|
||||||
|
"Defines a monitoring scope for the Watchtower instance.",
|
||||||
|
"WATCHTOWER_SCOPE")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterNotificationOptions that are used by watchtower to send notifications
|
||||||
|
func RegisterNotificationOptions(cmd *cobra.Command) {
|
||||||
|
ob := OptBuilder(cmd.PersistentFlags())
|
||||||
|
|
||||||
|
ob.StringSliceP(Notifications, "n", []string{},
|
||||||
|
" Notification types to send (valid: email, slack, msteams, gotify, shoutrrr)",
|
||||||
|
"WATCHTOWER_NOTIFICATIONS")
|
||||||
|
|
||||||
|
ob.String(NotificationsLevel, "info",
|
||||||
|
"The log level used for sending notifications. Possible values: panic, fatal, error, warn, info or debug",
|
||||||
|
"WATCHTOWER_NOTIFICATIONS_LEVEL")
|
||||||
|
|
||||||
|
ob.Int(NotificationsDelay, 0,
|
||||||
|
"Delay before sending notifications, expressed in seconds",
|
||||||
|
"WATCHTOWER_NOTIFICATIONS_DELAY")
|
||||||
|
|
||||||
|
ob.String(NotificationsHostname, "",
|
||||||
|
"Custom hostname for notification titles",
|
||||||
|
"WATCHTOWER_NOTIFICATIONS_HOSTNAME")
|
||||||
|
|
||||||
|
ob.String(NotificationTemplate, "",
|
||||||
|
"The shoutrrr text/template for the messages",
|
||||||
|
"WATCHTOWER_NOTIFICATION_TEMPLATE")
|
||||||
|
|
||||||
|
ob.StringArray(NotificationUrl, []string{},
|
||||||
|
"The shoutrrr URL to send notifications to",
|
||||||
|
"WATCHTOWER_NOTIFICATION_URL")
|
||||||
|
|
||||||
|
ob.Bool(NotificationReport, false,
|
||||||
|
"Use the session report as the notification template data",
|
||||||
|
"WATCHTOWER_NOTIFICATION_REPORT")
|
||||||
|
|
||||||
|
ob.String(WarnOnHeadFailure, "auto",
|
||||||
|
"When to warn about HEAD pull requests failing. Possible values: always, auto or never",
|
||||||
|
"WATCHTOWER_WARN_ON_HEAD_FAILURE")
|
||||||
|
|
||||||
|
RegisterLegacyNotificationFlags(ob.Flags)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package flags
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
@ -8,12 +8,12 @@ import (
|
||||||
type WatchConfig struct {
|
type WatchConfig struct {
|
||||||
Interval int
|
Interval int
|
||||||
Schedule string
|
Schedule string
|
||||||
NoPull bool `mapstructure:"no-pull"`
|
NoPull bool `mapstructure:"no-pull"`
|
||||||
NoRestart bool `mapstructure:"no-restart"`
|
NoRestart bool `mapstructure:"no-restart"`
|
||||||
NoStartupMessage bool `mapstructure:"no-startup-message"`
|
NoStartupMessage bool `mapstructure:"no-startup-message"`
|
||||||
Cleanup bool
|
Cleanup bool
|
||||||
RemoveVolumes bool `mapstructure:"remove-volumes"`
|
RemoveVolumes bool `mapstructure:"remove-volumes"`
|
||||||
EnableLabel bool `mapstructure:"label-enable"`
|
EnableLabel bool `mapstructure:"label-enable"`
|
||||||
Debug bool
|
Debug bool
|
||||||
Trace bool
|
Trace bool
|
||||||
MonitorOnly bool `mapstructure:"monitor-only"`
|
MonitorOnly bool `mapstructure:"monitor-only"`
|
||||||
|
@ -25,10 +25,10 @@ type WatchConfig struct {
|
||||||
RollingRestart bool `mapstructure:"rolling-restart"`
|
RollingRestart bool `mapstructure:"rolling-restart"`
|
||||||
HTTPAPIToken string `mapstructure:"http-api-token"`
|
HTTPAPIToken string `mapstructure:"http-api-token"`
|
||||||
Scope string
|
Scope string
|
||||||
EnableUpdateAPI bool `mapstructure:"http-api-update"`
|
EnableUpdateAPI bool `mapstructure:"http-api-update"`
|
||||||
EnableMetricsAPI bool `mapstructure:"http-api-metrics"`
|
EnableMetricsAPI bool `mapstructure:"http-api-metrics"`
|
||||||
UpdateAPIWithScheduler bool `mapstructure:"http-api-periodic-polls"`
|
UpdateAPIWithScheduler bool `mapstructure:"http-api-periodic-polls"`
|
||||||
WarnOnHeadFailed string `mapstructure:"warn-on-head-failure"`
|
WarnOnHeadFailed string `mapstructure:"warn-on-head-failure"`
|
||||||
NoColor bool `mapstructure:"no-color"`
|
NoColor bool `mapstructure:"no-color"`
|
||||||
Timeout time.Duration `mapstructure:"stop-timeout"`
|
Timeout time.Duration `mapstructure:"stop-timeout"`
|
||||||
}
|
}
|
|
@ -1,77 +0,0 @@
|
||||||
package flags
|
|
||||||
|
|
||||||
import "github.com/spf13/pflag"
|
|
||||||
|
|
||||||
// RegisterLegacyNotificationFlags registers all the flags related to the old notification system
|
|
||||||
func RegisterLegacyNotificationFlags(flags *pflag.FlagSet) {
|
|
||||||
depFlags := NewDeprecator(flags, "use notification-url instead")
|
|
||||||
depFlags.Deprecate = false
|
|
||||||
|
|
||||||
depFlags.Prefix = "notification-email-"
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_FROM"),
|
|
||||||
depFlags.String("from", "", "Address to send notification emails from")
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_TO"),
|
|
||||||
depFlags.String("to", "", "Address to send notification emails to")
|
|
||||||
|
|
||||||
//viper.GetInt("WATCHTOWER_NOTIFICATION_EMAIL_DELAY"),
|
|
||||||
depFlags.Int("delay", 0, "Delay before sending notifications, expressed in seconds")
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_SERVER"),
|
|
||||||
depFlags.String("server", "", "SMTP server to send notification emails through")
|
|
||||||
|
|
||||||
// viper.GetInt("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT"),
|
|
||||||
depFlags.Int("server-port", 25, "SMTP server port to send notification emails through")
|
|
||||||
|
|
||||||
// viper.GetBool("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_TLS_SKIP_VERIFY"),
|
|
||||||
depFlags.Bool("server-tls-skip-verify", false, `Controls whether watchtower verifies the SMTP server's certificate chain and host name.
|
|
||||||
Should only be used for testing.`)
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER"),
|
|
||||||
depFlags.String("server-user", "", "SMTP server user for sending notifications")
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD"),
|
|
||||||
depFlags.String("server-password", "", "SMTP server password for sending notifications")
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG"),
|
|
||||||
depFlags.String("subjecttag", "", "Subject prefix tag for notifications via mail")
|
|
||||||
|
|
||||||
depFlags.Prefix = "notification-slack-"
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL"),
|
|
||||||
depFlags.String("hook-url", "", "The Slack Hook URL to send notifications to")
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER"),
|
|
||||||
depFlags.String("identifier", "watchtower", "A string which will be used to identify the messages coming from this watchtower instance")
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_SLACK_CHANNEL"),
|
|
||||||
depFlags.String("channel", "", "A string which overrides the webhook's default channel. Example: #my-custom-channel")
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_SLACK_ICON_EMOJI"),
|
|
||||||
depFlags.String("icon-emoji", "", "An emoji code string to use in place of the default icon")
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_SLACK_ICON_URL"),
|
|
||||||
depFlags.String("icon-url", "", "An icon image URL string to use in place of the default icon")
|
|
||||||
|
|
||||||
depFlags.Prefix = "notification-msteams-"
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_MSTEAMS_HOOK_URL"),
|
|
||||||
depFlags.String("hook", "", "The MSTeams WebHook URL to send notifications to")
|
|
||||||
|
|
||||||
// viper.GetBool("WATCHTOWER_NOTIFICATION_MSTEAMS_USE_LOG_DATA"),
|
|
||||||
depFlags.Bool("data", false, "The MSTeams notifier will try to extract log entry fields as MSTeams message facts")
|
|
||||||
|
|
||||||
depFlags.Prefix = "notification-gotify-"
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_GOTIFY_URL"),
|
|
||||||
depFlags.String("url", "", "The Gotify URL to send notifications to")
|
|
||||||
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN"),
|
|
||||||
depFlags.String("token", "", "The Gotify Application required to query the Gotify API")
|
|
||||||
|
|
||||||
// viper.GetBool("WATCHTOWER_NOTIFICATION_GOTIFY_TLS_SKIP_VERIFY"),
|
|
||||||
depFlags.Bool("tls-skip-verify", false, `Controls whether watchtower verifies the Gotify server's certificate chain and host name.
|
|
||||||
Should only be used for testing.`)
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,330 +0,0 @@
|
||||||
package flags
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DockerAPIMinVersion is the minimum version of the docker api required to
|
|
||||||
// use watchtower
|
|
||||||
const DockerAPIMinVersion string = "1.25"
|
|
||||||
|
|
||||||
// DefaultInterval is the default time between the start of update checks
|
|
||||||
const DefaultInterval = int(time.Hour * 24 / time.Second)
|
|
||||||
|
|
||||||
// RegisterDockerFlags that are used directly by the docker api client
|
|
||||||
func RegisterDockerFlags(rootCmd *cobra.Command) {
|
|
||||||
flags := rootCmd.PersistentFlags()
|
|
||||||
flags.StringP("host", "H", "unix:///var/run/docker.sock", "daemon socket to connect to")
|
|
||||||
flags.BoolP("tlsverify", "v", false, "use TLS and verify the remote")
|
|
||||||
flags.StringP("api-version", "a", DockerAPIMinVersion, "api version to use by docker client")
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterSystemFlags that are used by watchtower to modify the program flow
|
|
||||||
func RegisterSystemFlags(rootCmd *cobra.Command) {
|
|
||||||
flags := rootCmd.PersistentFlags()
|
|
||||||
flags.IntP(
|
|
||||||
"interval",
|
|
||||||
"i",
|
|
||||||
DefaultInterval, // viper.GetInt("WATCHTOWER_POLL_INTERVAL"),
|
|
||||||
"poll interval (in seconds)")
|
|
||||||
|
|
||||||
flags.StringP(
|
|
||||||
"schedule",
|
|
||||||
"s",
|
|
||||||
"",
|
|
||||||
"The cron expression which defines when to update")
|
|
||||||
//viper.GetString("WATCHTOWER_SCHEDULE"),
|
|
||||||
|
|
||||||
flags.DurationP(
|
|
||||||
"stop-timeout",
|
|
||||||
"t",
|
|
||||||
time.Second*10, //viper.GetDuration("WATCHTOWER_TIMEOUT"),
|
|
||||||
"Timeout before a container is forcefully stopped")
|
|
||||||
|
|
||||||
flags.BoolP(
|
|
||||||
"no-pull",
|
|
||||||
"",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_NO_PULL"),
|
|
||||||
"Do not pull any new images")
|
|
||||||
|
|
||||||
flags.Bool(
|
|
||||||
"no-restart",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_NO_RESTART"),
|
|
||||||
"Do not restart any containers")
|
|
||||||
|
|
||||||
flags.Bool(
|
|
||||||
"no-startup-message",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_NO_STARTUP_MESSAGE"),
|
|
||||||
"Prevents watchtower from sending a startup message")
|
|
||||||
|
|
||||||
flags.BoolP(
|
|
||||||
"cleanup",
|
|
||||||
"c",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_CLEANUP"),
|
|
||||||
"Remove previously used images after updating")
|
|
||||||
|
|
||||||
flags.BoolP(
|
|
||||||
"remove-volumes",
|
|
||||||
"",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_REMOVE_VOLUMES"),
|
|
||||||
"Remove attached volumes before updating")
|
|
||||||
|
|
||||||
flags.BoolP(
|
|
||||||
"label-enable",
|
|
||||||
"e",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_LABEL_ENABLE"),
|
|
||||||
"Watch containers where the com.centurylinklabs.watchtower.enable label is true")
|
|
||||||
|
|
||||||
flags.BoolP(
|
|
||||||
"debug",
|
|
||||||
"d",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_DEBUG"),
|
|
||||||
"Enable debug mode with verbose logging")
|
|
||||||
|
|
||||||
flags.Bool(
|
|
||||||
"trace",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_TRACE"),
|
|
||||||
"Enable trace mode with very verbose logging - caution, exposes credentials")
|
|
||||||
|
|
||||||
flags.BoolP(
|
|
||||||
"monitor-only",
|
|
||||||
"m",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_MONITOR_ONLY"),
|
|
||||||
"Will only monitor for new images, not update the containers")
|
|
||||||
|
|
||||||
flags.BoolP(
|
|
||||||
"run-once",
|
|
||||||
"R",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_RUN_ONCE"),
|
|
||||||
"Run once now and exit")
|
|
||||||
|
|
||||||
flags.BoolP(
|
|
||||||
"include-restarting",
|
|
||||||
"",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_INCLUDE_RESTARTING"),
|
|
||||||
"Will also include restarting containers")
|
|
||||||
|
|
||||||
flags.BoolP(
|
|
||||||
"include-stopped",
|
|
||||||
"S",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_INCLUDE_STOPPED"),
|
|
||||||
"Will also include created and exited containers")
|
|
||||||
|
|
||||||
flags.Bool(
|
|
||||||
"revive-stopped",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_REVIVE_STOPPED"),
|
|
||||||
"Will also start stopped containers that were updated, if include-stopped is active")
|
|
||||||
|
|
||||||
flags.Bool(
|
|
||||||
"enable-lifecycle-hooks",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_LIFECYCLE_HOOKS"),
|
|
||||||
"Enable the execution of commands triggered by pre- and post-update lifecycle hooks")
|
|
||||||
|
|
||||||
flags.Bool(
|
|
||||||
"rolling-restart",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_ROLLING_RESTART"),
|
|
||||||
"Restart containers one at a time")
|
|
||||||
|
|
||||||
flags.Bool(
|
|
||||||
"http-api-update",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_HTTP_API_UPDATE"),
|
|
||||||
"Runs Watchtower in HTTP API mode, so that image updates must to be triggered by a request")
|
|
||||||
flags.Bool(
|
|
||||||
"http-api-metrics",
|
|
||||||
false, // viper.GetBool("WATCHTOWER_HTTP_API_METRICS"),
|
|
||||||
"Runs Watchtower with the Prometheus metrics API enabled")
|
|
||||||
|
|
||||||
flags.String(
|
|
||||||
"http-api-token",
|
|
||||||
"", // viper.GetString("WATCHTOWER_HTTP_API_TOKEN"),
|
|
||||||
"Sets an authentication token to HTTP API requests.")
|
|
||||||
flags.BoolP(
|
|
||||||
"http-api-periodic-polls",
|
|
||||||
"",
|
|
||||||
viper.GetBool("WATCHTOWER_HTTP_API_PERIODIC_POLLS"),
|
|
||||||
"Also run periodic updates (specified with --interval and --schedule) if HTTP API is enabled")
|
|
||||||
// https://no-color.org/
|
|
||||||
flags.BoolP(
|
|
||||||
"no-color",
|
|
||||||
"",
|
|
||||||
false, // viper.IsSet("NO_COLOR"),
|
|
||||||
"Disable ANSI color escape codes in log output")
|
|
||||||
flags.String(
|
|
||||||
"scope",
|
|
||||||
"", // viper.GetString("WATCHTOWER_SCOPE"),
|
|
||||||
"Defines a monitoring scope for the Watchtower instance.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterNotificationFlags that are used by watchtower to send notifications
|
|
||||||
func RegisterNotificationFlags(rootCmd *cobra.Command) {
|
|
||||||
flags := rootCmd.PersistentFlags()
|
|
||||||
|
|
||||||
flags.StringSliceP(
|
|
||||||
"notifications",
|
|
||||||
"n",
|
|
||||||
[]string{}, // viper.GetStringSlice("WATCHTOWER_NOTIFICATIONS"),
|
|
||||||
" Notification types to send (valid: email, slack, msteams, gotify, shoutrrr)")
|
|
||||||
|
|
||||||
flags.String(
|
|
||||||
"notifications-level",
|
|
||||||
"info", // viper.GetString("WATCHTOWER_NOTIFICATIONS_LEVEL"),
|
|
||||||
"The log level used for sending notifications. Possible values: panic, fatal, error, warn, info or debug")
|
|
||||||
|
|
||||||
flags.Int(
|
|
||||||
"notifications-delay",
|
|
||||||
0, // viper.GetInt("WATCHTOWER_NOTIFICATIONS_DELAY"),
|
|
||||||
"Delay before sending notifications, expressed in seconds")
|
|
||||||
|
|
||||||
flags.String(
|
|
||||||
"notifications-hostname",
|
|
||||||
"",
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATIONS_HOSTNAME"),
|
|
||||||
"Custom hostname for notification titles")
|
|
||||||
|
|
||||||
flags.String(
|
|
||||||
"notification-template",
|
|
||||||
"",
|
|
||||||
// viper.GetString("WATCHTOWER_NOTIFICATION_TEMPLATE"),
|
|
||||||
"The shoutrrr text/template for the messages")
|
|
||||||
|
|
||||||
flags.StringArray(
|
|
||||||
"notification-url",
|
|
||||||
[]string{},
|
|
||||||
// viper.GetStringSlice("WATCHTOWER_NOTIFICATION_URL"),
|
|
||||||
"The shoutrrr URL to send notifications to")
|
|
||||||
|
|
||||||
flags.Bool("notification-report",
|
|
||||||
false,
|
|
||||||
// viper.GetBool("WATCHTOWER_NOTIFICATION_REPORT"),
|
|
||||||
"Use the session report as the notification template data")
|
|
||||||
|
|
||||||
flags.String(
|
|
||||||
"warn-on-head-failure",
|
|
||||||
"auto",
|
|
||||||
// viper.GetString("WATCHTOWER_WARN_ON_HEAD_FAILURE"),
|
|
||||||
"When to warn about HEAD pull requests failing. Possible values: always, auto or never")
|
|
||||||
|
|
||||||
RegisterLegacyNotificationFlags(flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustBindEnv(flag string, env string) {
|
|
||||||
if err := viper.BindEnv(flag, env); err != nil {
|
|
||||||
log.Fatalf("failed to bind env %q to flag %q: %v", env, flag, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEnvBindings binds environment variables to their corresponding config keys
|
|
||||||
func SetEnvBindings() {
|
|
||||||
|
|
||||||
// Using WATCHTOWER as a prefix...
|
|
||||||
viper.SetEnvPrefix("WATCHTOWER")
|
|
||||||
// ...and replacing dashes with undescores
|
|
||||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
|
||||||
// ...map all environment variables to corresponding flags in upper case
|
|
||||||
viper.AutomaticEnv()
|
|
||||||
|
|
||||||
// Aliases for non-matching ENV keys (backwards compability)
|
|
||||||
mustBindEnv("interval", "WATCHTOWER_POLL_INTERVAL")
|
|
||||||
|
|
||||||
// Aliases for DOCKER_-prefixed env variables (matching those used for docker cli)
|
|
||||||
mustBindEnv("host", "DOCKER_HOST")
|
|
||||||
mustBindEnv("tlsverify", "DOCKER_TLS_VERIFY")
|
|
||||||
mustBindEnv("api-version", "DOCKER_API_VERSION")
|
|
||||||
}
|
|
||||||
|
|
||||||
// BindViperFlags binds the cmd PFlags to the viper configuration
|
|
||||||
func BindViperFlags(cmd *cobra.Command) {
|
|
||||||
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
|
|
||||||
log.Fatalf("failed to bind flags: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnvConfig translates the command-line options into environment variables
|
|
||||||
// that will initialize the api client
|
|
||||||
func EnvConfig() error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
host := viper.GetString("host")
|
|
||||||
tls := viper.GetBool("tlsverify")
|
|
||||||
version := viper.GetString("api-version")
|
|
||||||
if err = setEnvOptStr("DOCKER_HOST", host); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = setEnvOptBool("DOCKER_TLS_VERIFY", tls); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = setEnvOptStr("DOCKER_API_VERSION", version); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadFlags reads common flags used in the main program flow of watchtower
|
|
||||||
func ReadFlags() (cleanup bool, noRestart bool, monitorOnly bool, timeout time.Duration) {
|
|
||||||
|
|
||||||
cleanup = viper.GetBool("cleanup")
|
|
||||||
noRestart = viper.GetBool("no-restart")
|
|
||||||
monitorOnly = viper.GetBool("monitor-only")
|
|
||||||
timeout = viper.GetDuration("stop-timeout")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func setEnvOptStr(env string, opt string) error {
|
|
||||||
if opt == "" || opt == os.Getenv(env) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := os.Setenv(env, opt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setEnvOptBool(env string, opt bool) error {
|
|
||||||
if opt {
|
|
||||||
return setEnvOptStr(env, "1")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSecretsFromFiles checks if passwords/tokens/webhooks have been passed as a file instead of plaintext.
|
|
||||||
// If so, the value of the flag will be replaced with the contents of the file.
|
|
||||||
func GetSecretsFromFiles() {
|
|
||||||
secrets := []string{
|
|
||||||
"notification-email-server-password",
|
|
||||||
"notification-slack-hook-url",
|
|
||||||
"notification-msteams-hook",
|
|
||||||
"notification-gotify-token",
|
|
||||||
}
|
|
||||||
for _, secret := range secrets {
|
|
||||||
getSecretFromFile(secret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getSecretFromFile will check if the flag contains a reference to a file; if it does, replaces the value of the flag with the contents of the file.
|
|
||||||
func getSecretFromFile(secret string) {
|
|
||||||
value := viper.GetString(secret)
|
|
||||||
if value != "" && isFile(value) {
|
|
||||||
file, err := ioutil.ReadFile(value)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
viper.Set(secret, strings.TrimSpace(string(file)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isFile(s string) bool {
|
|
||||||
_, err := os.Stat(s)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -3,11 +3,11 @@ package container
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/containrrr/watchtower/internal/config"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containrrr/watchtower/internal/flags"
|
|
||||||
"github.com/containrrr/watchtower/pkg/registry"
|
"github.com/containrrr/watchtower/pkg/registry"
|
||||||
"github.com/containrrr/watchtower/pkg/registry/digest"
|
"github.com/containrrr/watchtower/pkg/registry/digest"
|
||||||
t "github.com/containrrr/watchtower/pkg/types"
|
t "github.com/containrrr/watchtower/pkg/types"
|
||||||
|
@ -43,7 +43,7 @@ type Client interface {
|
||||||
// * DOCKER_HOST the docker-engine host to send api requests to
|
// * DOCKER_HOST the docker-engine host to send api requests to
|
||||||
// * DOCKER_TLS_VERIFY whether to verify tls certificates
|
// * DOCKER_TLS_VERIFY whether to verify tls certificates
|
||||||
// * DOCKER_API_VERSION the minimum docker api version to work with
|
// * DOCKER_API_VERSION the minimum docker api version to work with
|
||||||
func NewClient(c *flags.WatchConfig) Client {
|
func NewClient(c *config.WatchConfig) Client {
|
||||||
cli, err := sdkClient.NewClientWithOpts(sdkClient.FromEnv)
|
cli, err := sdkClient.NewClientWithOpts(sdkClient.FromEnv)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -2,12 +2,12 @@ package notifications_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/containrrr/watchtower/internal/config"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containrrr/watchtower/cmd"
|
"github.com/containrrr/watchtower/cmd"
|
||||||
"github.com/containrrr/watchtower/internal/flags"
|
|
||||||
"github.com/containrrr/watchtower/pkg/notifications"
|
"github.com/containrrr/watchtower/pkg/notifications"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
@ -17,30 +17,20 @@ var _ = Describe("notifications", func() {
|
||||||
Describe("the notifier", func() {
|
Describe("the notifier", func() {
|
||||||
When("only empty notifier types are provided", func() {
|
When("only empty notifier types are provided", func() {
|
||||||
|
|
||||||
command := cmd.NewRootCommand()
|
parseCommandLine(
|
||||||
flags.RegisterNotificationFlags(command)
|
|
||||||
flags.BindViperFlags(command)
|
|
||||||
|
|
||||||
err := command.ParseFlags([]string{
|
|
||||||
"--notifications",
|
"--notifications",
|
||||||
"shoutrrr",
|
"shoutrrr",
|
||||||
})
|
)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
notif := notifications.NewNotifier()
|
notif := notifications.NewNotifier()
|
||||||
|
|
||||||
Expect(notif.GetNames()).To(BeEmpty())
|
Expect(notif.GetNames()).To(BeEmpty())
|
||||||
})
|
})
|
||||||
When("title is overriden in flag", func() {
|
When("title is overriden in flag", func() {
|
||||||
It("should use the specified hostname in the title", func() {
|
It("should use the specified hostname in the title", func() {
|
||||||
command := cmd.NewRootCommand()
|
parseCommandLine(
|
||||||
flags.RegisterNotificationFlags(command)
|
|
||||||
flags.BindViperFlags(command)
|
|
||||||
|
|
||||||
err := command.ParseFlags([]string{
|
|
||||||
"--notifications-hostname",
|
"--notifications-hostname",
|
||||||
"test.host",
|
"test.host",
|
||||||
})
|
)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
hostname := notifications.GetHostname()
|
hostname := notifications.GetHostname()
|
||||||
title := notifications.GetTitle(hostname)
|
title := notifications.GetTitle(hostname)
|
||||||
Expect(title).To(Equal("Watchtower updates on test.host"))
|
Expect(title).To(Equal("Watchtower updates on test.host"))
|
||||||
|
@ -54,9 +44,7 @@ var _ = Describe("notifications", func() {
|
||||||
})
|
})
|
||||||
When("no delay is defined", func() {
|
When("no delay is defined", func() {
|
||||||
It("should use the default delay", func() {
|
It("should use the default delay", func() {
|
||||||
command := cmd.NewRootCommand()
|
parseCommandLine()
|
||||||
flags.RegisterNotificationFlags(command)
|
|
||||||
flags.BindViperFlags(command)
|
|
||||||
|
|
||||||
delay := notifications.GetDelay(time.Duration(0))
|
delay := notifications.GetDelay(time.Duration(0))
|
||||||
Expect(delay).To(Equal(time.Duration(0)))
|
Expect(delay).To(Equal(time.Duration(0)))
|
||||||
|
@ -64,39 +52,25 @@ var _ = Describe("notifications", func() {
|
||||||
})
|
})
|
||||||
When("delay is defined", func() {
|
When("delay is defined", func() {
|
||||||
It("should use the specified delay", func() {
|
It("should use the specified delay", func() {
|
||||||
command := cmd.NewRootCommand()
|
parseCommandLine(
|
||||||
flags.RegisterNotificationFlags(command)
|
|
||||||
flags.BindViperFlags(command)
|
|
||||||
|
|
||||||
err := command.ParseFlags([]string{
|
|
||||||
"--notifications-delay",
|
"--notifications-delay",
|
||||||
"5",
|
"5",
|
||||||
})
|
)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
delay := notifications.GetDelay(time.Duration(0))
|
delay := notifications.GetDelay(time.Duration(0))
|
||||||
Expect(delay).To(Equal(time.Duration(5) * time.Second))
|
Expect(delay).To(Equal(time.Duration(5) * time.Second))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
When("legacy delay is defined", func() {
|
When("legacy delay is defined", func() {
|
||||||
It("should use the specified legacy delay", func() {
|
It("should use the specified legacy delay", func() {
|
||||||
command := cmd.NewRootCommand()
|
parseCommandLine()
|
||||||
flags.RegisterNotificationFlags(command)
|
|
||||||
flags.BindViperFlags(command)
|
|
||||||
delay := notifications.GetDelay(time.Duration(5) * time.Second)
|
delay := notifications.GetDelay(time.Duration(5) * time.Second)
|
||||||
Expect(delay).To(Equal(time.Duration(5) * time.Second))
|
Expect(delay).To(Equal(time.Duration(5) * time.Second))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
When("legacy delay and delay is defined", func() {
|
When("legacy delay and delay is defined", func() {
|
||||||
It("should use the specified legacy delay and ignore the specified delay", func() {
|
It("should use the specified legacy delay and ignore the specified delay", func() {
|
||||||
command := cmd.NewRootCommand()
|
parseCommandLine("--notifications-delay", "0")
|
||||||
flags.RegisterNotificationFlags(command)
|
|
||||||
flags.BindViperFlags(command)
|
|
||||||
|
|
||||||
err := command.ParseFlags([]string{
|
|
||||||
"--notifications-delay",
|
|
||||||
"0",
|
|
||||||
})
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
delay := notifications.GetDelay(time.Duration(7) * time.Second)
|
delay := notifications.GetDelay(time.Duration(7) * time.Second)
|
||||||
Expect(delay).To(Equal(time.Duration(7) * time.Second))
|
Expect(delay).To(Equal(time.Duration(7) * time.Second))
|
||||||
})
|
})
|
||||||
|
@ -132,7 +106,7 @@ 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() {
|
||||||
command := cmd.NewRootCommand()
|
command := cmd.NewRootCommand()
|
||||||
flags.RegisterNotificationFlags(command)
|
config.RegisterNotificationOptions(command)
|
||||||
username := "containrrrbot"
|
username := "containrrrbot"
|
||||||
tokenA := "AAAAAAAAA"
|
tokenA := "AAAAAAAAA"
|
||||||
tokenB := "BBBBBBBBB"
|
tokenB := "BBBBBBBBB"
|
||||||
|
@ -294,6 +268,14 @@ var _ = Describe("notifications", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
func parseCommandLine(args ...string) {
|
||||||
|
command := cmd.NewRootCommand()
|
||||||
|
config.RegisterNotificationOptions(command)
|
||||||
|
config.BindViperFlags(command)
|
||||||
|
|
||||||
|
ExpectWithOffset(1, command.ParseFlags(args)).To(Succeed())
|
||||||
|
}
|
||||||
|
|
||||||
func buildExpectedURL(username string, password string, host string, port int, from string, to string, auth string) string {
|
func buildExpectedURL(username string, password string, host string, port int, from string, to string, auth string) string {
|
||||||
hostname, err := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
@ -313,19 +295,12 @@ func buildExpectedURL(username string, password string, host string, port int, f
|
||||||
func testURL(args []string, expectedURL string, expectedDelay time.Duration) {
|
func testURL(args []string, expectedURL string, expectedDelay time.Duration) {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
command := cmd.NewRootCommand()
|
parseCommandLine(args...)
|
||||||
flags.RegisterNotificationFlags(command)
|
|
||||||
flags.BindViperFlags(command)
|
|
||||||
|
|
||||||
err := command.ParseFlags(args)
|
|
||||||
ExpectWithOffset(1, err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
hostname := notifications.GetHostname()
|
hostname := notifications.GetHostname()
|
||||||
title := notifications.GetTitle(hostname)
|
title := notifications.GetTitle(hostname)
|
||||||
urls, delay := notifications.AppendLegacyUrls([]string{}, title)
|
urls, delay := notifications.AppendLegacyUrls([]string{}, title)
|
||||||
|
|
||||||
ExpectWithOffset(1, err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
ExpectWithOffset(1, urls).To(ContainElement(expectedURL))
|
ExpectWithOffset(1, urls).To(ContainElement(expectedURL))
|
||||||
ExpectWithOffset(1, delay).To(Equal(expectedDelay))
|
ExpectWithOffset(1, delay).To(Equal(expectedDelay))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package notifications
|
package notifications
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/containrrr/watchtower/internal/config"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containrrr/shoutrrr/pkg/types"
|
"github.com/containrrr/shoutrrr/pkg/types"
|
||||||
"github.com/containrrr/watchtower/internal/actions/mocks"
|
"github.com/containrrr/watchtower/internal/actions/mocks"
|
||||||
"github.com/containrrr/watchtower/internal/flags"
|
|
||||||
s "github.com/containrrr/watchtower/pkg/session"
|
s "github.com/containrrr/watchtower/pkg/session"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
@ -75,7 +75,7 @@ var _ = Describe("Shoutrrr", func() {
|
||||||
When("no custom template is provided", func() {
|
When("no custom template is provided", func() {
|
||||||
It("should format the messages using the default template", func() {
|
It("should format the messages using the default template", func() {
|
||||||
cmd := new(cobra.Command)
|
cmd := new(cobra.Command)
|
||||||
flags.RegisterNotificationFlags(cmd)
|
config.RegisterNotificationOptions(cmd)
|
||||||
|
|
||||||
shoutrrr := createNotifier([]string{}, logrus.AllLevels, "", true)
|
shoutrrr := createNotifier([]string{}, logrus.AllLevels, "", true)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue