@ -15,13 +15,14 @@ import (
// DockerAPIMinVersion is the minimum version of the docker api required to
// use watchtower
const DockerAPIMinVersion string = "1.25"
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" , viper . GetString ( "DOCKER_HOST" ) , "daemon socket to connect to" )
flags . BoolP ( "tlsverify" , "v" , viper . GetBool ( "DOCKER_TLS_VERIFY" ) , "use TLS and verify the remote" )
flags . StringP ( "api-version" , "a" , viper. GetString ( "DOCKER_API_VERSION" ) , "api version to use by docker client" )
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
@ -30,126 +31,118 @@ func RegisterSystemFlags(rootCmd *cobra.Command) {
flags . IntP (
"interval" ,
"i" ,
viper . GetInt ( "WATCHTOWER_POLL_INTERVAL" ) ,
" P oll interval (in seconds)")
DefaultInterval, // viper.GetInt("WATCHTOWER_POLL_INTERVAL"),
" p oll interval (in seconds)")
flags . String P (
flags . String (
"schedule" ,
"s" ,
viper . GetString ( " WATCHTOWER_SCHEDULE ") ,
" ",
"The cron expression which defines when to update" )
//viper.GetString("WATCHTOWER_SCHEDULE"),
flags . DurationP (
"stop-timeout" ,
"t" ,
viper . GetDuration ( "WATCHTOWER_TIMEOUT" ) ,
time. Second * 10 , // viper.GetDuration("WATCHTOWER_TIMEOUT"),
"Timeout before a container is forcefully stopped" )
flags . BoolP (
"no-pull" ,
"" ,
viper . GetBool ( "WATCHTOWER_NO_PULL" ) ,
false , // viper.GetBool("WATCHTOWER_NO_PULL"),
"Do not pull any new images" )
flags . Bool P (
flags . Bool (
"no-restart" ,
"" ,
viper . GetBool ( "WATCHTOWER_NO_RESTART" ) ,
false , // viper.GetBool("WATCHTOWER_NO_RESTART"),
"Do not restart any containers" )
flags . Bool P (
flags . Bool (
"no-startup-message" ,
"" ,
viper . GetBool ( "WATCHTOWER_NO_STARTUP_MESSAGE" ) ,
false , // viper.GetBool("WATCHTOWER_NO_STARTUP_MESSAGE"),
"Prevents watchtower from sending a startup message" )
flags . BoolP (
"cleanup" ,
"c" ,
viper . GetBool ( "WATCHTOWER_CLEANUP" ) ,
false , // viper.GetBool("WATCHTOWER_CLEANUP"),
"Remove previously used images after updating" )
flags . BoolP (
"remove-volumes" ,
"" ,
viper . GetBool ( "WATCHTOWER_REMOVE_VOLUMES" ) ,
false , // viper.GetBool("WATCHTOWER_REMOVE_VOLUMES"),
"Remove attached volumes before updating" )
flags . BoolP (
"label-enable" ,
"e" ,
viper . GetBool ( "WATCHTOWER_LABEL_ENABLE" ) ,
false , // viper.GetBool("WATCHTOWER_LABEL_ENABLE"),
"Watch containers where the com.centurylinklabs.watchtower.enable label is true" )
flags . BoolP (
"debug" ,
"d" ,
viper . GetBool ( "WATCHTOWER_DEBUG" ) ,
false , // viper.GetBool("WATCHTOWER_DEBUG"),
"Enable debug mode with verbose logging" )
flags . Bool P (
flags . Bool (
"trace" ,
"" ,
viper . GetBool ( "WATCHTOWER_TRACE" ) ,
false , // viper.GetBool("WATCHTOWER_TRACE"),
"Enable trace mode with very verbose logging - caution, exposes credentials" )
flags . BoolP (
"monitor-only" ,
"m" ,
viper . GetBool ( "WATCHTOWER_MONITOR_ONLY" ) ,
false , // viper.GetBool("WATCHTOWER_MONITOR_ONLY"),
"Will only monitor for new images, not update the containers" )
flags . BoolP (
"run-once" ,
"R" ,
viper . GetBool ( "WATCHTOWER_RUN_ONCE" ) ,
false , // viper.GetBool("WATCHTOWER_RUN_ONCE"),
"Run once now and exit" )
flags . BoolP (
"include-restarting" ,
"" ,
viper . GetBool ( "WATCHTOWER_INCLUDE_RESTARTING" ) ,
false , // viper.GetBool("WATCHTOWER_INCLUDE_RESTARTING"),
"Will also include restarting containers" )
flags . BoolP (
"include-stopped" ,
"S" ,
viper . GetBool ( "WATCHTOWER_INCLUDE_STOPPED" ) ,
false , // viper.GetBool("WATCHTOWER_INCLUDE_STOPPED"),
"Will also include created and exited containers" )
flags . Bool P (
flags . Bool (
"revive-stopped" ,
"" ,
viper . GetBool ( "WATCHTOWER_REVIVE_STOPPED" ) ,
false , // viper.GetBool("WATCHTOWER_REVIVE_STOPPED"),
"Will also start stopped containers that were updated, if include-stopped is active" )
flags . Bool P (
flags . Bool (
"enable-lifecycle-hooks" ,
"" ,
viper . GetBool ( "WATCHTOWER_LIFECYCLE_HOOKS" ) ,
false , // viper.GetBool("WATCHTOWER_LIFECYCLE_HOOKS"),
"Enable the execution of commands triggered by pre- and post-update lifecycle hooks" )
flags . Bool P (
flags . Bool (
"rolling-restart" ,
"" ,
viper . GetBool ( "WATCHTOWER_ROLLING_RESTART" ) ,
false , // viper.GetBool("WATCHTOWER_ROLLING_RESTART"),
"Restart containers one at a time" )
flags . Bool P (
flags . Bool (
"http-api-update" ,
"" ,
viper . GetBool ( "WATCHTOWER_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 P (
flags . Bool (
"http-api-metrics" ,
"" ,
viper . GetBool ( "WATCHTOWER_HTTP_API_METRICS" ) ,
false , // viper.GetBool("WATCHTOWER_HTTP_API_METRICS"),
"Runs Watchtower with the Prometheus metrics API enabled" )
flags . String P (
flags . String (
"http-api-token" ,
"" ,
viper . GetString ( "WATCHTOWER_HTTP_API_TOKEN" ) ,
"" , // viper.GetString("WATCHTOWER_HTTP_API_TOKEN"),
"Sets an authentication token to HTTP API requests." )
flags . BoolP (
"http-api-periodic-polls" ,
@ -160,12 +153,11 @@ func RegisterSystemFlags(rootCmd *cobra.Command) {
flags . BoolP (
"no-color" ,
"" ,
viper . IsSet ( "NO_COLOR" ) ,
false , // viper.IsSet("NO_COLOR"),
"Disable ANSI color escape codes in log output" )
flags . String P (
flags . String (
"scope" ,
"" ,
viper . GetString ( "WATCHTOWER_SCOPE" ) ,
"" , // viper.GetString("WATCHTOWER_SCOPE"),
"Defines a monitoring scope for the Watchtower instance." )
}
@ -176,159 +168,162 @@ func RegisterNotificationFlags(rootCmd *cobra.Command) {
flags . StringSliceP (
"notifications" ,
"n" ,
viper . GetStringSlice ( "WATCHTOWER_NOTIFICATIONS" ) ,
[ ] string { } , // viper.GetStringSlice("WATCHTOWER_NOTIFICATIONS"),
" Notification types to send (valid: email, slack, msteams, gotify, shoutrrr)" )
flags . String (
"notifications-level" ,
viper . GetString ( "WATCHTOWER_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 P (
flags . Int (
"notifications-delay" ,
"" ,
viper . GetInt ( "WATCHTOWER_NOTIFICATIONS_DELAY" ) ,
0 , // viper.GetInt("WATCHTOWER_NOTIFICATIONS_DELAY"),
"Delay before sending notifications, expressed in seconds" )
flags . String P (
flags . String (
"notifications-hostname" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATIONS_HOSTNAME" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATIONS_HOSTNAME"),
"Custom hostname for notification titles" )
flags . String P (
flags . String (
"notification-email-from" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_EMAIL_FROM" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_FROM"),
"Address to send notification emails from" )
flags . String P (
flags . String (
"notification-email-to" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_EMAIL_TO" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_TO"),
"Address to send notification emails to" )
flags . Int P (
flags . Int (
"notification-email-delay" ,
"" ,
viper . GetInt ( "WATCHTOWER_NOTIFICATION_EMAIL_DELAY" ) ,
0 ,
//viper.GetInt("WATCHTOWER_NOTIFICATION_EMAIL_DELAY"),
"Delay before sending notifications, expressed in seconds" )
flags . String P (
flags . String (
"notification-email-server" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_EMAIL_SERVER" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_SERVER"),
"SMTP server to send notification emails through" )
flags . Int P (
flags . Int (
"notification-email-server-port" ,
"" ,
viper . GetInt ( "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT" ) ,
25 ,
// viper.GetInt("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT"),
"SMTP server port to send notification emails through" )
flags . Bool P (
flags . Bool (
"notification-email-server-tls-skip-verify" ,
"" ,
viper . GetBool ( "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_TLS_SKIP_VERIFY" ) ,
false ,
// viper.GetBool( "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_TLS_SKIP_VERIFY"),
` Controls whether watchtower verifies the SMTP server ' s certificate chain and host name .
Should only be used for testing . ` )
flags . String P (
flags . String (
"notification-email-server-user" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER"),
"SMTP server user for sending notifications" )
flags . String P (
flags . String (
"notification-email-server-password" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD" ) ,
// viper.GetString( "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD"),
"SMTP server password for sending notifications" )
flags . String P (
flags . String (
"notification-email-subjecttag" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG"),
"Subject prefix tag for notifications via mail" )
flags . String P (
flags . String (
"notification-slack-hook-url" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL"),
"The Slack Hook URL to send notifications to" )
flags . String P (
flags . String (
"notification-slack-identifier" ,
" ",
viper . GetString ( "WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER" ) ,
" watchtower ",
// viper.GetString("WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER"),
"A string which will be used to identify the messages coming from this watchtower instance" )
flags . String P (
flags . String (
"notification-slack-channel" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_SLACK_CHANNEL" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_SLACK_CHANNEL"),
"A string which overrides the webhook's default channel. Example: #my-custom-channel" )
flags . String P (
flags . String (
"notification-slack-icon-emoji" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_SLACK_ICON_EMOJI" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_SLACK_ICON_EMOJI"),
"An emoji code string to use in place of the default icon" )
flags . String P (
flags . String (
"notification-slack-icon-url" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_SLACK_ICON_URL" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_SLACK_ICON_URL"),
"An icon image URL string to use in place of the default icon" )
flags . String P (
flags . String (
"notification-msteams-hook" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_MSTEAMS_HOOK_URL" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_MSTEAMS_HOOK_URL"),
"The MSTeams WebHook URL to send notifications to" )
flags . Bool P (
flags . Bool (
"notification-msteams-data" ,
"" ,
viper . GetBool ( "WATCHTOWER_NOTIFICATION_MSTEAMS_USE_LOG_DATA" ) ,
false ,
// viper.GetBool( "WATCHTOWER_NOTIFICATION_MSTEAMS_USE_LOG_DATA"),
"The MSTeams notifier will try to extract log entry fields as MSTeams message facts" )
flags . String P (
flags . String (
"notification-gotify-url" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_GOTIFY_URL" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_GOTIFY_URL"),
"The Gotify URL to send notifications to" )
flags . String P (
flags . String (
"notification-gotify-token" ,
"" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN" ) ,
// viper.GetString("WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN"),
"The Gotify Application required to query the Gotify API" )
flags . Bool P (
flags . Bool (
"notification-gotify-tls-skip-verify" ,
"" ,
viper . GetBool ( "WATCHTOWER_NOTIFICATION_GOTIFY_TLS_SKIP_VERIFY" ) ,
false ,
// viper.GetBool( "WATCHTOWER_NOTIFICATION_GOTIFY_TLS_SKIP_VERIFY"),
` Controls whether watchtower verifies the Gotify server ' s certificate chain and host name .
Should only be used for testing . ` )
flags . String (
"notification-template" ,
viper . GetString ( "WATCHTOWER_NOTIFICATION_TEMPLATE" ) ,
"" ,
// viper.GetString("WATCHTOWER_NOTIFICATION_TEMPLATE"),
"The shoutrrr text/template for the messages" )
flags . StringArray (
"notification-url" ,
viper . GetStringSlice ( "WATCHTOWER_NOTIFICATION_URL" ) ,
[ ] string { } ,
// viper.GetStringSlice("WATCHTOWER_NOTIFICATION_URL"),
"The shoutrrr URL to send notifications to" )
flags . Bool ( "notification-report" ,
viper . GetBool ( "WATCHTOWER_NOTIFICATION_REPORT" ) ,
false ,
// viper.GetBool("WATCHTOWER_NOTIFICATION_REPORT"),
"Use the session report as the notification template data" )
flags . String (
"warn-on-head-failure" ,
viper . GetString ( "WATCHTOWER_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" )
}
@ -337,36 +332,23 @@ Should only be used for testing.`)
func SetDefaults ( ) {
day := ( time . Hour * 24 ) . Seconds ( )
viper . AutomaticEnv ( )
viper . SetDefault ( "DOCKER_HOST" , "unix:///var/run/docker.sock" )
viper . SetDefault ( "DOCKER_API_VERSION" , DockerAPIMinVersion )
viper . SetDefault ( "WATCHTOWER_POLL_INTERVAL" , day )
viper . SetDefault ( "WATCHTOWER_TIMEOUT" , time . Second * 10 )
viper . SetDefault ( "WATCHTOWER_NOTIFICATIONS" , [ ] string { } )
viper . SetDefault ( "WATCHTOWER_NOTIFICATIONS_LEVEL" , "info" )
viper . SetDefault ( "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT" , 25 )
viper . SetDefault ( "WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG" , "" )
viper . SetDefault ( "WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER" , "watchtower" )
}
// 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 ( cmd * cobra . Command ) error {
func EnvConfig ( ) error {
var err error
var host string
var tls bool
var version string
flags := cmd . PersistentFlags ( )
if host , err = flags . GetString ( "host" ) ; err != nil {
return err
}
if tls , err = flags . GetBool ( "tlsverify" ) ; err != nil {
return err
}
if version , err = flags . GetString ( "api-version" ) ; err != nil {
return err
}
host := viper . GetString ( "host" )
tls = viper . GetBool ( "tlsverify" )
version = viper . GetString ( "api-version" )
if err = setEnvOptStr ( "DOCKER_HOST" , host ) ; err != nil {
return err
}
@ -380,29 +362,14 @@ func EnvConfig(cmd *cobra.Command) error {
}
// ReadFlags reads common flags used in the main program flow of watchtower
func ReadFlags ( cmd * cobra . Command ) ( bool , bool , bool , time . Duration ) {
flags := cmd . PersistentFlags ( )
var err error
var cleanup bool
var noRestart bool
var monitorOnly bool
var timeout time . Duration
func ReadFlags ( ) ( cleanup bool , noRestart bool , monitorOnly bool , timeout time . Duration ) {
if cleanup , err = flags . GetBool ( "cleanup" ) ; err != nil {
log . Fatal ( err )
}
if noRestart , err = flags . GetBool ( "no-restart" ) ; err != nil {
log . Fatal ( err )
}
if monitorOnly , err = flags . GetBool ( "monitor-only" ) ; err != nil {
log . Fatal ( err )
}
if timeout , err = flags . GetDuration ( "stop-timeout" ) ; err != nil {
log . Fatal ( err )
}
cleanup = viper . GetBool ( "cleanup" )
noRestart = viper . GetBool ( "no-restart" )
monitorOnly = viper . GetBool ( "monitor-only" )
timeout = viper . GetDuration ( "stop-timeout" )
return cleanup , noRestart , monitorOnly , timeout
return
}
func setEnvOptStr ( env string , opt string ) error {
@ -425,9 +392,7 @@ func setEnvOptBool(env string, opt bool) error {
// 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 ( rootCmd * cobra . Command ) {
flags := rootCmd . PersistentFlags ( )
func GetSecretsFromFiles ( ) {
secrets := [ ] string {
"notification-email-server-password" ,
"notification-slack-hook-url" ,
@ -435,25 +400,19 @@ func GetSecretsFromFiles(rootCmd *cobra.Command) {
"notification-gotify-token" ,
}
for _ , secret := range secrets {
getSecretFromFile ( flags, secret)
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 ( flags * pflag . FlagSet , secret string ) {
value , err := flags . GetString ( secret )
if err != nil {
log . Error ( err )
}
func getSecretFromFile ( secret string ) {
value := viper . GetString ( secret )
if value != "" && isFile ( value ) {
file , err := ioutil . ReadFile ( value )
if err != nil {
log . Fatal ( err )
}
err = flags . Set ( secret , strings . TrimSpace ( string ( file ) ) )
if err != nil {
log . Error ( err )
}
viper . Set ( secret , strings . TrimSpace ( string ( file ) ) )
}
}