From 026a04b59b2a09a9287cec57af50e77ce2f8f2c6 Mon Sep 17 00:00:00 2001 From: Fabrizio Steiner Date: Fri, 2 Mar 2018 17:22:42 +0100 Subject: [PATCH] implemented enableLabel by a Filter --- README.md | 6 ++--- actions/check.go | 4 +-- actions/update.go | 21 ++------------- container/client.go | 25 +++++------------- container/filters.go | 61 ++++++++++++++++++++++++++++++++++++++++++++ main.go | 8 ++++-- 6 files changed, 79 insertions(+), 46 deletions(-) create mode 100644 container/filters.go diff --git a/README.md b/README.md index 8b3b260..4f84340 100644 --- a/README.md +++ b/README.md @@ -129,9 +129,9 @@ docker run -d --label=com.centurylinklabs.watchtower.stop-signal=SIGHUP someimag ## Selectively Watching Containers -By default, watchtower will watch all containers. -However, sometimes only some containers should be updated. -If you need to selectively watch containers, pass the --label-enable flag on startup and set the *com.centurylinklabs.watchtower.enable* label with a value of true for the containers you want to watch. +By default, watchtower will watch all containers. However, sometimes only some containers should be updated. + +If you need to only include only some containers, pass the --label-enable flag on startup and set the *com.centurylinklabs.watchtower.enable* label with a value of true for the containers you want to watch. ```docker LABEL com.centurylinklabs.watchtower.enable="true" diff --git a/actions/check.go b/actions/check.go index 8fb5786..5354dd7 100644 --- a/actions/check.go +++ b/actions/check.go @@ -6,14 +6,12 @@ import ( "github.com/v2tec/watchtower/container" ) -func watchtowerContainersFilter(c container.Container) bool { return c.IsWatchtower() } - // CheckPrereqs will ensure that there are not multiple instances of the // watchtower running simultaneously. If multiple watchtower containers are // detected, this function will stop and remove all but the most recently // started container. func CheckPrereqs(client container.Client, cleanup bool) error { - containers, err := client.ListContainers(watchtowerContainersFilter) + containers, err := client.ListContainers(container.WatchtowerContainersFilter) if err != nil { return err } diff --git a/actions/update.go b/actions/update.go index 3866d4b..4d488a5 100644 --- a/actions/update.go +++ b/actions/update.go @@ -12,31 +12,14 @@ var ( letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") ) -func allContainersFilter(container.Container) bool { return true } - -func containerFilter(names []string) container.Filter { - if len(names) == 0 { - return allContainersFilter - } - - return func(c container.Container) bool { - for _, name := range names { - if (name == c.Name()) || (name == c.Name()[1:]) { - return true - } - } - return false - } -} - // Update looks at the running Docker containers to see if any of the images // used to start those containers have been updated. If a change is detected in // any of the images, the associated containers are stopped and restarted with // the new image. -func Update(client container.Client, names []string, cleanup bool, noRestart bool, timeout time.Duration) error { +func Update(client container.Client, filter container.Filter, cleanup bool, noRestart bool, timeout time.Duration) error { log.Debug("Checking containers for updated images") - containers, err := client.ListContainers(containerFilter(names)) + containers, err := client.ListContainers(filter) if err != nil { return err } diff --git a/container/client.go b/container/client.go index 340c51b..5a75163 100644 --- a/container/client.go +++ b/container/client.go @@ -5,10 +5,10 @@ import ( "io/ioutil" "time" - log "github.com/sirupsen/logrus" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" dockerclient "github.com/docker/docker/client" + log "github.com/sirupsen/logrus" "golang.org/x/net/context" ) @@ -16,10 +16,6 @@ const ( defaultStopSignal = "SIGTERM" ) -// A Filter is a prototype for a function that can be used to filter the -// results from a call to the ListContainers() method on the Client. -type Filter func(Container) bool - // A Client is the interface through which watchtower interacts with the // Docker API. type Client interface { @@ -37,20 +33,19 @@ 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, enableLabel bool) Client { +func NewClient(pullImages bool) Client { cli, err := dockerclient.NewEnvClient() if err != nil { log.Fatalf("Error instantiating Docker client: %s", err) } - return dockerClient{api: cli, pullImages: pullImages, enableLabel: enableLabel} + return dockerClient{api: cli, pullImages: pullImages} } type dockerClient struct { - api *dockerclient.Client - pullImages bool - enableLabel bool + api *dockerclient.Client + pullImages bool } func (client dockerClient) ListContainers(fn Filter) ([]Container, error) { @@ -62,6 +57,7 @@ func (client dockerClient) ListContainers(fn Filter) ([]Container, error) { runningContainers, err := client.api.ContainerList( bg, types.ContainerListOptions{}) + if err != nil { return nil, err } @@ -79,15 +75,6 @@ func (client dockerClient) ListContainers(fn Filter) ([]Container, error) { c := Container{containerInfo: &containerInfo, imageInfo: &imageInfo} - if client.enableLabel { - // If label filtering is enabled, containers should only be enabled - // if the label is specifically set to true. - enabledLabel, ok := c.Enabled() - if !ok || !enabledLabel { - continue - } - } - if fn(c) { cs = append(cs, c) } diff --git a/container/filters.go b/container/filters.go new file mode 100644 index 0000000..894cbc3 --- /dev/null +++ b/container/filters.go @@ -0,0 +1,61 @@ +package container + +// A Filter is a prototype for a function that can be used to filter the +// results from a call to the ListContainers() method on the Client. +type Filter func(FilterableContainer) bool + +// A FilterableContainer is the interface which is used to filter +// containers. +type FilterableContainer interface { + Name() string + IsWatchtower() bool + Enabled() (bool, bool) +} + +// WatchtowerContainersFilter filters only watchtower containers +func WatchtowerContainersFilter(c FilterableContainer) bool { return c.IsWatchtower() } + +// Filter no containers and returns all +func noFilter(FilterableContainer) bool { return true } + +// Filters containers which don't have a specified name +func filterByNames(names []string, baseFilter Filter) Filter { + if len(names) == 0 { + return baseFilter + } + + return func(c FilterableContainer) bool { + for _, name := range names { + if (name == c.Name()) || (name == c.Name()[1:]) { + return baseFilter(c) + } + } + return false + } +} + +// Filters out containers that don't have the 'enableLabel' +func filterByEnableLabel(baseFilter Filter) Filter { + return func(c FilterableContainer) bool { + // If label filtering is enabled, containers should only be enabled + // if the label is specifically set to true. + enabledLabel, ok := c.Enabled() + if !ok || !enabledLabel { + return false + } + + return baseFilter(c) + } +} + +// BuildFilter creates the needed filter of containers +func BuildFilter(names []string, enableLabel bool) Filter { + filter := noFilter + filter = filterByNames(names, filter) + if enableLabel { + // If label filtering is enabled, containers should only be considered + // if the label is specifically set. + filter = filterByEnableLabel(filter) + } + return filter +} diff --git a/main.go b/main.go index c84f26c..97fe15d 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ var ( scheduleSpec string cleanup bool noRestart bool + enableLabel bool notifier *notifications.Notifier timeout time.Duration ) @@ -189,6 +190,7 @@ func before(c *cli.Context) error { if timeout < 0 { log.Fatal("Please specify a positive value for timeout value.") } + enableLabel = c.GlobalBool("label-enable") // configure environment vars for client err := envConfig(c) @@ -196,7 +198,7 @@ func before(c *cli.Context) error { return err } - client = container.NewClient(!c.GlobalBool("no-pull"), c.GlobalBool("label-enable")) + client = container.NewClient(!c.GlobalBool("no-pull")) notifier = notifications.NewNotifier(c) return nil @@ -209,6 +211,8 @@ func start(c *cli.Context) error { log.Fatal(err) } + filter := container.BuildFilter(names, enableLabel) + tryLockSem := make(chan bool, 1) tryLockSem <- true @@ -220,7 +224,7 @@ func start(c *cli.Context) error { case v := <-tryLockSem: defer func() { tryLockSem <- v }() notifier.StartNotification() - if err := actions.Update(client, names, cleanup, noRestart, timeout); err != nil { + if err := actions.Update(client, filter, cleanup, noRestart, timeout); err != nil { log.Println(err) } notifier.SendNotification()