Possibility to define a cron expression which specifies when to check for updated images. This allows to have a schedule in which updates should be made and therefore one could define a maintenance window.

pull/39/head
Fabrizio Steiner 8 years ago
parent 5902e9e0be
commit 525dfea3f2

@ -73,6 +73,7 @@ docker run --rm v2tec/watchtower --help
* `--host, -h` Docker daemon socket to connect to. Defaults to "unix:///var/run/docker.sock" but can be pointed at a remote Docker host by specifying a TCP endpoint as "tcp://hostname:port". The host value can also be provided by setting the `DOCKER_HOST` environment variable. * `--host, -h` Docker daemon socket to connect to. Defaults to "unix:///var/run/docker.sock" but can be pointed at a remote Docker host by specifying a TCP endpoint as "tcp://hostname:port". The host value can also be provided by setting the `DOCKER_HOST` environment variable.
* `--interval, -i` Poll interval (in seconds). This value controls how frequently watchtower will poll for new images. Defaults to 300 seconds (5 minutes). * `--interval, -i` Poll interval (in seconds). This value controls how frequently watchtower will poll for new images. Defaults to 300 seconds (5 minutes).
* `--schedule, -s` [Cron expression](https://godoc.org/github.com/robfig/cron#hdr-CRON_Expression_Format) which defines when and how often to check for new images. Either `--interval` or the schedule expression could be defined, but not both.
* `--no-pull` Do not pull new images. When this flag is specified, watchtower will not attempt to pull new images from the registry. Instead it will only monitor the local image cache for changes. Use this option if you are building new images directly on the Docker host without pushing them to a registry. * `--no-pull` Do not pull new images. When this flag is specified, watchtower will not attempt to pull new images from the registry. Instead it will only monitor the local image cache for changes. Use this option if you are building new images directly on the Docker host without pushing them to a registry.
* `--cleanup` Remove old images after updating. When this flag is specified, watchtower will remove the old image after restarting a container with a new image. Use this option to prevent the accumulation of orphaned images on your system as containers are updated. * `--cleanup` Remove old images after updating. When this flag is specified, watchtower will remove the old image after restarting a container with a new image. Use this option to prevent the accumulation of orphaned images on your system as containers are updated.
* `--tlsverify` Use TLS when connecting to the Docker socket and verify the server's certificate. * `--tlsverify` Use TLS when connecting to the Docker socket and verify the server's certificate.

@ -4,20 +4,21 @@ import (
"fmt" "fmt"
"os" "os"
"os/signal" "os/signal"
"sync"
"syscall" "syscall"
"time" "time"
"strconv"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/robfig/cron"
"github.com/urfave/cli" "github.com/urfave/cli"
"github.com/v2tec/watchtower/actions" "github.com/v2tec/watchtower/actions"
"github.com/v2tec/watchtower/container" "github.com/v2tec/watchtower/container"
) )
var ( var (
wg sync.WaitGroup
client container.Client client container.Client
pollInterval time.Duration scheduleSpec string
cleanup bool cleanup bool
noRestart bool noRestart bool
) )
@ -45,6 +46,11 @@ func main() {
Value: 300, Value: 300,
EnvVar: "WATCHTOWER_POLL_INTERVAL", EnvVar: "WATCHTOWER_POLL_INTERVAL",
}, },
cli.StringFlag{
Name: "schedule, s",
Usage: "the cron expression which defines when to update",
EnvVar: "WATCHTOWER_SCHEDULE",
},
cli.BoolFlag{ cli.BoolFlag{
Name: "no-pull", Name: "no-pull",
Usage: "do not pull new images", Usage: "do not pull new images",
@ -86,7 +92,17 @@ func before(c *cli.Context) error {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
} }
pollInterval = time.Duration(c.Int("interval")) * time.Second 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"
}
cleanup = c.GlobalBool("cleanup") cleanup = c.GlobalBool("cleanup")
noRestart = c.GlobalBool("no-restart") noRestart = c.GlobalBool("no-restart")
@ -97,40 +113,57 @@ func before(c *cli.Context) error {
} }
client = container.NewClient(!c.GlobalBool("no-pull")) client = container.NewClient(!c.GlobalBool("no-pull"))
handleSignals()
return nil return nil
} }
func start(c *cli.Context) { func start(c *cli.Context) error {
names := c.Args() names := c.Args()
if err := actions.CheckPrereqs(client, cleanup); err != nil { if err := actions.CheckPrereqs(client, cleanup); err != nil {
log.Fatal(err) log.Fatal(err)
} }
for { tryLockSem := make(chan bool, 1)
wg.Add(1) tryLockSem <- true
if err := actions.Update(client, names, cleanup, noRestart); err != nil {
fmt.Println(err) cron := cron.New()
} err := cron.AddFunc(
wg.Done() scheduleSpec,
func() {
select {
case v := <-tryLockSem:
defer func() { tryLockSem <- v }()
if err := actions.Update(client, names, cleanup, noRestart); err != nil {
fmt.Println(err)
}
default:
log.Debug("Skipped another update already running.")
}
nextRuns := cron.Entries()
if len(nextRuns) > 0 {
log.Debug("Scheduled next run: " + nextRuns[0].Next.String())
}
})
time.Sleep(pollInterval) if err != nil {
return err
} }
}
func handleSignals() { log.Info("First run: " + cron.Entries()[0].Schedule.Next(time.Now()).String())
cron.Start()
// Graceful shut-down on SIGINT/SIGTERM // Graceful shut-down on SIGINT/SIGTERM
c := make(chan os.Signal, 1) interrupt := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt) signal.Notify(interrupt, os.Interrupt)
signal.Notify(c, syscall.SIGTERM) signal.Notify(interrupt, syscall.SIGTERM)
go func() { <-interrupt
<-c cron.Stop()
wg.Wait() log.Info("Waiting for running update to be finished...")
os.Exit(1) <-tryLockSem
}() os.Exit(1)
return nil
} }
func setEnvOptStr(env string, opt string) error { func setEnvOptStr(env string, opt string) error {

Loading…
Cancel
Save