mirror of https://github.com/containrrr/watchtower
				
				
				
			Merge pull request #173 from v2tec/filters
Filters refactoring and always exclude disabled containerspull/174/head
						commit
						88a7a084a9
					
				@ -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
 | 
			
		||||
}
 | 
			
		||||
@ -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")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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
 | 
			
		||||
}
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue