watchtower/container/client_test.go
Brian DeHamer e21c21ec3b Account for latency in container removal
Under certain conditions when watchtower is monitoring a Docker Swarm
cluster there would be cases where an updated container could not be
started because the old hadn't yet been removed (name conflicts, mapped
port conflicts, etc). We suspect that this has something to do with the
async nature of swarm and even though we've asked the swarm master to
remove a container it may not be completely removed from the associated
node. The fix is to do some polling after the remove container call to
ensure that the container is truly gone before proceeding.
2015-07-28 19:29:20 +00:00

401 lines
11 KiB
Go

package container
import (
"errors"
"testing"
"time"
"github.com/samalba/dockerclient"
"github.com/samalba/dockerclient/mockclient"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func allContainers(Container) bool { return true }
func noContainers(Container) bool { return false }
func TestListContainers_Success(t *testing.T) {
ci := &dockerclient.ContainerInfo{Image: "abc123", Config: &dockerclient.ContainerConfig{Image: "img"}}
ii := &dockerclient.ImageInfo{}
api := mockclient.NewMockClient()
api.On("ListContainers", false, false, "").Return([]dockerclient.Container{{Id: "foo", Names: []string{"bar"}}}, nil)
api.On("InspectContainer", "foo").Return(ci, nil)
api.On("InspectImage", "abc123").Return(ii, nil)
client := DockerClient{api: api}
cs, err := client.ListContainers(allContainers)
assert.NoError(t, err)
assert.Len(t, cs, 1)
assert.Equal(t, ci, cs[0].containerInfo)
assert.Equal(t, ii, cs[0].imageInfo)
api.AssertExpectations(t)
}
func TestListContainers_Filter(t *testing.T) {
ci := &dockerclient.ContainerInfo{Image: "abc123", Config: &dockerclient.ContainerConfig{Image: "img"}}
ii := &dockerclient.ImageInfo{}
api := mockclient.NewMockClient()
api.On("ListContainers", false, false, "").Return([]dockerclient.Container{{Id: "foo", Names: []string{"bar"}}}, nil)
api.On("InspectContainer", "foo").Return(ci, nil)
api.On("InspectImage", "abc123").Return(ii, nil)
client := DockerClient{api: api}
cs, err := client.ListContainers(noContainers)
assert.NoError(t, err)
assert.Len(t, cs, 0)
api.AssertExpectations(t)
}
func TestListContainers_ListError(t *testing.T) {
api := mockclient.NewMockClient()
api.On("ListContainers", false, false, "").Return([]dockerclient.Container{}, errors.New("oops"))
client := DockerClient{api: api}
_, err := client.ListContainers(allContainers)
assert.Error(t, err)
assert.EqualError(t, err, "oops")
api.AssertExpectations(t)
}
func TestListContainers_InspectContainerError(t *testing.T) {
api := mockclient.NewMockClient()
api.On("ListContainers", false, false, "").Return([]dockerclient.Container{{Id: "foo", Names: []string{"bar"}}}, nil)
api.On("InspectContainer", "foo").Return(&dockerclient.ContainerInfo{}, errors.New("uh-oh"))
client := DockerClient{api: api}
_, err := client.ListContainers(allContainers)
assert.Error(t, err)
assert.EqualError(t, err, "uh-oh")
api.AssertExpectations(t)
}
func TestListContainers_InspectImageError(t *testing.T) {
ci := &dockerclient.ContainerInfo{Image: "abc123", Config: &dockerclient.ContainerConfig{Image: "img"}}
ii := &dockerclient.ImageInfo{}
api := mockclient.NewMockClient()
api.On("ListContainers", false, false, "").Return([]dockerclient.Container{{Id: "foo", Names: []string{"bar"}}}, nil)
api.On("InspectContainer", "foo").Return(ci, nil)
api.On("InspectImage", "abc123").Return(ii, errors.New("whoops"))
client := DockerClient{api: api}
_, err := client.ListContainers(allContainers)
assert.Error(t, err)
assert.EqualError(t, err, "whoops")
api.AssertExpectations(t)
}
func TestStopContainer_DefaultSuccess(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Id: "abc123",
Config: &dockerclient.ContainerConfig{},
},
}
ci := &dockerclient.ContainerInfo{
State: &dockerclient.State{
Running: false,
},
}
api := mockclient.NewMockClient()
api.On("KillContainer", "abc123", "SIGTERM").Return(nil)
api.On("InspectContainer", "abc123").Return(ci, nil).Once()
api.On("RemoveContainer", "abc123", true, false).Return(nil)
api.On("InspectContainer", "abc123").Return(&dockerclient.ContainerInfo{}, errors.New("Not Found"))
client := DockerClient{api: api}
err := client.StopContainer(c, time.Second)
assert.NoError(t, err)
api.AssertExpectations(t)
}
func TestStopContainer_CustomSignalSuccess(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Id: "abc123",
Config: &dockerclient.ContainerConfig{
Labels: map[string]string{"com.centurylinklabs.watchtower.stop-signal": "SIGUSR1"}},
},
}
ci := &dockerclient.ContainerInfo{
State: &dockerclient.State{
Running: false,
},
}
api := mockclient.NewMockClient()
api.On("KillContainer", "abc123", "SIGUSR1").Return(nil)
api.On("InspectContainer", "abc123").Return(ci, nil).Once()
api.On("RemoveContainer", "abc123", true, false).Return(nil)
api.On("InspectContainer", "abc123").Return(&dockerclient.ContainerInfo{}, errors.New("Not Found"))
client := DockerClient{api: api}
err := client.StopContainer(c, time.Second)
assert.NoError(t, err)
api.AssertExpectations(t)
}
func TestStopContainer_KillContainerError(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Id: "abc123",
Config: &dockerclient.ContainerConfig{},
},
}
api := mockclient.NewMockClient()
api.On("KillContainer", "abc123", "SIGTERM").Return(errors.New("oops"))
client := DockerClient{api: api}
err := client.StopContainer(c, time.Second)
assert.Error(t, err)
assert.EqualError(t, err, "oops")
api.AssertExpectations(t)
}
func TestStopContainer_RemoveContainerError(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Id: "abc123",
Config: &dockerclient.ContainerConfig{},
},
}
api := mockclient.NewMockClient()
api.On("KillContainer", "abc123", "SIGTERM").Return(nil)
api.On("InspectContainer", "abc123").Return(&dockerclient.ContainerInfo{}, errors.New("dangit"))
api.On("RemoveContainer", "abc123", true, false).Return(errors.New("whoops"))
client := DockerClient{api: api}
err := client.StopContainer(c, time.Second)
assert.Error(t, err)
assert.EqualError(t, err, "whoops")
api.AssertExpectations(t)
}
func TestStartContainer_Success(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Config: &dockerclient.ContainerConfig{},
HostConfig: &dockerclient.HostConfig{},
},
imageInfo: &dockerclient.ImageInfo{
Config: &dockerclient.ContainerConfig{},
},
}
api := mockclient.NewMockClient()
api.On("CreateContainer", mock.AnythingOfType("*dockerclient.ContainerConfig"), "foo").Return("def789", nil)
api.On("StartContainer", "def789", mock.AnythingOfType("*dockerclient.HostConfig")).Return(nil)
client := DockerClient{api: api}
err := client.StartContainer(c)
assert.NoError(t, err)
api.AssertExpectations(t)
}
func TestStartContainer_CreateContainerError(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Config: &dockerclient.ContainerConfig{},
HostConfig: &dockerclient.HostConfig{},
},
imageInfo: &dockerclient.ImageInfo{
Config: &dockerclient.ContainerConfig{},
},
}
api := mockclient.NewMockClient()
api.On("CreateContainer", mock.Anything, "foo").Return("", errors.New("oops"))
client := DockerClient{api: api}
err := client.StartContainer(c)
assert.Error(t, err)
assert.EqualError(t, err, "oops")
api.AssertExpectations(t)
}
func TestStartContainer_StartContainerError(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Config: &dockerclient.ContainerConfig{},
HostConfig: &dockerclient.HostConfig{},
},
imageInfo: &dockerclient.ImageInfo{
Config: &dockerclient.ContainerConfig{},
},
}
api := mockclient.NewMockClient()
api.On("CreateContainer", mock.Anything, "foo").Return("def789", nil)
api.On("StartContainer", "def789", mock.Anything).Return(errors.New("whoops"))
client := DockerClient{api: api}
err := client.StartContainer(c)
assert.Error(t, err)
assert.EqualError(t, err, "whoops")
api.AssertExpectations(t)
}
func TestRenameContainer_Success(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Id: "abc123",
},
}
api := mockclient.NewMockClient()
api.On("RenameContainer", "abc123", "foo").Return(nil)
client := DockerClient{api: api}
err := client.RenameContainer(c, "foo")
assert.NoError(t, err)
api.AssertExpectations(t)
}
func TestRenameContainer_Error(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Id: "abc123",
},
}
api := mockclient.NewMockClient()
api.On("RenameContainer", "abc123", "foo").Return(errors.New("oops"))
client := DockerClient{api: api}
err := client.RenameContainer(c, "foo")
assert.Error(t, err)
assert.EqualError(t, err, "oops")
api.AssertExpectations(t)
}
func TestIsContainerStale_NotStaleSuccess(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Config: &dockerclient.ContainerConfig{Image: "bar"},
},
imageInfo: &dockerclient.ImageInfo{Id: "abc123"},
}
newImageInfo := &dockerclient.ImageInfo{Id: "abc123"}
api := mockclient.NewMockClient()
api.On("PullImage", "bar:latest", mock.Anything).Return(nil)
api.On("InspectImage", "bar:latest").Return(newImageInfo, nil)
client := DockerClient{api: api, pullImages: true}
stale, err := client.IsContainerStale(c)
assert.NoError(t, err)
assert.False(t, stale)
api.AssertExpectations(t)
}
func TestIsContainerStale_StaleSuccess(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Config: &dockerclient.ContainerConfig{Image: "bar:1.0"},
},
imageInfo: &dockerclient.ImageInfo{Id: "abc123"},
}
newImageInfo := &dockerclient.ImageInfo{Id: "xyz789"}
api := mockclient.NewMockClient()
api.On("PullImage", "bar:1.0", mock.Anything).Return(nil)
api.On("InspectImage", "bar:1.0").Return(newImageInfo, nil)
client := DockerClient{api: api, pullImages: true}
stale, err := client.IsContainerStale(c)
assert.NoError(t, err)
assert.True(t, stale)
api.AssertExpectations(t)
}
func TestIsContainerStale_NoPullSuccess(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Config: &dockerclient.ContainerConfig{Image: "bar:1.0"},
},
imageInfo: &dockerclient.ImageInfo{Id: "abc123"},
}
newImageInfo := &dockerclient.ImageInfo{Id: "xyz789"}
api := mockclient.NewMockClient()
api.On("InspectImage", "bar:1.0").Return(newImageInfo, nil)
client := DockerClient{api: api, pullImages: false}
stale, err := client.IsContainerStale(c)
assert.NoError(t, err)
assert.True(t, stale)
api.AssertExpectations(t)
}
func TestIsContainerStale_PullImageError(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Config: &dockerclient.ContainerConfig{Image: "bar:latest"},
},
imageInfo: &dockerclient.ImageInfo{Id: "abc123"},
}
api := mockclient.NewMockClient()
api.On("PullImage", "bar:latest", mock.Anything).Return(errors.New("oops"))
client := DockerClient{api: api, pullImages: true}
_, err := client.IsContainerStale(c)
assert.Error(t, err)
assert.EqualError(t, err, "oops")
api.AssertExpectations(t)
}
func TestIsContainerStale_InspectImageError(t *testing.T) {
c := Container{
containerInfo: &dockerclient.ContainerInfo{
Name: "foo",
Config: &dockerclient.ContainerConfig{Image: "bar:latest"},
},
imageInfo: &dockerclient.ImageInfo{Id: "abc123"},
}
newImageInfo := &dockerclient.ImageInfo{}
api := mockclient.NewMockClient()
api.On("PullImage", "bar:latest", mock.Anything).Return(nil)
api.On("InspectImage", "bar:latest").Return(newImageInfo, errors.New("uh-oh"))
client := DockerClient{api: api, pullImages: true}
_, err := client.IsContainerStale(c)
assert.Error(t, err)
assert.EqualError(t, err, "uh-oh")
api.AssertExpectations(t)
}