mirror of
https://github.com/docker/docs.git
synced 2026-04-02 17:28:55 +07:00
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>
331 lines
12 KiB
Go
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)
|
|
}
|