2018-02-05 16:05:59 -05:00
|
|
|
package container // import "github.com/docker/docker/integration/container"
|
2017-05-23 16:32:34 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2018-03-20 13:29:18 -04:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2018-07-12 15:34:04 -04:00
|
|
|
"net/http"
|
2017-05-23 16:32:34 -04:00
|
|
|
"strconv"
|
2017-07-12 17:51:46 -04:00
|
|
|
"testing"
|
2018-03-20 13:29:18 -04:00
|
|
|
"time"
|
2017-05-23 16:32:34 -04:00
|
|
|
|
2018-03-20 13:29:18 -04:00
|
|
|
"github.com/docker/docker/api/types"
|
2017-05-23 16:32:34 -04:00
|
|
|
"github.com/docker/docker/api/types/container"
|
|
|
|
"github.com/docker/docker/api/types/network"
|
2018-07-12 15:34:04 -04:00
|
|
|
"github.com/docker/docker/api/types/versions"
|
2018-03-20 13:29:18 -04:00
|
|
|
ctr "github.com/docker/docker/integration/internal/container"
|
2018-04-17 04:22:04 -04:00
|
|
|
"github.com/docker/docker/internal/test/request"
|
2018-03-20 13:29:18 -04:00
|
|
|
"github.com/docker/docker/oci"
|
2018-06-11 09:32:11 -04:00
|
|
|
"gotest.tools/assert"
|
|
|
|
is "gotest.tools/assert/cmp"
|
|
|
|
"gotest.tools/poll"
|
|
|
|
"gotest.tools/skip"
|
2017-05-23 16:32:34 -04:00
|
|
|
)
|
|
|
|
|
2017-07-12 18:26:09 -04:00
|
|
|
func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) {
|
2017-05-23 16:32:34 -04:00
|
|
|
defer setupTest(t)()
|
2017-07-12 18:26:09 -04:00
|
|
|
client := request.NewAPIClient(t)
|
2017-05-23 16:32:34 -04:00
|
|
|
|
|
|
|
testCases := []struct {
|
2017-07-12 18:26:09 -04:00
|
|
|
doc string
|
2017-05-23 16:32:34 -04:00
|
|
|
image string
|
|
|
|
expectedError string
|
|
|
|
}{
|
|
|
|
{
|
2017-07-12 18:26:09 -04:00
|
|
|
doc: "image and tag",
|
2017-05-23 16:32:34 -04:00
|
|
|
image: "test456:v1",
|
|
|
|
expectedError: "No such image: test456:v1",
|
|
|
|
},
|
|
|
|
{
|
2017-07-12 18:26:09 -04:00
|
|
|
doc: "image no tag",
|
2017-05-23 16:32:34 -04:00
|
|
|
image: "test456",
|
|
|
|
expectedError: "No such image: test456",
|
|
|
|
},
|
|
|
|
{
|
2017-07-12 18:26:09 -04:00
|
|
|
doc: "digest",
|
2017-05-23 16:32:34 -04:00
|
|
|
image: "sha256:0cb40641836c461bc97c793971d84d758371ed682042457523e4ae701efeaaaa",
|
|
|
|
expectedError: "No such image: sha256:0cb40641836c461bc97c793971d84d758371ed682042457523e4ae701efeaaaa",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2017-07-12 18:26:09 -04:00
|
|
|
for _, tc := range testCases {
|
2017-05-23 16:32:34 -04:00
|
|
|
tc := tc
|
2017-07-12 18:26:09 -04:00
|
|
|
t.Run(tc.doc, func(t *testing.T) {
|
2017-05-23 16:32:34 -04:00
|
|
|
t.Parallel()
|
2017-07-12 18:26:09 -04:00
|
|
|
_, err := client.ContainerCreate(context.Background(),
|
|
|
|
&container.Config{Image: tc.image},
|
2017-05-23 16:32:34 -04:00
|
|
|
&container.HostConfig{},
|
|
|
|
&network.NetworkingConfig{},
|
2018-04-14 12:52:02 -04:00
|
|
|
"",
|
2017-05-23 16:32:34 -04:00
|
|
|
)
|
2018-05-20 18:06:50 -04:00
|
|
|
assert.Check(t, is.ErrorContains(err, tc.expectedError))
|
2017-05-23 16:32:34 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-12 18:26:09 -04:00
|
|
|
func TestCreateWithInvalidEnv(t *testing.T) {
|
2017-05-23 16:32:34 -04:00
|
|
|
defer setupTest(t)()
|
2017-07-12 18:26:09 -04:00
|
|
|
client := request.NewAPIClient(t)
|
2017-05-23 16:32:34 -04:00
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
env string
|
|
|
|
expectedError string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
env: "",
|
|
|
|
expectedError: "invalid environment variable:",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
env: "=",
|
|
|
|
expectedError: "invalid environment variable: =",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
env: "=foo",
|
|
|
|
expectedError: "invalid environment variable: =foo",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for index, tc := range testCases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(strconv.Itoa(index), func(t *testing.T) {
|
|
|
|
t.Parallel()
|
2017-07-12 18:26:09 -04:00
|
|
|
_, err := client.ContainerCreate(context.Background(),
|
2017-05-23 16:32:34 -04:00
|
|
|
&container.Config{
|
|
|
|
Image: "busybox",
|
|
|
|
Env: []string{tc.env},
|
|
|
|
},
|
|
|
|
&container.HostConfig{},
|
|
|
|
&network.NetworkingConfig{},
|
2018-04-14 12:52:02 -04:00
|
|
|
"",
|
2017-05-23 16:32:34 -04:00
|
|
|
)
|
2018-05-20 18:06:50 -04:00
|
|
|
assert.Check(t, is.ErrorContains(err, tc.expectedError))
|
2017-05-23 16:32:34 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2018-02-07 19:21:44 -05:00
|
|
|
|
|
|
|
// Test case for #30166 (target was not validated)
|
|
|
|
func TestCreateTmpfsMountsTarget(t *testing.T) {
|
2018-04-19 05:14:15 -04:00
|
|
|
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
|
2018-02-07 19:21:44 -05:00
|
|
|
|
|
|
|
defer setupTest(t)()
|
|
|
|
client := request.NewAPIClient(t)
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
target string
|
|
|
|
expectedError string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
target: ".",
|
|
|
|
expectedError: "mount path must be absolute",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
target: "foo",
|
|
|
|
expectedError: "mount path must be absolute",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
target: "/",
|
|
|
|
expectedError: "destination can't be '/'",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
target: "//",
|
|
|
|
expectedError: "destination can't be '/'",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
_, err := client.ContainerCreate(context.Background(),
|
|
|
|
&container.Config{
|
|
|
|
Image: "busybox",
|
|
|
|
},
|
|
|
|
&container.HostConfig{
|
|
|
|
Tmpfs: map[string]string{tc.target: ""},
|
|
|
|
},
|
|
|
|
&network.NetworkingConfig{},
|
|
|
|
"",
|
|
|
|
)
|
2018-05-20 18:06:50 -04:00
|
|
|
assert.Check(t, is.ErrorContains(err, tc.expectedError))
|
2018-02-07 19:21:44 -05:00
|
|
|
}
|
|
|
|
}
|
2018-03-20 13:29:18 -04:00
|
|
|
func TestCreateWithCustomMaskedPaths(t *testing.T) {
|
|
|
|
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
|
|
|
|
|
|
|
|
defer setupTest(t)()
|
|
|
|
client := request.NewAPIClient(t)
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
maskedPaths []string
|
|
|
|
expected []string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
maskedPaths: []string{},
|
|
|
|
expected: []string{},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
maskedPaths: nil,
|
|
|
|
expected: oci.DefaultSpec().Linux.MaskedPaths,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
maskedPaths: []string{"/proc/kcore", "/proc/keys"},
|
|
|
|
expected: []string{"/proc/kcore", "/proc/keys"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
checkInspect := func(t *testing.T, ctx context.Context, name string, expected []string) {
|
|
|
|
_, b, err := client.ContainerInspectWithRaw(ctx, name, false)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
var inspectJSON map[string]interface{}
|
|
|
|
err = json.Unmarshal(b, &inspectJSON)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
cfg, ok := inspectJSON["HostConfig"].(map[string]interface{})
|
|
|
|
assert.Check(t, is.Equal(true, ok), name)
|
|
|
|
|
|
|
|
maskedPaths, ok := cfg["MaskedPaths"].([]interface{})
|
|
|
|
assert.Check(t, is.Equal(true, ok), name)
|
|
|
|
|
|
|
|
mps := []string{}
|
|
|
|
for _, mp := range maskedPaths {
|
|
|
|
mps = append(mps, mp.(string))
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.DeepEqual(t, expected, mps)
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range testCases {
|
|
|
|
name := fmt.Sprintf("create-masked-paths-%d", i)
|
|
|
|
config := container.Config{
|
|
|
|
Image: "busybox",
|
|
|
|
Cmd: []string{"true"},
|
|
|
|
}
|
|
|
|
hc := container.HostConfig{}
|
|
|
|
if tc.maskedPaths != nil {
|
|
|
|
hc.MaskedPaths = tc.maskedPaths
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the container.
|
|
|
|
c, err := client.ContainerCreate(context.Background(),
|
|
|
|
&config,
|
|
|
|
&hc,
|
|
|
|
&network.NetworkingConfig{},
|
|
|
|
name,
|
|
|
|
)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
checkInspect(t, ctx, name, tc.expected)
|
|
|
|
|
|
|
|
// Start the container.
|
|
|
|
err = client.ContainerStart(ctx, c.ID, types.ContainerStartOptions{})
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
poll.WaitOn(t, ctr.IsInState(ctx, client, c.ID, "exited"), poll.WithDelay(100*time.Millisecond))
|
|
|
|
|
|
|
|
checkInspect(t, ctx, name, tc.expected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCreateWithCustomReadonlyPaths(t *testing.T) {
|
|
|
|
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
|
|
|
|
|
|
|
|
defer setupTest(t)()
|
|
|
|
client := request.NewAPIClient(t)
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
doc string
|
|
|
|
readonlyPaths []string
|
|
|
|
expected []string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
readonlyPaths: []string{},
|
|
|
|
expected: []string{},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
readonlyPaths: nil,
|
|
|
|
expected: oci.DefaultSpec().Linux.ReadonlyPaths,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
readonlyPaths: []string{"/proc/asound", "/proc/bus"},
|
|
|
|
expected: []string{"/proc/asound", "/proc/bus"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
checkInspect := func(t *testing.T, ctx context.Context, name string, expected []string) {
|
|
|
|
_, b, err := client.ContainerInspectWithRaw(ctx, name, false)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
var inspectJSON map[string]interface{}
|
|
|
|
err = json.Unmarshal(b, &inspectJSON)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
cfg, ok := inspectJSON["HostConfig"].(map[string]interface{})
|
|
|
|
assert.Check(t, is.Equal(true, ok), name)
|
|
|
|
|
|
|
|
readonlyPaths, ok := cfg["ReadonlyPaths"].([]interface{})
|
|
|
|
assert.Check(t, is.Equal(true, ok), name)
|
|
|
|
|
|
|
|
rops := []string{}
|
|
|
|
for _, rop := range readonlyPaths {
|
|
|
|
rops = append(rops, rop.(string))
|
|
|
|
}
|
|
|
|
assert.DeepEqual(t, expected, rops)
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range testCases {
|
|
|
|
name := fmt.Sprintf("create-readonly-paths-%d", i)
|
|
|
|
config := container.Config{
|
|
|
|
Image: "busybox",
|
|
|
|
Cmd: []string{"true"},
|
|
|
|
}
|
|
|
|
hc := container.HostConfig{}
|
|
|
|
if tc.readonlyPaths != nil {
|
|
|
|
hc.ReadonlyPaths = tc.readonlyPaths
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the container.
|
|
|
|
c, err := client.ContainerCreate(context.Background(),
|
|
|
|
&config,
|
|
|
|
&hc,
|
|
|
|
&network.NetworkingConfig{},
|
|
|
|
name,
|
|
|
|
)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
checkInspect(t, ctx, name, tc.expected)
|
|
|
|
|
|
|
|
// Start the container.
|
|
|
|
err = client.ContainerStart(ctx, c.ID, types.ContainerStartOptions{})
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
poll.WaitOn(t, ctr.IsInState(ctx, client, c.ID, "exited"), poll.WithDelay(100*time.Millisecond))
|
|
|
|
|
|
|
|
checkInspect(t, ctx, name, tc.expected)
|
|
|
|
}
|
|
|
|
}
|
2018-07-12 15:34:04 -04:00
|
|
|
|
|
|
|
func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
|
|
|
|
defer setupTest(t)()
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
doc string
|
|
|
|
interval time.Duration
|
|
|
|
timeout time.Duration
|
|
|
|
retries int
|
|
|
|
startPeriod time.Duration
|
|
|
|
expectedErr string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
doc: "test invalid Interval in Healthcheck: less than 0s",
|
|
|
|
interval: -10 * time.Millisecond,
|
|
|
|
timeout: time.Second,
|
|
|
|
retries: 1000,
|
|
|
|
expectedErr: fmt.Sprintf("Interval in Healthcheck cannot be less than %s", container.MinimumDuration),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "test invalid Interval in Healthcheck: larger than 0s but less than 1ms",
|
|
|
|
interval: 500 * time.Microsecond,
|
|
|
|
timeout: time.Second,
|
|
|
|
retries: 1000,
|
|
|
|
expectedErr: fmt.Sprintf("Interval in Healthcheck cannot be less than %s", container.MinimumDuration),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "test invalid Timeout in Healthcheck: less than 1ms",
|
|
|
|
interval: time.Second,
|
|
|
|
timeout: -100 * time.Millisecond,
|
|
|
|
retries: 1000,
|
|
|
|
expectedErr: fmt.Sprintf("Timeout in Healthcheck cannot be less than %s", container.MinimumDuration),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "test invalid Retries in Healthcheck: less than 0",
|
|
|
|
interval: time.Second,
|
|
|
|
timeout: time.Second,
|
|
|
|
retries: -10,
|
|
|
|
expectedErr: "Retries in Healthcheck cannot be negative",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "test invalid StartPeriod in Healthcheck: not 0 and less than 1ms",
|
|
|
|
interval: time.Second,
|
|
|
|
timeout: time.Second,
|
|
|
|
retries: 1000,
|
|
|
|
startPeriod: 100 * time.Microsecond,
|
|
|
|
expectedErr: fmt.Sprintf("StartPeriod in Healthcheck cannot be less than %s", container.MinimumDuration),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range testCases {
|
|
|
|
i := i
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.doc, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
healthCheck := map[string]interface{}{
|
|
|
|
"Interval": tc.interval,
|
|
|
|
"Timeout": tc.timeout,
|
|
|
|
"Retries": tc.retries,
|
|
|
|
}
|
|
|
|
if tc.startPeriod != 0 {
|
|
|
|
healthCheck["StartPeriod"] = tc.startPeriod
|
|
|
|
}
|
|
|
|
|
|
|
|
config := map[string]interface{}{
|
|
|
|
"Image": "busybox",
|
|
|
|
"Healthcheck": healthCheck,
|
|
|
|
}
|
|
|
|
|
|
|
|
res, body, err := request.Post("/containers/create?name="+fmt.Sprintf("test_%d_", i)+t.Name(), request.JSONBody(config))
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
|
|
|
|
assert.Check(t, is.Equal(http.StatusInternalServerError, res.StatusCode))
|
|
|
|
} else {
|
|
|
|
assert.Check(t, is.Equal(http.StatusBadRequest, res.StatusCode))
|
|
|
|
}
|
|
|
|
|
|
|
|
buf, err := request.ReadBody(body)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
assert.Check(t, is.Contains(string(buf), tc.expectedErr))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|