Files
docker-docs/cluster/engine_test.go
Daniel Hiltgen dde577d154 Add token pass-thru for Authconfig
This augments the CreateContainer call to detect the AuthConfig header
and use any supplied auth for pull operations.  This will allow pulling
of protected image on to specific node during the create operation.

CLI usage example using username/password:

    # Calculate the header
    REPO_USER=yourusername
    read -s PASSWORD
    HEADER=$(echo "{\"username\":\"${REPO_USER}\",\"password\":\"${PASSWORD}\"}"|base64 -w 0 )
    unset PASSWORD
    echo HEADER=$HEADER

    # Then add the following to your ~/.docker/config.json
    "HttpHeaders": {
        "X-Registry-Auth": "<HEADER string from above>"
    }

    # Now run a private image against swarm:
    docker run --rm -it yourprivateimage:latest

CLI usage example using registry tokens: (Required engine 1.10 with new auth token support)

    REPO=yourrepo/yourimage
    REPO_USER=yourusername
    read -s PASSWORD
    AUTH_URL=https://auth.docker.io/token
    TOKEN=$(curl -s -u "${REPO_USER}:${PASSWORD}" "${AUTH_URL}?scope=repository:${REPO}:pull&service=registry.docker.io" |
        jq -r ".token")
    HEADER=$(echo "{\"registrytoken\":\"${TOKEN}\"}"|base64 -w 0 )
    echo HEADER=$HEADER

    # Update the docker config as above, but the token will expire quickly...

Signed-off-by: Daniel Hiltgen <daniel.hiltgen@docker.com>
2015-12-11 18:36:55 -08:00

331 lines
12 KiB
Go

