Skip to content

Commit

Permalink
Allow watchtower to update rebooting containers (#651)
Browse files Browse the repository at this point in the history
Co-authored-by: nils måsén <[email protected]>
Co-authored-by: Simon Aronsson <[email protected]>
  • Loading branch information
3 people authored Nov 19, 2020
1 parent 64d48b7 commit 2842b97
Show file tree
Hide file tree
Showing 6 changed files with 362 additions and 19 deletions.
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func PreRun(cmd *cobra.Command, args []string) {

noPull, _ := f.GetBool("no-pull")
includeStopped, _ := f.GetBool("include-stopped")
includeRestarting, _ := f.GetBool("include-restarting")
reviveStopped, _ := f.GetBool("revive-stopped")
removeVolumes, _ := f.GetBool("remove-volumes")

Expand All @@ -128,6 +129,7 @@ func PreRun(cmd *cobra.Command, args []string) {
includeStopped,
reviveStopped,
removeVolumes,
includeRestarting,
)

notifier = notifications.NewNotifier(cmd)
Expand Down
39 changes: 25 additions & 14 deletions pkg/container/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package container
import (
"bytes"
"fmt"
"github.com/containrrr/watchtower/pkg/registry"
"io/ioutil"
"strings"
"time"

"github.com/containrrr/watchtower/pkg/registry"

t "github.com/containrrr/watchtower/pkg/types"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
Expand Down Expand Up @@ -39,36 +40,42 @@ 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, includeStopped bool, reviveStopped bool, removeVolumes bool) Client {
func NewClient(pullImages bool, includeStopped bool, reviveStopped bool, removeVolumes bool, includeRestarting bool) Client {
cli, err := sdkClient.NewClientWithOpts(sdkClient.FromEnv)

if err != nil {
log.Fatalf("Error instantiating Docker client: %s", err)
}

return dockerClient{
api: cli,
pullImages: pullImages,
removeVolumes: removeVolumes,
includeStopped: includeStopped,
reviveStopped: reviveStopped,
api: cli,
pullImages: pullImages,
removeVolumes: removeVolumes,
includeStopped: includeStopped,
reviveStopped: reviveStopped,
includeRestarting: includeRestarting,
}
}

type dockerClient struct {
api sdkClient.CommonAPIClient
pullImages bool
removeVolumes bool
includeStopped bool
reviveStopped bool
api sdkClient.CommonAPIClient
pullImages bool
removeVolumes bool
includeStopped bool
reviveStopped bool
includeRestarting bool
}

func (client dockerClient) ListContainers(fn t.Filter) ([]Container, error) {
cs := []Container{}
bg := context.Background()

if client.includeStopped {
log.Debug("Retrieving containers including stopped and exited")
if client.includeStopped && client.includeRestarting {
log.Debug("Retrieving running, stopped, restarting and exited containers")
} else if client.includeStopped {
log.Debug("Retrieving running, stopped and exited containers")
} else if client.includeRestarting {
log.Debug("Retrieving running and restarting containers")
} else {
log.Debug("Retrieving running containers")
}
Expand Down Expand Up @@ -108,6 +115,10 @@ func (client dockerClient) createListFilter() filters.Args {
filterArgs.Add("status", "exited")
}

if client.includeRestarting {
filterArgs.Add("status", "restarting")
}

return filterArgs
}

Expand Down
41 changes: 40 additions & 1 deletion pkg/container/container_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package container

import (
"testing"

"github.com/containrrr/watchtower/pkg/container/mocks"
"github.com/containrrr/watchtower/pkg/filters"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
cli "github.com/docker/docker/client"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
)

func TestContainer(t *testing.T) {
Expand Down Expand Up @@ -68,6 +69,44 @@ var _ = Describe("the container", func() {
Expect(len(containers) > 0).To(BeTrue())
})
})
When(`listing containers with the "include restart" option`, func() {
It("should return both stopped, restarting and running containers", func() {
client = dockerClient{
api: docker,
pullImages: false,
includeRestarting: true,
}
containers, err := client.ListContainers(filters.NoFilter)
Expect(err).NotTo(HaveOccurred())
RestartingContainerFound := false
for _, ContainerRunning := range containers {
if ContainerRunning.containerInfo.State.Restarting {
RestartingContainerFound = true
}
}
Expect(RestartingContainerFound).To(BeTrue())
Expect(RestartingContainerFound).NotTo(BeFalse())
})
})
When(`listing containers without restarting ones`, func() {
It("should not return restarting containers", func() {
client = dockerClient{
api: docker,
pullImages: false,
includeRestarting: false,
}
containers, err := client.ListContainers(filters.NoFilter)
Expect(err).NotTo(HaveOccurred())
RestartingContainerFound := false
for _, ContainerRunning := range containers {
if ContainerRunning.containerInfo.State.Restarting {
RestartingContainerFound = true
}
}
Expect(RestartingContainerFound).To(BeFalse())
Expect(RestartingContainerFound).NotTo(BeTrue())
})
})
})
When("asked for metadata", func() {
var c *Container
Expand Down
31 changes: 27 additions & 4 deletions pkg/container/mocks/ApiServer.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package mocks

import (
"encoding/json"
"fmt"
"github.com/sirupsen/logrus"
"io/ioutil"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"

"github.com/docker/docker/api/types"
"github.com/sirupsen/logrus"
)

// NewMockAPIServer returns a mocked docker api server that responds to some fixed requests
Expand All @@ -18,16 +21,36 @@ func NewMockAPIServer() *httptest.Server {
logrus.Debug("Mock server has received a HTTP call on ", r.URL)
var response = ""

if isRequestFor("filters=%7B%22status%22%3A%7B%22running%22%3Atrue%7D%7D&limit=0", r) {
response = getMockJSONFromDisk("./mocks/data/containers.json")
} else if isRequestFor("filters=%7B%22status%22%3A%7B%22created%22%3Atrue%2C%22exited%22%3Atrue%2C%22running%22%3Atrue%7D%7D&limit=0", r) {
if isRequestFor("filters=", r) {

Filters := r.URL.Query().Get("filters")
var result map[string]interface{}
json.Unmarshal([]byte(Filters), &result)
status := result["status"].(map[string]interface{})

response = getMockJSONFromDisk("./mocks/data/containers.json")
var x2 []types.Container
var containers []types.Container
json.Unmarshal([]byte(response), &containers)
for _, v := range containers {
for key := range status {
if v.State == key {
x2 = append(x2, v)
}
}
}

b, _ := json.Marshal(x2)
response = string(b)

} else if isRequestFor("containers/json?limit=0", r) {
response = getMockJSONFromDisk("./mocks/data/containers.json")
} else if isRequestFor("ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65", r) {
response = getMockJSONFromDisk("./mocks/data/container_stopped.json")
} else if isRequestFor("b978af0b858aa8855cce46b628817d4ed58e58f2c4f66c9b9c5449134ed4c008", r) {
response = getMockJSONFromDisk("./mocks/data/container_running.json")
} else if isRequestFor("ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b67", r) {
response = getMockJSONFromDisk("./mocks/data/container_restarting.json")
} else if isRequestFor("sha256:19d07168491a3f9e2798a9bed96544e34d57ddc4757a4ac5bb199dea896c87fd", r) {
response = getMockJSONFromDisk("./mocks/data/image01.json")
} else if isRequestFor("sha256:4dbc5f9c07028a985e14d1393e849ea07f68804c4293050d5a641b138db72daa", r) {
Expand Down
Loading

0 comments on commit 2842b97

Please sign in to comment.