Implement support for updating stopped containers

This commit adds a command line flag, "--check-all". If true, the
behavior requested by #112 is used (i.e., stopped containers are updated
but not restarted). If false, the previous behavior is used (ignore
stopped containers entirely).
This commit is contained in:
Alex Hicks 2018-01-10 09:32:01 -05:00
parent 2cfbebb0e9
commit ab14e25f7c
No known key found for this signature in database
GPG key ID: 8C00D3C81928CA24
4 changed files with 42 additions and 13 deletions

View file

@ -67,6 +67,8 @@ func Update(client container.Client, names []string, cleanup bool, noRestart boo
continue continue
} }
container.WasRunning = container.IsRunning()
if container.Stale { if container.Stale {
if err := client.StopContainer(container, waitTime); err != nil { if err := client.StopContainer(container, waitTime); err != nil {
log.Error(err) log.Error(err)

View file

@ -37,31 +37,41 @@ 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(pullImages, enableLabel bool) Client { func NewClient(pullImages, enableLabel bool, checkAll bool) Client {
cli, err := dockerclient.NewEnvClient() cli, err := dockerclient.NewEnvClient()
if err != nil { if err != nil {
log.Fatalf("Error instantiating Docker client: %s", err) log.Fatalf("Error instantiating Docker client: %s", err)
} }
return dockerClient{api: cli, pullImages: pullImages, enableLabel: enableLabel} return dockerClient{
api: cli,
pullImages: pullImages,
enableLabel: enableLabel,
checkAll: checkAll}
} }
type dockerClient struct { type dockerClient struct {
api *dockerclient.Client api *dockerclient.Client
pullImages bool pullImages bool
enableLabel bool enableLabel bool
checkAll bool
} }
func (client dockerClient) ListContainers(fn Filter) ([]Container, error) { func (client dockerClient) ListContainers(fn Filter) ([]Container, error) {
cs := []Container{} cs := []Container{}
bg := context.Background() bg := context.Background()
log.Debug("Retrieving running containers") if client.checkAll {
log.Debug("Retrieving containers")
} else {
log.Debug("Retrieving running containers")
}
runningContainers, err := client.api.ContainerList( runningContainers, err := client.api.ContainerList(
bg, bg,
types.ContainerListOptions{}) types.ContainerListOptions{
All: client.checkAll})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -103,10 +113,12 @@ func (client dockerClient) StopContainer(c Container, timeout time.Duration) err
signal = defaultStopSignal signal = defaultStopSignal
} }
log.Infof("Stopping %s (%s) with %s", c.Name(), c.ID(), signal) if c.WasRunning {
log.Infof("Stopping %s (%s) with %s", c.Name(), c.ID(), signal)
if err := client.api.ContainerKill(bg, c.ID(), signal); err != nil { if err := client.api.ContainerKill(bg, c.ID(), signal); err != nil {
return err return err
}
} }
// Wait for container to exit, but proceed anyway after the timeout elapses // Wait for container to exit, but proceed anyway after the timeout elapses
@ -173,11 +185,13 @@ func (client dockerClient) StartContainer(c Container) error {
} }
log.Debugf("Starting container %s (%s)", name, creation.ID) if c.WasRunning {
log.Debugf("Starting container %s (%s)", name, creation.ID)
err = client.api.ContainerStart(bg, creation.ID, types.ContainerStartOptions{}) err = client.api.ContainerStart(bg, creation.ID, types.ContainerStartOptions{})
if err != nil { if err != nil {
return err return err
}
} }
return nil return nil

View file

@ -27,7 +27,8 @@ func NewContainer(containerInfo *types.ContainerJSON, imageInfo *types.ImageInsp
// Container represents a running Docker container. // Container represents a running Docker container.
type Container struct { type Container struct {
Stale bool Stale bool
WasRunning bool
containerInfo *types.ContainerJSON containerInfo *types.ContainerJSON
imageInfo *types.ImageInspect imageInfo *types.ImageInspect
@ -106,6 +107,13 @@ func (c Container) IsWatchtower() bool {
return ok && val == "true" return ok && val == "true"
} }
// IsRunning returns a boolean flag indicating whether or not the current
// container is running. The status is determined by the value of the
// container's "State.Running" property.
func (c Container) IsRunning() bool {
return c.containerInfo.State.Running
}
// StopSignal returns the custom stop signal (if any) that is encoded in the // StopSignal returns the custom stop signal (if any) that is encoded in the
// container's metadata. If the container has not specified a custom stop // container's metadata. If the container has not specified a custom stop
// signal, the empty string "" is returned. // signal, the empty string "" is returned.

View file

@ -61,6 +61,11 @@ func main() {
Usage: "the cron expression which defines when to update", Usage: "the cron expression which defines when to update",
EnvVar: "WATCHTOWER_SCHEDULE", EnvVar: "WATCHTOWER_SCHEDULE",
}, },
cli.BoolFlag{
Name: "check-all",
Usage: "check non-running containers",
EnvVar: "WATCHTOWER_CHECK_ALL",
},
cli.BoolFlag{ cli.BoolFlag{
Name: "no-pull", Name: "no-pull",
Usage: "do not pull new images", Usage: "do not pull new images",
@ -159,7 +164,7 @@ func before(c *cli.Context) error {
return err return err
} }
client = container.NewClient(!c.GlobalBool("no-pull"), c.GlobalBool("label-enable")) client = container.NewClient(!c.GlobalBool("no-pull"), c.GlobalBool("label-enable"), c.GlobalBool("check-all"))
notifier = notifications.NewNotifier(c) notifier = notifications.NewNotifier(c)
return nil return nil