package cluster
import (
"errors"
"fmt"
"math"
"testing"
"time"
"github.com/samalba/dockerclient"
"github.com/samalba/dockerclient/mockclient"
"github.com/samalba/dockerclient/nopclient"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var (
mockInfo = &dockerclient.Info{
ID: "id",
Name: "name",
NCPU: 10,
MemTotal: 20,
Driver: "driver-test",
ExecutionDriver: "execution-driver-test",
KernelVersion: "1.2.3",
OperatingSystem: "golang",
Labels: []string{"foo=bar"},
}
mockVersion = &dockerclient.Version{
Version: "1.6.2",
}
engOpts = &EngineOpts{
RefreshMinInterval: time.Duration(30) * time.Second,
RefreshMaxInterval: time.Duration(60) * time.Second,
RefreshRetry: 3,
}
)
func TestEngineConnectionFailure(t *testing.T) {
engine := NewEngine("test", 0, engOpts)
assert.False(t, engine.isConnected())
// Always fail.
client := mockclient.NewMockClient()
client.On("Info").Return(&dockerclient.Info{}, errors.New("fail"))
// 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)
}
func TestOutdatedEngine(t *testing.T) {
engine := NewEngine("test", 0, engOpts)
client := mockclient.NewMockClient()
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)
}
func TestEngineCpusMemory(t *testing.T) {
engine := NewEngine("test", 0, engOpts)
assert.False(t, engine.isConnected())
client := mockclient.NewMockClient()
client.On("Info").Return(mockInfo, nil)
client.On("Version").Return(mockVersion, nil)
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{}, nil)
client.On("ListImages", mock.Anything).Return([]*dockerclient.Image{}, nil)
client.On("ListVolumes", mock.Anything).Return([]*dockerclient.Volume{}, nil)
client.On("ListNetworks", mock.Anything).Return([]*dockerclient.NetworkResource{}, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
assert.NoError(t, engine.ConnectWithClient(client))
assert.True(t, engine.isConnected())
assert.True(t, engine.IsHealthy())
assert.Equal(t, engine.UsedCpus(), int64(0))
assert.Equal(t, engine.UsedMemory(), int64(0))
client.Mock.AssertExpectations(t)
}
func TestEngineSpecs(t *testing.T) {
engine := NewEngine("test", 0, engOpts)
assert.False(t, engine.isConnected())
client := mockclient.NewMockClient()
client.On("Info").Return(mockInfo, nil)
client.On("Version").Return(mockVersion, nil)
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{}, nil)
client.On("ListImages", mock.Anything).Return([]*dockerclient.Image{}, nil)
client.On("ListVolumes", mock.Anything).Return([]*dockerclient.Volume{}, nil)
client.On("ListNetworks", mock.Anything).Return([]*dockerclient.NetworkResource{}, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
assert.NoError(t, engine.ConnectWithClient(client))
assert.True(t, engine.isConnected())
assert.True(t, engine.IsHealthy())
assert.Equal(t, engine.Cpus, mockInfo.NCPU)
assert.Equal(t, engine.Memory, mockInfo.MemTotal)
assert.Equal(t, engine.Labels["storagedriver"], mockInfo.Driver)
assert.Equal(t, engine.Labels["executiondriver"], mockInfo.ExecutionDriver)
assert.Equal(t, engine.Labels["kernelversion"], mockInfo.KernelVersion)
assert.Equal(t, engine.Labels["operatingsystem"], mockInfo.OperatingSystem)
assert.Equal(t, engine.Labels["foo"], "bar")
client.Mock.AssertExpectations(t)
}
func TestEngineState(t *testing.T) {
engine := NewEngine("test", 0, engOpts)
assert.False(t, engine.isConnected())
client := mockclient.NewMockClient()
client.On("Info").Return(mockInfo, nil)
client.On("Version").Return(mockVersion, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
// The client will return one container at first, then a second one will appear.
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{{Id: "one"}}, nil).Once()
client.On("ListImages", mock.Anything).Return([]*dockerclient.Image{}, nil).Once()
client.On("ListVolumes", mock.Anything).Return([]*dockerclient.Volume{}, nil)
client.On("ListNetworks", mock.Anything).Return([]*dockerclient.NetworkResource{}, nil)
client.On("InspectContainer", "one").Return(&dockerclient.ContainerInfo{Config: &dockerclient.ContainerConfig{CpuShares: 100}}, nil).Once()
client.On("ListContainers", true, false, fmt.Sprintf("{%q:[%q]}", "id", "two")).Return([]dockerclient.Container{{Id: "two"}}, nil).Once()
client.On("InspectContainer", "two").Return(&dockerclient.ContainerInfo{Config: &dockerclient.ContainerConfig{CpuShares: 100}}, nil).Once()
assert.NoError(t, engine.ConnectWithClient(client))
assert.True(t, engine.isConnected())
// The engine should only have a single container at this point.
containers := engine.Containers()
assert.Len(t, containers, 1)
if containers[0].Id != "one" {
t.Fatalf("Missing container: one")
}
// Fake an event which will trigger a refresh. The second container will appear.
engine.handler(&dockerclient.Event{Id: "two", Status: "created"}, nil)
containers = engine.Containers()
assert.Len(t, containers, 2)
if containers[0].Id != "one" && containers[1].Id != "one" {
t.Fatalf("Missing container: one")
}
if containers[0].Id != "two" && containers[1].Id != "two" {
t.Fatalf("Missing container: two")
}
client.Mock.AssertExpectations(t)
}
func TestCreateContainer(t *testing.T) {
var (
config = &ContainerConfig{dockerclient.ContainerConfig{
Image: "busybox",
CpuShares: 1,
Cmd: []string{"date"},
Tty: false,
}}
engine = NewEngine("test", 0, engOpts)
client = mockclient.NewMockClient()
)
client.On("Info").Return(mockInfo, nil)
client.On("Version").Return(mockVersion, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{}, nil).Once()
client.On("ListImages", mock.Anything).Return([]*dockerclient.Image{}, nil).Once()
client.On("ListVolumes", mock.Anything).Return([]*dockerclient.Volume{}, nil)
client.On("ListNetworks", mock.Anything).Return([]*dockerclient.NetworkResource{}, nil)
assert.NoError(t, engine.ConnectWithClient(client))
assert.True(t, engine.isConnected())
mockConfig := config.ContainerConfig
mockConfig.CpuShares = int64(math.Ceil(float64(config.CpuShares*1024) / float64(mockInfo.NCPU)))
mockConfig.HostConfig.CpuShares = mockConfig.CpuShares
// Everything is ok
name := "test1"
id := "id1"
var auth *dockerclient.AuthConfig
client.On("CreateContainer", &mockConfig, name, auth).Return(id, nil).Once()
client.On("ListContainers", true, false, fmt.Sprintf(`{"id":[%q]}`, id)).Return([]dockerclient.Container{{Id: id}}, nil).Once()
client.On("ListImages", mock.Anything).Return([]*dockerclient.Image{}, nil).Once()
client.On("ListVolumes", mock.Anything).Return([]*dockerclient.Volume{}, nil)
client.On("ListNetworks", mock.Anything).Return([]*dockerclient.NetworkResource{}, nil)
client.On("InspectContainer", id).Return(&dockerclient.ContainerInfo{Config: &config.ContainerConfig}, nil).Once()
container, err := engine.Create(config, name, false, auth)
assert.Nil(t, err)
assert.Equal(t, container.Id, id)
assert.Len(t, engine.Containers(), 1)
// Image not found, pullImage == false
name = "test2"
mockConfig.CpuShares = int64(math.Ceil(float64(config.CpuShares*1024) / float64(mockInfo.NCPU)))
client.On("CreateContainer", &mockConfig, name, auth).Return("", dockerclient.ErrImageNotFound).Once()
container, err = engine.Create(config, name, false, auth)
assert.Equal(t, err, dockerclient.ErrImageNotFound)
assert.Nil(t, container)
// Image not found, pullImage == true, and the image can be pulled successfully
name = "test3"
id = "id3"
mockConfig.CpuShares = int64(math.Ceil(float64(config.CpuShares*1024) / float64(mockInfo.NCPU)))
client.On("PullImage", config.Image+":latest", mock.Anything).Return(nil).Once()
client.On("CreateContainer", &mockConfig, name, auth).Return("", dockerclient.ErrImageNotFound).Once()
client.On("CreateContainer", &mockConfig, name, auth).Return(id, nil).Once()
client.On("ListContainers", true, false, fmt.Sprintf(`{"id":[%q]}`, id)).Return([]dockerclient.Container{{Id: id}}, nil).Once()
client.On("ListImages", mock.Anything).Return([]*dockerclient.Image{}, nil).Once()
client.On("ListVolumes", mock.Anything).Return([]*dockerclient.Volume{}, nil)
client.On("ListNetworks", mock.Anything).Return([]*dockerclient.NetworkResource{}, nil)
client.On("InspectContainer", id).Return(&dockerclient.ContainerInfo{Config: &config.ContainerConfig}, nil).Once()
container, err = engine.Create(config, name, true, auth)
assert.Nil(t, err)
assert.Equal(t, container.Id, id)
assert.Len(t, engine.Containers(), 2)
}
func TestImages(t *testing.T) {
engine := NewEngine("test", 0, engOpts)
engine.images = []*Image{
{dockerclient.Image{Id: "a"}, engine},
{dockerclient.Image{Id: "b"}, engine},
{dockerclient.Image{Id: "c"}, engine},
}
result := engine.Images()
assert.Equal(t, len(result), 3)
}
func TestTotalMemory(t *testing.T) {
engine := NewEngine("test", 0.05, engOpts)
engine.Memory = 1024
assert.Equal(t, engine.TotalMemory(), int64(1024+1024*5/100))
engine = NewEngine("test", 0, engOpts)
engine.Memory = 1024
assert.Equal(t, engine.TotalMemory(), int64(1024))
}
func TestTotalCpus(t *testing.T) {
engine := NewEngine("test", 0.05, engOpts)
engine.Cpus = 2
assert.Equal(t, engine.TotalCpus(), int64(2+2*5/100))
engine = NewEngine("test", 0, engOpts)
engine.Cpus = 2
assert.Equal(t, engine.TotalCpus(), int64(2))
}
func TestUsedCpus(t *testing.T) {
var (
containerNcpu = []int64{1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47}
hostNcpu = []int64{1, 2, 4, 8, 10, 12, 16, 20, 32, 36, 40, 48}
)
engine := NewEngine("test", 0, engOpts)
client := mockclient.NewMockClient()
for _, hn := range hostNcpu {
for _, cn := range containerNcpu {
if cn <= hn {
mockInfo.NCPU = hn
cpuShares := int64(math.Ceil(float64(cn*1024) / float64(mockInfo.NCPU)))
client.On("Info").Return(mockInfo, nil)
client.On("Version").Return(mockVersion, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
client.On("ListImages", mock.Anything).Return([]*dockerclient.Image{}, nil).Once()
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{{Id: "test"}}, nil).Once()
client.On("ListVolumes", mock.Anything).Return([]*dockerclient.Volume{}, nil)
client.On("ListNetworks", mock.Anything).Return([]*dockerclient.NetworkResource{}, nil)
client.On("InspectContainer", "test").Return(&dockerclient.ContainerInfo{Config: &dockerclient.ContainerConfig{CpuShares: cpuShares}}, nil).Once()
engine.ConnectWithClient(client)
assert.Equal(t, engine.UsedCpus(), cn)
}
}
}
}
func TestContainerRemovedDuringRefresh(t *testing.T) {
var (
container1 = dockerclient.Container{Id: "c1"}
container2 = dockerclient.Container{Id: "c2"}
info1 *dockerclient.ContainerInfo
info2 = &dockerclient.ContainerInfo{Id: "c2", Config: &dockerclient.ContainerConfig{}}
)
engine := NewEngine("test", 0, engOpts)
assert.False(t, engine.isConnected())
// A container is removed before it can be inspected.
client := mockclient.NewMockClient()
client.On("Info").Return(mockInfo, nil)
client.On("Version").Return(mockVersion, nil)
client.On("ListImages", mock.Anything).Return([]*dockerclient.Image{}, nil)
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{container1, container2}, nil)
client.On("ListVolumes", mock.Anything).Return([]*dockerclient.Volume{}, nil)
client.On("ListNetworks", mock.Anything).Return([]*dockerclient.NetworkResource{}, nil)
client.On("InspectContainer", "c1").Return(info1, errors.New("Not found"))
client.On("InspectContainer", "c2").Return(info2, nil)
assert.NoError(t, engine.ConnectWithClient(client))
assert.Nil(t, engine.RefreshContainers(true))
// List of containers is still valid
containers := engine.Containers()
assert.Len(t, containers, 1)
assert.Equal(t, containers[0].Id, "c2")
client.Mock.AssertExpectations(t)
}