Fix panic on various calls when assigning Engine client to nil

Signed-off-by: Alexandre Beslic <abronan@docker.com>
This commit is contained in:
Alexandre Beslic 2015-09-02 09:41:20 -07:00
parent cfa802c07f
commit a7303ecc5e
4 changed files with 164 additions and 9 deletions

2
Godeps/Godeps.json generated
View File

@ -104,7 +104,7 @@
},
{
"ImportPath": "github.com/samalba/dockerclient",
"Rev": "8802d66ce78e69ff16692f6fa89ad969a5711b8b"
"Rev": "a87700686584dc383ddb7e2bfb042e0f7a8c371b"
},
{
"ImportPath": "github.com/samuel/go-zookeeper/zk",

View File

@ -0,0 +1,147 @@
package nopclient
import (
"errors"
"io"
"github.com/samalba/dockerclient"
)
var (
ErrNoEngine = errors.New("Engine no longer exists")
)
type NopClient struct {
}
func NewNopClient() *NopClient {
return &NopClient{}
}
func (client *NopClient) Info() (*dockerclient.Info, error) {
return nil, ErrNoEngine
}
func (client *NopClient) ListContainers(all bool, size bool, filters string) ([]dockerclient.Container, error) {
return nil, ErrNoEngine
}
func (client *NopClient) InspectContainer(id string) (*dockerclient.ContainerInfo, error) {
return nil, ErrNoEngine
}
func (client *NopClient) InspectImage(id string) (*dockerclient.ImageInfo, error) {
return nil, ErrNoEngine
}
func (client *NopClient) CreateContainer(config *dockerclient.ContainerConfig, name string) (string, error) {
return "", ErrNoEngine
}
func (client *NopClient) ContainerLogs(id string, options *dockerclient.LogOptions) (io.ReadCloser, error) {
return nil, ErrNoEngine
}
func (client *NopClient) ContainerChanges(id string) ([]*dockerclient.ContainerChanges, error) {
return nil, ErrNoEngine
}
func (client *NopClient) StartContainer(id string, config *dockerclient.HostConfig) error {
return ErrNoEngine
}
func (client *NopClient) StopContainer(id string, timeout int) error {
return ErrNoEngine
}
func (client *NopClient) RestartContainer(id string, timeout int) error {
return ErrNoEngine
}
func (client *NopClient) KillContainer(id, signal string) error {
return ErrNoEngine
}
func (client *NopClient) Wait(id string) <-chan dockerclient.WaitResult {
return nil
}
func (client *NopClient) MonitorEvents(options *dockerclient.MonitorEventsOptions, stopChan <-chan struct{}) (<-chan dockerclient.EventOrError, error) {
return nil, ErrNoEngine
}
func (client *NopClient) StartMonitorEvents(cb dockerclient.Callback, ec chan error, args ...interface{}) {
return
}
func (client *NopClient) StopAllMonitorEvents() {
return
}
func (client *NopClient) TagImage(nameOrID string, repo string, tag string, force bool) error {
return ErrNoEngine
}
func (client *NopClient) StartMonitorStats(id string, cb dockerclient.StatCallback, ec chan error, args ...interface{}) {
return
}
func (client *NopClient) StopAllMonitorStats() {
return
}
func (client *NopClient) Version() (*dockerclient.Version, error) {
return nil, ErrNoEngine
}
func (client *NopClient) PullImage(name string, auth *dockerclient.AuthConfig) error {
return ErrNoEngine
}
func (client *NopClient) LoadImage(reader io.Reader) error {
return ErrNoEngine
}
func (client *NopClient) RemoveContainer(id string, force, volumes bool) error {
return ErrNoEngine
}
func (client *NopClient) ListImages(all bool) ([]*dockerclient.Image, error) {
return nil, ErrNoEngine
}
func (client *NopClient) RemoveImage(name string, force bool) ([]*dockerclient.ImageDelete, error) {
return nil, ErrNoEngine
}
func (client *NopClient) PauseContainer(name string) error {
return ErrNoEngine
}
func (client *NopClient) UnpauseContainer(name string) error {
return ErrNoEngine
}
func (client *NopClient) ExecCreate(config *dockerclient.ExecConfig) (string, error) {
return "", ErrNoEngine
}
func (client *NopClient) ExecStart(id string, config *dockerclient.ExecConfig) error {
return ErrNoEngine
}
func (client *NopClient) ExecResize(id string, width, height int) error {
return ErrNoEngine
}
func (client *NopClient) RenameContainer(oldName string, newName string) error {
return ErrNoEngine
}
func (client *NopClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) {
return nil, ErrNoEngine
}
func (client *NopClient) BuildImage(image *dockerclient.BuildImage) (io.ReadCloser, error) {
return nil, ErrNoEngine
}

View File

@ -14,6 +14,7 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/version"
"github.com/samalba/dockerclient"
"github.com/samalba/dockerclient/nopclient"
)
const (
@ -31,6 +32,7 @@ const (
func NewEngine(addr string, overcommitRatio float64) *Engine {
e := &Engine{
Addr: addr,
client: nopclient.NewNopClient(),
Labels: make(map[string]string),
stopCh: make(chan struct{}),
containers: make(map[string]*Container),
@ -89,18 +91,15 @@ func (e *Engine) ConnectWithClient(client dockerclient.Client) error {
// Fetch the engine labels.
if err := e.updateSpecs(); err != nil {
e.client = nil
return err
}
// Force a state update before returning.
if err := e.RefreshContainers(true); err != nil {
e.client = nil
return err
}
if err := e.RefreshImages(); err != nil {
e.client = nil
return err
}
@ -120,16 +119,17 @@ func (e *Engine) Disconnect() {
e.Lock()
defer e.Unlock()
// do not close the chan, so it wait until the refreshLoop goroutine stops
e.stopCh <- struct{}{}
// close the chan
close(e.stopCh)
e.client.StopAllMonitorEvents()
e.client = nil
e.client = nopclient.NewNopClient()
e.emitEvent("engine_disconnect")
}
// isConnected returns true if the engine is connected to a remote docker API
func (e *Engine) isConnected() bool {
return e.client != nil
_, ok := e.client.(*nopclient.NopClient)
return !ok
}
// IsHealthy returns true if the engine is healthy

View File

@ -8,6 +8,7 @@ import (
"github.com/samalba/dockerclient"
"github.com/samalba/dockerclient/mockclient"
"github.com/samalba/dockerclient/nopclient"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
@ -38,8 +39,12 @@ func TestEngineConnectionFailure(t *testing.T) {
client := mockclient.NewMockClient()
client.On("Info").Return(&dockerclient.Info{}, errors.New("fail"))
// Connect() should fail and isConnected() return false.
// Connect() should fail
assert.Error(t, engine.ConnectWithClient(client))
// isConnected() should return false
nop := nopclient.NewNopClient()
assert.Error(t, engine.ConnectWithClient(nop))
assert.False(t, engine.isConnected())
client.Mock.AssertExpectations(t)
@ -51,6 +56,9 @@ func TestOutdatedEngine(t *testing.T) {
client.On("Info").Return(&dockerclient.Info{}, nil)
assert.Error(t, engine.ConnectWithClient(client))
nop := nopclient.NewNopClient()
assert.Error(t, engine.ConnectWithClient(nop))
assert.False(t, engine.isConnected())
client.Mock.AssertExpectations(t)