Make it possible to use watchtower to update exited or created containers as well (#289)

* feature/112: add additional tests that verify include-stopped

* feature/112: implement include-stopped

* feature/112: update readme and cli help

* feature/112: fix linting issues

* remove superfluous logging
This commit is contained in:
Simon Aronsson 2019-05-12 09:29:52 +02:00 committed by GitHub
parent 1631c8cc2e
commit e584f8bfcf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 101 additions and 33 deletions

View file

@ -2,6 +2,8 @@ package container
import (
"fmt"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"io/ioutil"
"time"
@ -33,36 +35,48 @@ type Client interface {
// * DOCKER_HOST the docker-engine host to send api requests to
// * DOCKER_TLS_VERIFY whether to verify tls certificates
// * DOCKER_API_VERSION the minimum docker api version to work with
func NewClient(pullImages bool) Client {
func NewClient(pullImages bool, includeStopped bool) Client {
cli, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv)
if err != nil {
log.Fatalf("Error instantiating Docker client: %s", err)
}
return dockerClient{api: cli, pullImages: pullImages}
return dockerClient{
api: cli,
pullImages: pullImages,
includeStopped: includeStopped,
}
}
type dockerClient struct {
api dockerclient.CommonAPIClient
pullImages bool
api dockerclient.CommonAPIClient
pullImages bool
includeStopped bool
}
func (client dockerClient) ListContainers(fn Filter) ([]Container, error) {
cs := []Container{}
bg := context.Background()
log.Debug("Retrieving running containers")
if client.includeStopped {
log.Debug("Retrieving containers including stopped and exited")
} else {
log.Debug("Retrieving running containers")
}
runningContainers, err := client.api.ContainerList(
filter := client.createListFilter()
containers, err := client.api.ContainerList(
bg,
types.ContainerListOptions{})
types.ContainerListOptions{
Filters: filter,
})
if err != nil {
return nil, err
}
for _, runningContainer := range runningContainers {
for _, runningContainer := range containers {
containerInfo, err := client.api.ContainerInspect(bg, runningContainer.ID)
if err != nil {
return nil, err
@ -83,6 +97,18 @@ func (client dockerClient) ListContainers(fn Filter) ([]Container, error) {
return cs, nil
}
func (client dockerClient) createListFilter() filters.Args {
filterArgs := filters.NewArgs()
filterArgs.Add("status", "running")
if client.includeStopped {
filterArgs.Add("status", "created")
filterArgs.Add("status", "exited")
}
return filterArgs
}
func (client dockerClient) StopContainer(c Container, timeout time.Duration) error {
bg := context.Background()
signal := c.StopSignal()
@ -90,10 +116,11 @@ func (client dockerClient) StopContainer(c Container, timeout time.Duration) err
signal = defaultStopSignal
}
log.Infof("Stopping %s (%s) with %s", c.Name(), c.ID(), signal)
if err := client.api.ContainerKill(bg, c.ID(), signal); err != nil {
return err
if c.IsRunning() {
log.Infof("Stopping %s (%s) with %s", c.Name(), c.ID(), signal)
if err := client.api.ContainerKill(bg, c.ID(), signal); err != nil {
return err
}
}
// Wait for container to exit, but proceed anyway after the timeout elapses
@ -160,15 +187,23 @@ func (client dockerClient) StartContainer(c Container) error {
}
log.Debugf("Starting container %s (%s)", name, creation.ID)
return client.startContainerIfPreviouslyRunning(bg, c, creation)
err = client.api.ContainerStart(bg, creation.ID, types.ContainerStartOptions{})
}
func (client dockerClient) startContainerIfPreviouslyRunning(bg context.Context, c Container, creation container.ContainerCreateCreatedBody) error {
name := c.Name()
if !c.IsRunning() {
return nil
}
log.Debugf("Starting container %s (%s)", name, creation.ID)
err := client.api.ContainerStart(bg, creation.ID, types.ContainerStartOptions{})
if err != nil {
return err
}
return nil
}
func (client dockerClient) RenameContainer(c Container, newName string) error {