| 
									
										
										
										
											2019-04-03 21:02:05 +02:00
										 |  |  | package main // import "github.com/containrrr/watchtower" | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/signal" | 
					
						
							|  |  |  | 	"syscall" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 	"strconv" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 21:02:05 +02:00
										 |  |  | 	"github.com/containrrr/watchtower/actions" | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 	cliApp "github.com/containrrr/watchtower/app" | 
					
						
							| 
									
										
										
										
											2019-04-03 21:02:05 +02:00
										 |  |  | 	"github.com/containrrr/watchtower/container" | 
					
						
							|  |  |  | 	"github.com/containrrr/watchtower/notifications" | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 	"github.com/robfig/cron" | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 	log "github.com/sirupsen/logrus" | 
					
						
							| 
									
										
										
										
											2016-10-13 22:57:08 +01:00
										 |  |  | 	"github.com/urfave/cli" | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-24 21:41:32 +01:00
										 |  |  | // DockerAPIMinVersion is the version of the docker API, which is minimally required by | 
					
						
							|  |  |  | // watchtower. Currently we require at least API 1.24 and therefore Docker 1.12 or later. | 
					
						
							|  |  |  | const DockerAPIMinVersion string = "1.24" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-02 18:37:12 +01:00
										 |  |  | var version = "master" | 
					
						
							| 
									
										
										
										
											2017-12-27 19:15:50 +01:00
										 |  |  | var commit = "unknown" | 
					
						
							|  |  |  | var date = "unknown" | 
					
						
							| 
									
										
										
										
											2017-02-02 18:37:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | var ( | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | 	client       container.Client | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 	scheduleSpec string | 
					
						
							| 
									
										
										
										
											2015-07-31 18:24:27 +00:00
										 |  |  | 	cleanup      bool | 
					
						
							| 
									
										
										
										
											2016-02-03 10:11:43 +00:00
										 |  |  | 	noRestart    bool | 
					
						
							| 
									
										
										
										
											2019-04-07 15:52:56 +02:00
										 |  |  | 	monitorOnly  bool | 
					
						
							| 
									
										
										
										
											2018-03-02 17:22:42 +01:00
										 |  |  | 	enableLabel  bool | 
					
						
							| 
									
										
										
										
											2017-10-29 23:30:44 -07:00
										 |  |  | 	notifier     *notifications.Notifier | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 	timeout      time.Duration | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | func init() { | 
					
						
							|  |  |  | 	log.SetLevel(log.InfoLevel) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | func main() { | 
					
						
							|  |  |  | 	app := cli.NewApp() | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 	InitApp(app) | 
					
						
							|  |  |  | 	cliApp.SetupCliFlags(app) | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | 	if err := app.Run(os.Args); err != nil { | 
					
						
							|  |  |  | 		log.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | // InitApp initializes urfave app metadata and sets up entrypoints | 
					
						
							|  |  |  | func InitApp(app *cli.App) { | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | 	app.Name = "watchtower" | 
					
						
							| 
									
										
										
										
											2017-12-27 19:15:50 +01:00
										 |  |  | 	app.Version = version + " - " + commit + " - " + date | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | 	app.Usage = "Automatically update running Docker containers" | 
					
						
							| 
									
										
										
										
											2015-07-20 22:54:18 +00:00
										 |  |  | 	app.Before = before | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | 	app.Action = start | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func before(c *cli.Context) error { | 
					
						
							|  |  |  | 	if c.GlobalBool("debug") { | 
					
						
							|  |  |  | 		log.SetLevel(log.DebugLevel) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 	pollingSet := c.IsSet("interval") | 
					
						
							|  |  |  | 	cronSet := c.IsSet("schedule") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if pollingSet && cronSet { | 
					
						
							|  |  |  | 		log.Fatal("Only schedule or interval can be defined, not both.") | 
					
						
							|  |  |  | 	} else if cronSet { | 
					
						
							|  |  |  | 		scheduleSpec = c.String("schedule") | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		scheduleSpec = "@every " + strconv.Itoa(c.Int("interval")) + "s" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 	readFlags(c) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-24 19:39:53 +01:00
										 |  |  | 	if timeout < 0 { | 
					
						
							|  |  |  | 		log.Fatal("Please specify a positive value for timeout value.") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-02 17:22:42 +01:00
										 |  |  | 	enableLabel = c.GlobalBool("label-enable") | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-14 11:10:48 +01:00
										 |  |  | 	// configure environment vars for client | 
					
						
							|  |  |  | 	err := envConfig(c) | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 17:22:42 +01:00
										 |  |  | 	client = container.NewClient(!c.GlobalBool("no-pull")) | 
					
						
							| 
									
										
										
										
											2017-10-29 23:30:44 -07:00
										 |  |  | 	notifier = notifications.NewNotifier(c) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | func start(c *cli.Context) error { | 
					
						
							| 
									
										
										
										
											2015-08-04 16:32:01 +00:00
										 |  |  | 	names := c.Args() | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 	filter := container.BuildFilter(names, enableLabel) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if c.GlobalBool("run-once") { | 
					
						
							|  |  |  | 		log.Info("Running a one time update.") | 
					
						
							|  |  |  | 		runUpdatesWithNotifications(filter) | 
					
						
							| 
									
										
										
										
											2017-12-05 20:01:25 -08:00
										 |  |  | 		os.Exit(1) | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-04 16:32:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-31 18:24:27 +00:00
										 |  |  | 	if err := actions.CheckPrereqs(client, cleanup); err != nil { | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | 		log.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 	runUpgradesOnSchedule(filter) | 
					
						
							|  |  |  | 	os.Exit(1) | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-02 17:22:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | func runUpgradesOnSchedule(filter container.Filter) error { | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 	tryLockSem := make(chan bool, 1) | 
					
						
							|  |  |  | 	tryLockSem <- true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cron := cron.New() | 
					
						
							|  |  |  | 	err := cron.AddFunc( | 
					
						
							|  |  |  | 		scheduleSpec, | 
					
						
							|  |  |  | 		func() { | 
					
						
							|  |  |  | 			select { | 
					
						
							| 
									
										
										
										
											2018-01-22 20:36:32 +01:00
										 |  |  | 			case v := <-tryLockSem: | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 				defer func() { tryLockSem <- v }() | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 				runUpdatesWithNotifications(filter) | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 			default: | 
					
						
							|  |  |  | 				log.Debug("Skipped another update already running.") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			nextRuns := cron.Entries() | 
					
						
							|  |  |  | 			if len(nextRuns) > 0 { | 
					
						
							|  |  |  | 				log.Debug("Scheduled next run: " + nextRuns[0].Next.String()) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 	log.Debug("Starting Watchtower and scheduling first run: " + cron.Entries()[0].Schedule.Next(time.Now()).String()) | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 	cron.Start() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | 	// Graceful shut-down on SIGINT/SIGTERM | 
					
						
							| 
									
										
										
										
											2016-12-30 00:23:02 +01:00
										 |  |  | 	interrupt := make(chan os.Signal, 1) | 
					
						
							|  |  |  | 	signal.Notify(interrupt, os.Interrupt) | 
					
						
							|  |  |  | 	signal.Notify(interrupt, syscall.SIGTERM) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	<-interrupt | 
					
						
							|  |  |  | 	cron.Stop() | 
					
						
							|  |  |  | 	log.Info("Waiting for running update to be finished...") | 
					
						
							|  |  |  | 	<-tryLockSem | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | func runUpdatesWithNotifications(filter container.Filter) { | 
					
						
							|  |  |  | 	notifier.StartNotification() | 
					
						
							|  |  |  | 	updateParams := actions.UpdateParams{ | 
					
						
							|  |  |  | 		Filter:      filter, | 
					
						
							|  |  |  | 		Cleanup:     cleanup, | 
					
						
							|  |  |  | 		NoRestart:   noRestart, | 
					
						
							|  |  |  | 		Timeout:     timeout, | 
					
						
							|  |  |  | 		MonitorOnly: monitorOnly, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err := actions.Update(client, updateParams) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Println(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-14 11:10:48 +01:00
										 |  |  | func setEnvOptStr(env string, opt string) error { | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 	if opt == "" || opt == os.Getenv(env) { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err := os.Setenv(env, opt) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-10-14 11:10:48 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-07-20 22:54:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-14 11:10:48 +01:00
										 |  |  | func setEnvOptBool(env string, opt bool) error { | 
					
						
							| 
									
										
										
										
											2016-10-18 13:54:41 +01:00
										 |  |  | 	if opt == true { | 
					
						
							| 
									
										
										
										
											2016-10-14 11:10:48 +01:00
										 |  |  | 		return setEnvOptStr(env, "1") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-07-24 19:51:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-14 11:10:48 +01:00
										 |  |  | // envConfig translates the command-line options into environment variables | 
					
						
							|  |  |  | // that will initialize the api client | 
					
						
							|  |  |  | func envConfig(c *cli.Context) error { | 
					
						
							|  |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2015-07-13 21:41:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-14 11:10:48 +01:00
										 |  |  | 	err = setEnvOptStr("DOCKER_HOST", c.GlobalString("host")) | 
					
						
							|  |  |  | 	err = setEnvOptBool("DOCKER_TLS_VERIFY", c.GlobalBool("tlsverify")) | 
					
						
							| 
									
										
										
										
											2017-01-24 21:41:32 +01:00
										 |  |  | 	err = setEnvOptStr("DOCKER_API_VERSION", DockerAPIMinVersion) | 
					
						
							| 
									
										
										
										
											2015-07-21 19:37:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-14 11:10:48 +01:00
										 |  |  | 	return err | 
					
						
							| 
									
										
										
										
											2015-07-21 19:37:18 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-04-08 17:38:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | func readFlags(c *cli.Context) { | 
					
						
							|  |  |  | 	cleanup = c.GlobalBool("cleanup") | 
					
						
							|  |  |  | 	noRestart = c.GlobalBool("no-restart") | 
					
						
							|  |  |  | 	monitorOnly = c.GlobalBool("monitor-only") | 
					
						
							|  |  |  | 	timeout = c.GlobalDuration("stop-timeout") | 
					
						
							|  |  |  | } |