mirror of
https://github.com/containrrr/watchtower.git
synced 2025-12-14 06:06:38 +01:00
Merge pull request #173 from v2tec/filters
Filters refactoring and always exclude disabled containers
This commit is contained in:
commit
88a7a084a9
12 changed files with 333 additions and 135 deletions
18
README.md
18
README.md
|
|
@ -129,9 +129,21 @@ docker run -d --label=com.centurylinklabs.watchtower.stop-signal=SIGHUP someimag
|
||||||
|
|
||||||
## Selectively Watching Containers
|
## Selectively Watching Containers
|
||||||
|
|
||||||
By default, watchtower will watch all containers.
|
By default, watchtower will watch all containers. However, sometimes only some containers should be updated.
|
||||||
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.
|
If you need to exclude some containers, set the *com.centurylinklabs.watchtower.enable* label to `false`.
|
||||||
|
|
||||||
|
```docker
|
||||||
|
LABEL com.centurylinklabs.watchtower.enable="false"
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, it can be specified as part of the `docker run` command line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d --label=com.centurylinklabs.watchtower.enable=false someimage
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
```docker
|
||||||
LABEL com.centurylinklabs.watchtower.enable="true"
|
LABEL com.centurylinklabs.watchtower.enable="true"
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,12 @@ import (
|
||||||
"github.com/v2tec/watchtower/container"
|
"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
|
// CheckPrereqs will ensure that there are not multiple instances of the
|
||||||
// watchtower running simultaneously. If multiple watchtower containers are
|
// watchtower running simultaneously. If multiple watchtower containers are
|
||||||
// detected, this function will stop and remove all but the most recently
|
// detected, this function will stop and remove all but the most recently
|
||||||
// started container.
|
// started container.
|
||||||
func CheckPrereqs(client container.Client, cleanup bool) error {
|
func CheckPrereqs(client container.Client, cleanup bool) error {
|
||||||
containers, err := client.ListContainers(watchtowerContainersFilter)
|
containers, err := client.ListContainers(container.WatchtowerContainersFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,31 +12,14 @@ var (
|
||||||
letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
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
|
// 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
|
// 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
|
// any of the images, the associated containers are stopped and restarted with
|
||||||
// the new image.
|
// 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")
|
log.Debug("Checking containers for updated images")
|
||||||
|
|
||||||
containers, err := client.ListContainers(containerFilter(names))
|
containers, err := client.ListContainers(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
dockerclient "github.com/docker/docker/client"
|
dockerclient "github.com/docker/docker/client"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -16,10 +16,6 @@ const (
|
||||||
defaultStopSignal = "SIGTERM"
|
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
|
// A Client is the interface through which watchtower interacts with the
|
||||||
// Docker API.
|
// Docker API.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
|
|
@ -37,20 +33,19 @@ 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 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}
|
||||||
}
|
}
|
||||||
|
|
||||||
type dockerClient struct {
|
type dockerClient struct {
|
||||||
api *dockerclient.Client
|
api *dockerclient.Client
|
||||||
pullImages bool
|
pullImages bool
|
||||||
enableLabel bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client dockerClient) ListContainers(fn Filter) ([]Container, error) {
|
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(
|
runningContainers, err := client.api.ContainerList(
|
||||||
bg,
|
bg,
|
||||||
types.ContainerListOptions{})
|
types.ContainerListOptions{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -79,15 +75,6 @@ func (client dockerClient) ListContainers(fn Filter) ([]Container, error) {
|
||||||
|
|
||||||
c := Container{containerInfo: &containerInfo, imageInfo: &imageInfo}
|
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) {
|
if fn(c) {
|
||||||
cs = append(cs, c)
|
cs = append(cs, c)
|
||||||
}
|
}
|
||||||
|
|
@ -229,10 +216,9 @@ func (client dockerClient) IsContainerStale(c Container) (bool, error) {
|
||||||
if newImageInfo.ID != oldImageInfo.ID {
|
if newImageInfo.ID != oldImageInfo.ID {
|
||||||
log.Infof("Found new %s image (%s)", imageName, newImageInfo.ID)
|
log.Infof("Found new %s image (%s)", imageName, newImageInfo.ID)
|
||||||
return true, nil
|
return true, nil
|
||||||
} else {
|
|
||||||
log.Debugf("No new images found for %s", c.Name())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("No new images found for %s", c.Name())
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ func (c Container) runtimeConfig() *dockercontainer.Config {
|
||||||
config.Volumes = structMapSubtract(config.Volumes, imageConfig.Volumes)
|
config.Volumes = structMapSubtract(config.Volumes, imageConfig.Volumes)
|
||||||
|
|
||||||
// subtract ports exposed in image from container
|
// subtract ports exposed in image from container
|
||||||
for k, _ := range config.ExposedPorts {
|
for k := range config.ExposedPorts {
|
||||||
if _, ok := imageConfig.ExposedPorts[k]; ok {
|
if _, ok := imageConfig.ExposedPorts[k]; ok {
|
||||||
delete(config.ExposedPorts, k)
|
delete(config.ExposedPorts, k)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
container/filters.go
Normal file
75
container/filters.go
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
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 considered
|
||||||
|
// if the label is specifically set.
|
||||||
|
_, ok := c.Enabled()
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseFilter(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filters out containers that have a 'enableLabel' and is set to disable.
|
||||||
|
func filterByDisabledLabel(baseFilter Filter) Filter {
|
||||||
|
return func(c FilterableContainer) bool {
|
||||||
|
enabledLabel, ok := c.Enabled()
|
||||||
|
if ok && !enabledLabel {
|
||||||
|
// If the label has been set and it demands a disable
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
filter = filterByDisabledLabel(filter)
|
||||||
|
return filter
|
||||||
|
}
|
||||||
153
container/filters_test.go
Normal file
153
container/filters_test.go
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/v2tec/watchtower/container/mocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWatchtowerContainersFilter(t *testing.T) {
|
||||||
|
container := new(mocks.FilterableContainer)
|
||||||
|
|
||||||
|
container.On("IsWatchtower").Return(true)
|
||||||
|
|
||||||
|
assert.True(t, WatchtowerContainersFilter(container))
|
||||||
|
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoFilter(t *testing.T) {
|
||||||
|
container := new(mocks.FilterableContainer)
|
||||||
|
|
||||||
|
assert.True(t, noFilter(container))
|
||||||
|
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterByNames(t *testing.T) {
|
||||||
|
var names []string
|
||||||
|
|
||||||
|
filter := filterByNames(names, nil)
|
||||||
|
assert.Nil(t, filter)
|
||||||
|
|
||||||
|
names = append(names, "test")
|
||||||
|
|
||||||
|
filter = filterByNames(names, noFilter)
|
||||||
|
assert.NotNil(t, filter)
|
||||||
|
|
||||||
|
container := new(mocks.FilterableContainer)
|
||||||
|
container.On("Name").Return("test")
|
||||||
|
assert.True(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Name").Return("NoTest")
|
||||||
|
assert.False(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterByEnableLabel(t *testing.T) {
|
||||||
|
filter := filterByEnableLabel(noFilter)
|
||||||
|
assert.NotNil(t, filter)
|
||||||
|
|
||||||
|
container := new(mocks.FilterableContainer)
|
||||||
|
container.On("Enabled").Return(true, true)
|
||||||
|
assert.True(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Enabled").Return(false, true)
|
||||||
|
assert.True(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Enabled").Return(false, false)
|
||||||
|
assert.False(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterByDisabledLabel(t *testing.T) {
|
||||||
|
filter := filterByDisabledLabel(noFilter)
|
||||||
|
assert.NotNil(t, filter)
|
||||||
|
|
||||||
|
container := new(mocks.FilterableContainer)
|
||||||
|
container.On("Enabled").Return(true, true)
|
||||||
|
assert.True(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Enabled").Return(false, true)
|
||||||
|
assert.False(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Enabled").Return(false, false)
|
||||||
|
assert.True(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildFilter(t *testing.T) {
|
||||||
|
var names []string
|
||||||
|
names = append(names, "test")
|
||||||
|
|
||||||
|
filter := BuildFilter(names, false)
|
||||||
|
|
||||||
|
container := new(mocks.FilterableContainer)
|
||||||
|
container.On("Name").Return("Invalid")
|
||||||
|
container.On("Enabled").Return(false, false)
|
||||||
|
assert.False(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Name").Return("test")
|
||||||
|
container.On("Enabled").Return(false, false)
|
||||||
|
assert.True(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Name").Return("Invalid")
|
||||||
|
container.On("Enabled").Return(true, true)
|
||||||
|
assert.False(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Name").Return("test")
|
||||||
|
container.On("Enabled").Return(true, true)
|
||||||
|
assert.True(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Enabled").Return(false, true)
|
||||||
|
assert.False(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildFilterEnableLabel(t *testing.T) {
|
||||||
|
var names []string
|
||||||
|
names = append(names, "test")
|
||||||
|
|
||||||
|
filter := BuildFilter(names, true)
|
||||||
|
|
||||||
|
container := new(mocks.FilterableContainer)
|
||||||
|
container.On("Enabled").Return(false, false)
|
||||||
|
assert.False(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Name").Return("Invalid")
|
||||||
|
container.On("Enabled").Twice().Return(true, true)
|
||||||
|
assert.False(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Name").Return("test")
|
||||||
|
container.On("Enabled").Twice().Return(true, true)
|
||||||
|
assert.True(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
|
||||||
|
container = new(mocks.FilterableContainer)
|
||||||
|
container.On("Enabled").Return(false, true)
|
||||||
|
assert.False(t, filter(container))
|
||||||
|
container.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
package mockclient
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/v2tec/watchtower/container"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MockClient struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) ListContainers(cf container.Filter) ([]container.Container, error) {
|
|
||||||
args := m.Called(cf)
|
|
||||||
return args.Get(0).([]container.Container), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) StopContainer(c container.Container, timeout time.Duration) error {
|
|
||||||
args := m.Called(c, timeout)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) StartContainer(c container.Container) error {
|
|
||||||
args := m.Called(c)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) RenameContainer(c container.Container, name string) error {
|
|
||||||
args := m.Called(c, name)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) IsContainerStale(c container.Container) (bool, error) {
|
|
||||||
args := m.Called(c)
|
|
||||||
return args.Bool(0), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) RemoveImage(c container.Container) error {
|
|
||||||
args := m.Called(c)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package mockclient
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/v2tec/watchtower/container"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMockInterface(t *testing.T) {
|
|
||||||
iface := reflect.TypeOf((*container.Client)(nil)).Elem()
|
|
||||||
mock := &MockClient{}
|
|
||||||
|
|
||||||
if !reflect.TypeOf(mock).Implements(iface) {
|
|
||||||
t.Fatalf("Mock does not implement the Client interface")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
57
container/mocks/FilterableContainer.go
Normal file
57
container/mocks/FilterableContainer.go
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
// FilterableContainer is an autogenerated mock type for the FilterableContainer type
|
||||||
|
type FilterableContainer struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled provides a mock function with given fields:
|
||||||
|
func (_m *FilterableContainer) Enabled() (bool, bool) {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
var r0 bool
|
||||||
|
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 bool
|
||||||
|
if rf, ok := ret.Get(1).(func() bool); ok {
|
||||||
|
r1 = rf()
|
||||||
|
} else {
|
||||||
|
r1 = ret.Get(1).(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsWatchtower provides a mock function with given fields:
|
||||||
|
func (_m *FilterableContainer) IsWatchtower() bool {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
var r0 bool
|
||||||
|
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name provides a mock function with given fields:
|
||||||
|
func (_m *FilterableContainer) Name() string {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
var r0 string
|
||||||
|
if rf, ok := ret.Get(0).(func() string); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
@ -5,20 +5,18 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/reference"
|
"github.com/docker/docker/api/types/reference"
|
||||||
"github.com/docker/docker/cli/command"
|
"github.com/docker/docker/cli/command"
|
||||||
"github.com/docker/docker/cliconfig"
|
"github.com/docker/docker/cliconfig"
|
||||||
"github.com/docker/docker/cliconfig/configfile"
|
"github.com/docker/docker/cliconfig/configfile"
|
||||||
"github.com/docker/docker/cliconfig/credentials"
|
"github.com/docker/docker/cliconfig/credentials"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
// EncodedAuth returns an encoded auth config for the given registry
|
||||||
* Return an encoded auth config for the given registry
|
// loaded from environment variables or docker config
|
||||||
* loaded from environment variables or docker config
|
// as available in that order
|
||||||
* as available in that order
|
|
||||||
*/
|
|
||||||
func EncodedAuth(ref string) (string, error) {
|
func EncodedAuth(ref string) (string, error) {
|
||||||
auth, err := EncodedEnvAuth(ref)
|
auth, err := EncodedEnvAuth(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -27,11 +25,9 @@ func EncodedAuth(ref string) (string, error) {
|
||||||
return auth, err
|
return auth, err
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// EncodedEnvAuth returns an encoded auth config for the given registry
|
||||||
* Return an encoded auth config for the given registry
|
// loaded from environment variables
|
||||||
* loaded from environment variables
|
// Returns an error if authentication environment variables have not been set
|
||||||
* Returns an error if authentication environment variables have not been set
|
|
||||||
*/
|
|
||||||
func EncodedEnvAuth(ref string) (string, error) {
|
func EncodedEnvAuth(ref string) (string, error) {
|
||||||
username := os.Getenv("REPO_USER")
|
username := os.Getenv("REPO_USER")
|
||||||
password := os.Getenv("REPO_PASS")
|
password := os.Getenv("REPO_PASS")
|
||||||
|
|
@ -42,17 +38,14 @@ func EncodedEnvAuth(ref string) (string, error) {
|
||||||
}
|
}
|
||||||
log.Debugf("Loaded auth credentials %s for %s", auth, ref)
|
log.Debugf("Loaded auth credentials %s for %s", auth, ref)
|
||||||
return EncodeAuth(auth)
|
return EncodeAuth(auth)
|
||||||
} else {
|
|
||||||
return "", errors.New("Registry auth environment variables (REPO_USER, REPO_PASS) not set")
|
|
||||||
}
|
}
|
||||||
|
return "", errors.New("Registry auth environment variables (REPO_USER, REPO_PASS) not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// EncodedConfigAuth returns an encoded auth config for the given registry
|
||||||
* Return an encoded auth config for the given registry
|
// loaded from the docker config
|
||||||
* loaded from the docker config
|
// Returns an empty string if credentials cannot be found for the referenced server
|
||||||
* Returns an empty string if credentials cannot be found for the referenced server
|
// The docker config must be mounted on the container
|
||||||
* The docker config must be mounted on the container
|
|
||||||
*/
|
|
||||||
func EncodedConfigAuth(ref string) (string, error) {
|
func EncodedConfigAuth(ref string) (string, error) {
|
||||||
server, err := ParseServerAddress(ref)
|
server, err := ParseServerAddress(ref)
|
||||||
configDir := os.Getenv("DOCKER_CONFIG")
|
configDir := os.Getenv("DOCKER_CONFIG")
|
||||||
|
|
@ -92,18 +85,14 @@ func CredentialsStore(configFile configfile.ConfigFile) credentials.Store {
|
||||||
return credentials.NewFileStore(&configFile)
|
return credentials.NewFileStore(&configFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// EncodeAuth Base64 encode an AuthConfig struct for transmission over HTTP
|
||||||
* Base64 encode an AuthConfig struct for transmission over HTTP
|
|
||||||
*/
|
|
||||||
func EncodeAuth(auth types.AuthConfig) (string, error) {
|
func EncodeAuth(auth types.AuthConfig) (string, error) {
|
||||||
return command.EncodeAuthToBase64(auth)
|
return command.EncodeAuthToBase64(auth)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// DefaultAuthHandler will be invoked if an AuthConfig is rejected
|
||||||
* This function will be invoked if an AuthConfig is rejected
|
// It could be used to return a new value for the "X-Registry-Auth" authentication header,
|
||||||
* It could be used to return a new value for the "X-Registry-Auth" authentication header,
|
// but there's no point trying again with the same value as used in AuthConfig
|
||||||
* but there's no point trying again with the same value as used in AuthConfig
|
|
||||||
*/
|
|
||||||
func DefaultAuthHandler() (string, error) {
|
func DefaultAuthHandler() (string, error) {
|
||||||
log.Debug("Authentication request was rejected. Trying again without authentication")
|
log.Debug("Authentication request was rejected. Trying again without authentication")
|
||||||
return "", nil
|
return "", nil
|
||||||
|
|
|
||||||
8
main.go
8
main.go
|
|
@ -29,6 +29,7 @@ var (
|
||||||
scheduleSpec string
|
scheduleSpec string
|
||||||
cleanup bool
|
cleanup bool
|
||||||
noRestart bool
|
noRestart bool
|
||||||
|
enableLabel bool
|
||||||
notifier *notifications.Notifier
|
notifier *notifications.Notifier
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
)
|
)
|
||||||
|
|
@ -189,6 +190,7 @@ func before(c *cli.Context) error {
|
||||||
if timeout < 0 {
|
if timeout < 0 {
|
||||||
log.Fatal("Please specify a positive value for timeout value.")
|
log.Fatal("Please specify a positive value for timeout value.")
|
||||||
}
|
}
|
||||||
|
enableLabel = c.GlobalBool("label-enable")
|
||||||
|
|
||||||
// configure environment vars for client
|
// configure environment vars for client
|
||||||
err := envConfig(c)
|
err := envConfig(c)
|
||||||
|
|
@ -196,7 +198,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"))
|
||||||
notifier = notifications.NewNotifier(c)
|
notifier = notifications.NewNotifier(c)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -209,6 +211,8 @@ func start(c *cli.Context) error {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filter := container.BuildFilter(names, enableLabel)
|
||||||
|
|
||||||
tryLockSem := make(chan bool, 1)
|
tryLockSem := make(chan bool, 1)
|
||||||
tryLockSem <- true
|
tryLockSem <- true
|
||||||
|
|
||||||
|
|
@ -220,7 +224,7 @@ func start(c *cli.Context) error {
|
||||||
case v := <-tryLockSem:
|
case v := <-tryLockSem:
|
||||||
defer func() { tryLockSem <- v }()
|
defer func() { tryLockSem <- v }()
|
||||||
notifier.StartNotification()
|
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)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
notifier.SendNotification()
|
notifier.SendNotification()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue