d8cd8165 |
package main
import ( |
576985a1 |
"archive/tar" |
d8cd8165 |
"bytes" |
7d62e40f |
"context" |
d8cd8165 |
"encoding/json" |
75f6929b |
"fmt" |
d8cd8165 |
"io" |
fc7b904d |
"io/ioutil" |
531433e7 |
"net/http" |
8771cafa |
"os" |
fc7b904d |
"path/filepath" |
ef1d410b |
"regexp" |
5bbf5cc6 |
"runtime" |
d44c9f91 |
"strings" |
59e55dcd |
"testing" |
76141a00 |
"time" |
91bfed60 |
|
91e197d6 |
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container" |
fc7b904d |
mounttypes "github.com/docker/docker/api/types/mount" |
91e197d6 |
networktypes "github.com/docker/docker/api/types/network" |
e4408318 |
"github.com/docker/docker/api/types/versions" |
0fd5a654 |
"github.com/docker/docker/client" |
10e171cd |
"github.com/docker/docker/integration-cli/cli" |
50c4475d |
"github.com/docker/docker/integration-cli/cli/build" |
fc7b904d |
"github.com/docker/docker/pkg/ioutils" |
91bfed60 |
"github.com/docker/docker/pkg/stringid" |
b37c214e |
"github.com/docker/docker/testutil/request" |
fc7b904d |
"github.com/docker/docker/volume" |
0fd5a654 |
"github.com/docker/go-connections/nat" |
9f0b3f56 |
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/poll" |
d8cd8165 |
)
|
1d92789b |
func (s *DockerSuite) TestContainerAPIGetAll(c *testing.T) { |
ecbb0e62 |
startCount := getContainerCount(c) |
ba5370c1 |
name := "getall" |
5c295460 |
dockerCmd(c, "run", "--name", name, "busybox", "true") |
d8cd8165 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
d8cd8165 |
|
0fd5a654 |
options := types.ContainerListOptions{
All: true, |
ba5370c1 |
} |
0fd5a654 |
containers, err := cli.ContainerList(context.Background(), options) |
6345208b |
assert.NilError(c, err)
assert.Equal(c, len(containers), startCount+1) |
0fd5a654 |
actual := containers[0].Names[0] |
6dc7f4c1 |
assert.Equal(c, actual, "/"+name) |
d8cd8165 |
}
|
725f3415 |
// regression test for empty json field being omitted #13691 |
1d92789b |
func (s *DockerSuite) TestContainerAPIGetJSONNoFieldsOmitted(c *testing.T) { |
0fd5a654 |
startCount := getContainerCount(c) |
5c295460 |
dockerCmd(c, "run", "busybox", "true") |
725f3415 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
options := types.ContainerListOptions{
All: true,
}
containers, err := cli.ContainerList(context.Background(), options) |
6345208b |
assert.NilError(c, err)
assert.Equal(c, len(containers), startCount+1) |
0fd5a654 |
actual := fmt.Sprintf("%+v", containers[0]) |
725f3415 |
// empty Labels field triggered this bug, make sense to check for everything
// cause even Ports for instance can trigger this bug
// better safe than sorry..
fields := []string{ |
0fd5a654 |
"ID", |
725f3415 |
"Names",
"Image",
"Command",
"Created",
"Ports",
"Labels",
"Status", |
755f8609 |
"NetworkSettings", |
725f3415 |
}
// decoding into types.Container do not work since it eventually unmarshal
// and empty field to an empty go map, so we just check for a string
for _, f := range fields { |
0fd5a654 |
if !strings.Contains(actual, f) { |
725f3415 |
c.Fatalf("Field %s is missing and it shouldn't", f)
}
}
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIGetExport(c *testing.T) { |
6482410c |
// Not supported on Windows as Windows does not support docker export |
f9a3558a |
testRequires(c, DaemonIsLinux) |
ba5370c1 |
name := "exportcontainer" |
5c295460 |
dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test") |
d8cd8165 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
d8cd8165 |
|
0fd5a654 |
body, err := cli.ContainerExport(context.Background(), name) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer body.Close() |
d8cd8165 |
found := false |
0fd5a654 |
for tarReader := tar.NewReader(body); ; { |
d8cd8165 |
h, err := tarReader.Next() |
4bdf957c |
if err != nil && err == io.EOF {
break |
d8cd8165 |
}
if h.Name == "test" {
found = true
break
}
} |
6135eec3 |
assert.Assert(c, found, "The created test file has not been found in the exported image") |
d8cd8165 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIGetChanges(c *testing.T) { |
2b0a7422 |
// Not supported on Windows as Windows does not support docker diff (/containers/name/changes) |
f9a3558a |
testRequires(c, DaemonIsLinux) |
ba5370c1 |
name := "changescontainer" |
5c295460 |
dockerCmd(c, "run", "--name", name, "busybox", "rm", "/etc/passwd") |
d8cd8165 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
d8cd8165 |
|
0fd5a654 |
changes, err := cli.ContainerDiff(context.Background(), name) |
6345208b |
assert.NilError(c, err) |
d8cd8165 |
// Check the changelog for removal of /etc/passwd
success := false
for _, elem := range changes {
if elem.Path == "/etc/passwd" && elem.Kind == 2 {
success = true
}
} |
6135eec3 |
assert.Assert(c, success, "/etc/passwd has been removed but is not present in the diff") |
d8cd8165 |
} |
d44c9f91 |
|
1d92789b |
func (s *DockerSuite) TestGetContainerStats(c *testing.T) { |
4d7707e1 |
var ( |
5c295460 |
name = "statscontainer" |
4d7707e1 |
) |
340e5233 |
runSleepingContainer(c, "--name", name) |
5c295460 |
|
4d7707e1 |
type b struct { |
0fd5a654 |
stats types.ContainerStats
err error |
4d7707e1 |
} |
0fd5a654 |
|
4d7707e1 |
bc := make(chan b, 1) |
76141a00 |
go func() { |
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
stats, err := cli.ContainerStats(context.Background(), name, true) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
bc <- b{stats, err} |
76141a00 |
}()
|
4d7707e1 |
// allow some time to stream the stats from the container
time.Sleep(4 * time.Second) |
5c295460 |
dockerCmd(c, "rm", "-f", name) |
76141a00 |
|
4d7707e1 |
// collect the results from the stats stream or timeout and fail
// if the stream was not disconnected.
select {
case <-time.After(2 * time.Second): |
dc944ea7 |
c.Fatal("stream was not closed after container was removed") |
4d7707e1 |
case sr := <-bc: |
0fd5a654 |
dec := json.NewDecoder(sr.stats.Body)
defer sr.stats.Body.Close() |
7fed7d7e |
var s *types.Stats |
4d7707e1 |
// decode only one object from the stream |
6345208b |
assert.NilError(c, dec.Decode(&s)) |
76141a00 |
}
} |
198ff76d |
|
1d92789b |
func (s *DockerSuite) TestGetContainerStatsRmRunning(c *testing.T) { |
a899aa67 |
out := runSleepingContainer(c) |
b3e8ab30 |
id := strings.TrimSpace(out)
|
6a0105b4 |
buf := &ChannelBuffer{C: make(chan []byte, 1)} |
b3e8ab30 |
defer buf.Close() |
f5fb3c69 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
stats, err := cli.ContainerStats(context.Background(), id, true) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer stats.Body.Close() |
f5fb3c69 |
|
9a9ce80a |
chErr := make(chan error, 1) |
b3e8ab30 |
go func() { |
0fd5a654 |
_, err = io.Copy(buf, stats.Body) |
b3e8ab30 |
chErr <- err
}()
b := make([]byte, 32)
// make sure we've got some stats |
f5fb3c69 |
_, err = buf.ReadTimeout(b, 2*time.Second) |
6345208b |
assert.NilError(c, err) |
b3e8ab30 |
// Now remove without `-f` and make sure we are still pulling stats |
693ba98c |
_, _, err = dockerCmdWithError("rm", id) |
6135eec3 |
assert.Assert(c, err != nil, "rm should have failed but didn't") |
b3e8ab30 |
_, err = buf.ReadTimeout(b, 2*time.Second) |
6345208b |
assert.NilError(c, err) |
b3e8ab30 |
|
f5fb3c69 |
dockerCmd(c, "rm", "-f", id) |
2743e2d8 |
assert.Assert(c, <-chErr == nil) |
b3e8ab30 |
}
|
6a0105b4 |
// ChannelBuffer holds a chan of byte array that can be populate in a goroutine.
type ChannelBuffer struct {
C chan []byte
}
// Write implements Writer.
func (c *ChannelBuffer) Write(b []byte) (int, error) {
c.C <- b
return len(b), nil
}
// Close closes the go channel.
func (c *ChannelBuffer) Close() error {
close(c.C)
return nil
}
// ReadTimeout reads the content of the channel in the specified byte array with
// the specified duration as timeout.
func (c *ChannelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) {
select {
case b := <-c.C:
return copy(p[0:], b), nil
case <-time.After(n):
return -1, fmt.Errorf("timeout reading from channel")
}
}
|
ec97f414 |
// regression test for gh13421
// previous test was just checking one stat entry so it didn't fail (stats with
// stream false always return one stat) |
1d92789b |
func (s *DockerSuite) TestGetContainerStatsStream(c *testing.T) { |
ec97f414 |
name := "statscontainer" |
340e5233 |
runSleepingContainer(c, "--name", name) |
ec97f414 |
type b struct { |
0fd5a654 |
stats types.ContainerStats
err error |
ec97f414 |
} |
0fd5a654 |
|
ec97f414 |
bc := make(chan b, 1)
go func() { |
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
stats, err := cli.ContainerStats(context.Background(), name, true) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
bc <- b{stats, err} |
ec97f414 |
}()
// allow some time to stream the stats from the container
time.Sleep(4 * time.Second) |
5c295460 |
dockerCmd(c, "rm", "-f", name) |
ec97f414 |
// collect the results from the stats stream or timeout and fail
// if the stream was not disconnected.
select {
case <-time.After(2 * time.Second):
c.Fatal("stream was not closed after container was removed")
case sr := <-bc: |
0fd5a654 |
b, err := ioutil.ReadAll(sr.stats.Body)
defer sr.stats.Body.Close() |
6345208b |
assert.NilError(c, err) |
f85ee178 |
s := string(b) |
ec97f414 |
// count occurrences of "read" of types.Stats
if l := strings.Count(s, "read"); l < 2 {
c.Fatalf("Expected more than one stat streamed, got %d", l)
}
}
}
|
1d92789b |
func (s *DockerSuite) TestGetContainerStatsNoStream(c *testing.T) { |
ec97f414 |
name := "statscontainer" |
340e5233 |
runSleepingContainer(c, "--name", name) |
ec97f414 |
type b struct { |
0fd5a654 |
stats types.ContainerStats
err error |
ec97f414 |
} |
0fd5a654 |
|
ec97f414 |
bc := make(chan b, 1) |
0fd5a654 |
|
ec97f414 |
go func() { |
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
stats, err := cli.ContainerStats(context.Background(), name, false) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
bc <- b{stats, err} |
ec97f414 |
}()
// allow some time to stream the stats from the container
time.Sleep(4 * time.Second) |
5c295460 |
dockerCmd(c, "rm", "-f", name) |
ec97f414 |
// collect the results from the stats stream or timeout and fail
// if the stream was not disconnected.
select {
case <-time.After(2 * time.Second):
c.Fatal("stream was not closed after container was removed")
case sr := <-bc: |
0fd5a654 |
b, err := ioutil.ReadAll(sr.stats.Body)
defer sr.stats.Body.Close() |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
s := string(b) |
340e5233 |
// count occurrences of `"read"` of types.Stats |
6345208b |
assert.Assert(c, strings.Count(s, `"read"`) == 1, "Expected only one stat streamed, got %d", strings.Count(s, `"read"`)) |
ec97f414 |
}
}
|
1d92789b |
func (s *DockerSuite) TestGetStoppedContainerStats(c *testing.T) { |
3434860d |
name := "statscontainer" |
340e5233 |
dockerCmd(c, "create", "--name", name, "busybox", "ps") |
8dc5791f |
|
c322af80 |
chResp := make(chan error, 1) |
3434860d |
// We expect an immediate response, but if it's not immediate, the test would hang, so put it in a goroutine
// below we'll check this on a timeout. |
8dc5791f |
go func() { |
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
resp, err := cli.ContainerStats(context.Background(), name, false) |
5bba06e0 |
assert.NilError(c, err) |
0fd5a654 |
defer resp.Body.Close()
chResp <- err |
8dc5791f |
}()
|
3434860d |
select { |
0fd5a654 |
case err := <-chResp: |
6345208b |
assert.NilError(c, err) |
3434860d |
case <-time.After(10 * time.Second): |
2eee6133 |
c.Fatal("timeout waiting for stats response for stopped container") |
3434860d |
} |
8dc5791f |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIPause(c *testing.T) { |
2b0a7422 |
// Problematic on Windows as Windows does not support pause |
f9a3558a |
testRequires(c, DaemonIsLinux) |
10e171cd |
|
1d92789b |
getPaused := func(c *testing.T) []string { |
10e171cd |
return strings.Fields(cli.DockerCmd(c, "ps", "-f", "status=paused", "-q", "-a").Combined())
}
out := cli.DockerCmd(c, "run", "-d", "busybox", "sleep", "30").Combined() |
8636a219 |
ContainerID := strings.TrimSpace(out)
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerPause(context.Background(), ContainerID) |
6345208b |
assert.NilError(c, err) |
8636a219 |
|
10e171cd |
pausedContainers := getPaused(c) |
8636a219 |
if len(pausedContainers) != 1 || stringid.TruncateID(ContainerID) != pausedContainers[0] { |
dc944ea7 |
c.Fatalf("there should be one paused container and not %d", len(pausedContainers)) |
8636a219 |
}
|
0fd5a654 |
err = cli.ContainerUnpause(context.Background(), ContainerID) |
6345208b |
assert.NilError(c, err) |
8636a219 |
|
10e171cd |
pausedContainers = getPaused(c) |
6135eec3 |
assert.Equal(c, len(pausedContainers), 0, "There should be no paused container.") |
8636a219 |
} |
d9e4b143 |
|
1d92789b |
func (s *DockerSuite) TestContainerAPITop(c *testing.T) { |
f9a3558a |
testRequires(c, DaemonIsLinux) |
228d7484 |
out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "top && true") |
7c40c0a9 |
id := strings.TrimSpace(out) |
6345208b |
assert.NilError(c, waitRun(id)) |
d9e4b143 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
0823ab70 |
// sort by comm[andline] to make sure order stays the same in case of PID rollover
top, err := cli.ContainerTop(context.Background(), id, []string{"aux", "--sort=comm"}) |
6345208b |
assert.NilError(c, err) |
a2024a54 |
assert.Equal(c, len(top.Titles), 11, fmt.Sprintf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles)) |
d9e4b143 |
if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" { |
dc944ea7 |
c.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles) |
d9e4b143 |
} |
a2024a54 |
assert.Equal(c, len(top.Processes), 2, fmt.Sprintf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes)) |
228d7484 |
assert.Equal(c, top.Processes[0][10], "/bin/sh -c top && true") |
6dc7f4c1 |
assert.Equal(c, top.Processes[1][10], "top") |
d9e4b143 |
} |
f19061cc |
|
1d92789b |
func (s *DockerSuite) TestContainerAPITopWindows(c *testing.T) { |
6482410c |
testRequires(c, DaemonIsWindows) |
a899aa67 |
out := runSleepingContainer(c, "-d") |
7c40c0a9 |
id := strings.TrimSpace(out) |
6345208b |
assert.NilError(c, waitRun(id)) |
6482410c |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
top, err := cli.ContainerTop(context.Background(), id, nil) |
6345208b |
assert.NilError(c, err)
assert.Equal(c, len(top.Titles), 4, "expected 4 titles, found %d: %v", len(top.Titles), top.Titles) |
6482410c |
if top.Titles[0] != "Name" || top.Titles[3] != "Private Working Set" {
c.Fatalf("expected `Name` at `Titles[0]` and `Private Working Set` at Titles[3]: %v", top.Titles)
} |
6345208b |
assert.Assert(c, len(top.Processes) >= 2, "expected at least 2 processes, found %d: %v", len(top.Processes), top.Processes) |
6482410c |
foundProcess := false
expectedProcess := "busybox.exe"
for _, process := range top.Processes {
if process[0] == expectedProcess {
foundProcess = true
break
}
}
|
6345208b |
assert.Assert(c, foundProcess, "expected to find %s: %v", expectedProcess, top.Processes) |
6482410c |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICommit(c *testing.T) { |
cd4f507b |
cName := "testapicommit" |
5c295460 |
dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test") |
f19061cc |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
f19061cc |
|
0fd5a654 |
options := types.ContainerCommitOptions{
Reference: "testcontainerapicommit:testtag", |
f19061cc |
} |
0fd5a654 |
img, err := cli.ContainerCommit(context.Background(), cName, options) |
6345208b |
assert.NilError(c, err) |
f19061cc |
|
62a856e9 |
cmd := inspectField(c, img.ID, "Config.Cmd") |
a2024a54 |
assert.Equal(c, cmd, "[/bin/sh -c touch /test]", fmt.Sprintf("got wrong Cmd from commit: %q", cmd)) |
4bdf957c |
|
f19061cc |
// sanity check, make sure the image is what we think it is |
6b3c9281 |
dockerCmd(c, "run", img.ID, "ls", "/test") |
c61fa847 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICommitWithLabelInConfig(c *testing.T) { |
c61fa847 |
cName := "testapicommitwithconfig" |
5c295460 |
dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test") |
c61fa847 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
c61fa847 |
|
0fd5a654 |
config := containertypes.Config{
Labels: map[string]string{"key1": "value1", "key2": "value2"}}
options := types.ContainerCommitOptions{
Reference: "testcontainerapicommitwithconfig",
Config: &config, |
c61fa847 |
} |
0fd5a654 |
img, err := cli.ContainerCommit(context.Background(), cName, options) |
6345208b |
assert.NilError(c, err) |
c61fa847 |
|
62a856e9 |
label1 := inspectFieldMap(c, img.ID, "Config.Labels", "key1") |
6dc7f4c1 |
assert.Equal(c, label1, "value1") |
c61fa847 |
|
62a856e9 |
label2 := inspectFieldMap(c, img.ID, "Config.Labels", "key2") |
6dc7f4c1 |
assert.Equal(c, label2, "value2") |
c61fa847 |
|
62a856e9 |
cmd := inspectField(c, img.ID, "Config.Cmd") |
a2024a54 |
assert.Equal(c, cmd, "[/bin/sh -c touch /test]", fmt.Sprintf("got wrong Cmd from commit: %q", cmd)) |
c61fa847 |
// sanity check, make sure the image is what we think it is |
6b3c9281 |
dockerCmd(c, "run", img.ID, "ls", "/test") |
f19061cc |
} |
23fa7d41 |
|
1d92789b |
func (s *DockerSuite) TestContainerAPIBadPort(c *testing.T) { |
2b0a7422 |
// TODO Windows to Windows CI - Port this test |
f9a3558a |
testRequires(c, DaemonIsLinux) |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
Cmd: []string{"/bin/sh", "-c", "echo test"},
}
hostConfig := containertypes.HostConfig{
PortBindings: nat.PortMap{
"8080/tcp": []nat.PortBinding{ |
12b6083c |
{ |
0fd5a654 |
HostIP: "",
HostPort: "aa80"}, |
12b6083c |
},
},
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "") |
6345208b |
assert.ErrorContains(c, err, `invalid port specification: "aa80"`) |
12b6083c |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICreate(c *testing.T) { |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
Cmd: []string{"/bin/sh", "-c", "touch /test && ls /test"}, |
23fa7d41 |
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
c7845e27 |
|
7a9cb29f |
container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "") |
6345208b |
assert.NilError(c, err) |
23fa7d41 |
|
6b3c9281 |
out, _ := dockerCmd(c, "start", "-a", container.ID) |
6345208b |
assert.Equal(c, strings.TrimSpace(out), "/test") |
23fa7d41 |
} |
5dc02a2f |
|
1d92789b |
func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *testing.T) { |
4ce81779 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
4ce81779 |
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "") |
0fd5a654 |
expected := "No command specified" |
6345208b |
assert.ErrorContains(c, err, expected) |
4ce81779 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *testing.T) { |
cfa515fd |
// Container creation must fail if client specified configurations for more than one network |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
}
networkingConfig := networktypes.NetworkingConfig{
EndpointsConfig: map[string]*networktypes.EndpointSettings{
"net1": {},
"net2": {},
"net3": {}, |
cfa515fd |
},
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, nil, "") |
0fd5a654 |
msg := err.Error() |
cfa515fd |
// network name order in error message is not deterministic |
98f2638f |
assert.Assert(c, strings.Contains(msg, "Container cannot be connected to network endpoints"))
assert.Assert(c, strings.Contains(msg, "net1"))
assert.Assert(c, strings.Contains(msg, "net2"))
assert.Assert(c, strings.Contains(msg, "net3")) |
cfa515fd |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICreateBridgeNetworkMode(c *testing.T) { |
2b0a7422 |
// Windows does not support bridge |
f9a3558a |
testRequires(c, DaemonIsLinux) |
80d73c83 |
UtilCreateNetworkMode(c, "bridge") |
557c7cb8 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICreateOtherNetworkModes(c *testing.T) { |
2b0a7422 |
// Windows does not support these network modes |
557c7cb8 |
testRequires(c, DaemonIsLinux, NotUserNamespace)
UtilCreateNetworkMode(c, "host") |
80d73c83 |
UtilCreateNetworkMode(c, "container:web1")
} |
fca4aea0 |
|
1d92789b |
func UtilCreateNetworkMode(c *testing.T, networkMode containertypes.NetworkMode) { |
0fd5a654 |
config := containertypes.Config{
Image: "busybox", |
fca4aea0 |
} |
80d73c83 |
|
0fd5a654 |
hostConfig := containertypes.HostConfig{
NetworkMode: networkMode,
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
80d73c83 |
|
7a9cb29f |
container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "") |
6345208b |
assert.NilError(c, err) |
fca4aea0 |
|
0fd5a654 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
6345208b |
assert.NilError(c, err) |
80d73c83 |
|
7c40c0a9 |
assert.Equal(c, containerJSON.HostConfig.NetworkMode, networkMode, "Mismatched NetworkMode") |
fca4aea0 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *testing.T) { |
2b0a7422 |
// TODO Windows to Windows CI. The CpuShares part could be ported. |
f9a3558a |
testRequires(c, DaemonIsLinux) |
0fd5a654 |
config := containertypes.Config{
Image: "busybox", |
ebe344d3 |
}
|
0fd5a654 |
hostConfig := containertypes.HostConfig{
Resources: containertypes.Resources{
CPUShares: 512,
CpusetCpus: "0",
},
} |
ebe344d3 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
ebe344d3 |
|
7a9cb29f |
container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "") |
6345208b |
assert.NilError(c, err) |
ebe344d3 |
|
0fd5a654 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
6345208b |
assert.NilError(c, err) |
ebe344d3 |
|
62a856e9 |
out := inspectField(c, containerJSON.ID, "HostConfig.CpuShares") |
6345208b |
assert.Equal(c, out, "512") |
ebe344d3 |
|
62a856e9 |
outCpuset := inspectField(c, containerJSON.ID, "HostConfig.CpusetCpus") |
6dc7f4c1 |
assert.Equal(c, outCpuset, "0") |
ebe344d3 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIVerifyHeader(c *testing.T) { |
5dc02a2f |
config := map[string]interface{}{
"Image": "busybox",
}
|
bb1c576e |
create := func(ct string) (*http.Response, io.ReadCloser, error) { |
5dc02a2f |
jsonData := bytes.NewBuffer(nil) |
2743e2d8 |
assert.Assert(c, json.NewEncoder(jsonData).Encode(config) == nil) |
b11ba123 |
return request.Post("/containers/create", request.RawContent(ioutil.NopCloser(jsonData)), request.ContentType(ct)) |
5dc02a2f |
}
// Try with no content-type |
bb1c576e |
res, body, err := create("") |
6345208b |
assert.NilError(c, err) |
e4408318 |
// todo: we need to figure out a better way to compare between dockerd versions
// comparing between daemon API version is not precise.
if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") { |
6345208b |
assert.Equal(c, res.StatusCode, http.StatusBadRequest) |
e4408318 |
} else { |
0fa116fa |
assert.Assert(c, res.StatusCode != http.StatusOK) |
e4408318 |
} |
5dc02a2f |
body.Close() |
c7845e27 |
|
5dc02a2f |
// Try with wrong content-type |
bb1c576e |
res, body, err = create("application/xml") |
6345208b |
assert.NilError(c, err) |
e4408318 |
if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") { |
6345208b |
assert.Equal(c, res.StatusCode, http.StatusBadRequest) |
e4408318 |
} else { |
0fa116fa |
assert.Assert(c, res.StatusCode != http.StatusOK) |
e4408318 |
} |
5dc02a2f |
body.Close()
// now application/json |
bb1c576e |
res, body, err = create("application/json") |
6345208b |
assert.NilError(c, err)
assert.Equal(c, res.StatusCode, http.StatusCreated) |
5dc02a2f |
body.Close()
} |
308a2302 |
|
580d3677 |
// Issue 14230. daemon should return 500 for invalid port syntax |
1d92789b |
func (s *DockerSuite) TestContainerAPIInvalidPortSyntax(c *testing.T) { |
477201a2 |
config := `{
"Image": "busybox",
"HostConfig": { |
2b0a7422 |
"NetworkMode": "default", |
477201a2 |
"PortBindings": {
"19039;1230": [
{}
]
}
}
}`
|
b11ba123 |
res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) |
6345208b |
assert.NilError(c, err) |
e4408318 |
if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") { |
6345208b |
assert.Equal(c, res.StatusCode, http.StatusBadRequest) |
e4408318 |
} else { |
0fa116fa |
assert.Assert(c, res.StatusCode != http.StatusOK) |
e4408318 |
} |
477201a2 |
|
4f304e72 |
b, err := request.ReadBody(body) |
6345208b |
assert.NilError(c, err) |
98f2638f |
assert.Assert(c, strings.Contains(string(b[:]), "invalid port")) |
477201a2 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIRestartPolicyInvalidPolicyName(c *testing.T) { |
94e95e47 |
config := `{
"Image": "busybox",
"HostConfig": {
"RestartPolicy": {
"Name": "something",
"MaximumRetryCount": 0
}
}
}`
|
b11ba123 |
res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) |
6345208b |
assert.NilError(c, err) |
e4408318 |
if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") { |
6345208b |
assert.Equal(c, res.StatusCode, http.StatusBadRequest) |
e4408318 |
} else { |
0fa116fa |
assert.Assert(c, res.StatusCode != http.StatusOK) |
e4408318 |
} |
94e95e47 |
|
4f304e72 |
b, err := request.ReadBody(body) |
6345208b |
assert.NilError(c, err) |
98f2638f |
assert.Assert(c, strings.Contains(string(b[:]), "invalid restart policy")) |
94e95e47 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIRestartPolicyRetryMismatch(c *testing.T) { |
94e95e47 |
config := `{
"Image": "busybox",
"HostConfig": {
"RestartPolicy": {
"Name": "always",
"MaximumRetryCount": 2
}
}
}`
|
b11ba123 |
res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) |
6345208b |
assert.NilError(c, err) |
e4408318 |
if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") { |
6345208b |
assert.Equal(c, res.StatusCode, http.StatusBadRequest) |
e4408318 |
} else { |
0fa116fa |
assert.Assert(c, res.StatusCode != http.StatusOK) |
e4408318 |
} |
94e95e47 |
|
4f304e72 |
b, err := request.ReadBody(body) |
6345208b |
assert.NilError(c, err) |
98f2638f |
assert.Assert(c, strings.Contains(string(b[:]), "maximum retry count cannot be used with restart policy")) |
94e95e47 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIRestartPolicyNegativeRetryCount(c *testing.T) { |
94e95e47 |
config := `{
"Image": "busybox",
"HostConfig": {
"RestartPolicy": {
"Name": "on-failure",
"MaximumRetryCount": -2
}
}
}`
|
b11ba123 |
res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) |
6345208b |
assert.NilError(c, err) |
e4408318 |
if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") { |
6345208b |
assert.Equal(c, res.StatusCode, http.StatusBadRequest) |
e4408318 |
} else { |
0fa116fa |
assert.Assert(c, res.StatusCode != http.StatusOK) |
e4408318 |
} |
94e95e47 |
|
4f304e72 |
b, err := request.ReadBody(body) |
6345208b |
assert.NilError(c, err) |
98f2638f |
assert.Assert(c, strings.Contains(string(b[:]), "maximum retry count cannot be negative")) |
9db5d649 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIRestartPolicyDefaultRetryCount(c *testing.T) { |
9db5d649 |
config := `{
"Image": "busybox",
"HostConfig": {
"RestartPolicy": {
"Name": "on-failure",
"MaximumRetryCount": 0
}
}
}`
|
b11ba123 |
res, _, err := request.Post("/containers/create", request.RawString(config), request.JSON) |
6345208b |
assert.NilError(c, err)
assert.Equal(c, res.StatusCode, http.StatusCreated) |
94e95e47 |
}
|
308a2302 |
// Issue 7941 - test to make sure a "null" in JSON is just ignored.
// W/o this fix a null in JSON would be parsed into a string var as "null" |
1d92789b |
func (s *DockerSuite) TestContainerAPIPostCreateNull(c *testing.T) { |
308a2302 |
config := `{
"Hostname":"",
"Domainname":"",
"Memory":0,
"MemorySwap":0,
"CpuShares":0,
"Cpuset":null,
"AttachStdin":true,
"AttachStdout":true,
"AttachStderr":true,
"ExposedPorts":{},
"Tty":true,
"OpenStdin":true,
"StdinOnce":true,
"Env":[],
"Cmd":"ls",
"Image":"busybox",
"Volumes":{},
"WorkingDir":"",
"Entrypoint":null,
"NetworkDisabled":false,
"OnBuild":null}`
|
b11ba123 |
res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) |
6345208b |
assert.NilError(c, err)
assert.Equal(c, res.StatusCode, http.StatusCreated) |
308a2302 |
|
4f304e72 |
b, err := request.ReadBody(body) |
6345208b |
assert.NilError(c, err) |
308a2302 |
type createResp struct { |
6b3c9281 |
ID string |
308a2302 |
}
var container createResp |
2743e2d8 |
assert.Assert(c, json.Unmarshal(b, &container) == nil) |
62a856e9 |
out := inspectField(c, container.ID, "HostConfig.CpusetCpus") |
6345208b |
assert.Equal(c, out, "") |
1eea2c58 |
|
62a856e9 |
outMemory := inspectField(c, container.ID, "HostConfig.Memory") |
6dc7f4c1 |
assert.Equal(c, outMemory, "0") |
62a856e9 |
outMemorySwap := inspectField(c, container.ID, "HostConfig.MemorySwap") |
6dc7f4c1 |
assert.Equal(c, outMemorySwap, "0") |
308a2302 |
} |
1a35b16b |
|
1d92789b |
func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *testing.T) { |
2b0a7422 |
// TODO Windows: Port once memory is supported |
f9a3558a |
testRequires(c, DaemonIsLinux) |
1a35b16b |
config := `{
"Image": "busybox",
"Cmd": "ls",
"OpenStdin": true,
"CpuShares": 100,
"Memory": 524287
}`
|
b11ba123 |
res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) |
6345208b |
assert.NilError(c, err) |
4f304e72 |
b, err2 := request.ReadBody(body) |
2743e2d8 |
assert.Assert(c, err2 == nil) |
1a35b16b |
|
e4408318 |
if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") { |
6345208b |
assert.Equal(c, res.StatusCode, http.StatusBadRequest) |
e4408318 |
} else { |
0fa116fa |
assert.Assert(c, res.StatusCode != http.StatusOK) |
e4408318 |
} |
d2e23405 |
assert.Assert(c, strings.Contains(string(b), "Minimum memory limit allowed is 6MB")) |
1a35b16b |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIRename(c *testing.T) { |
7fb7a477 |
out, _ := dockerCmd(c, "run", "--name", "TestContainerAPIRename", "-d", "busybox", "sh") |
8f752ffe |
containerID := strings.TrimSpace(out) |
7fb7a477 |
newName := "TestContainerAPIRenameNew" |
0fd5a654 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerRename(context.Background(), containerID, newName) |
6345208b |
assert.NilError(c, err) |
8f752ffe |
|
62a856e9 |
name := inspectField(c, containerID, "Name") |
6135eec3 |
assert.Equal(c, name, "/"+newName, "Failed to rename container") |
8f752ffe |
} |
f7e417ea |
|
1d92789b |
func (s *DockerSuite) TestContainerAPIKill(c *testing.T) { |
f7e417ea |
name := "test-api-kill" |
777ee34b |
runSleepingContainer(c, "-i", "--name", name) |
f7e417ea |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerKill(context.Background(), name, "SIGKILL") |
6345208b |
assert.NilError(c, err) |
f7e417ea |
|
62a856e9 |
state := inspectField(c, name, "State.Running") |
a2024a54 |
assert.Equal(c, state, "false", fmt.Sprintf("got wrong State from container %s: %q", name, state)) |
f7e417ea |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIRestart(c *testing.T) { |
f7e417ea |
name := "test-api-restart" |
6482410c |
runSleepingContainer(c, "-di", "--name", name) |
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
f7e417ea |
|
0fd5a654 |
timeout := 1 * time.Second
err = cli.ContainerRestart(context.Background(), name, &timeout) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
|
2743e2d8 |
assert.Assert(c, waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 15*time.Second) == nil) |
f7e417ea |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIRestartNotimeoutParam(c *testing.T) { |
74121a42 |
name := "test-api-restart-no-timeout-param" |
a899aa67 |
out := runSleepingContainer(c, "-di", "--name", name) |
74121a42 |
id := strings.TrimSpace(out) |
6345208b |
assert.NilError(c, waitRun(id)) |
74121a42 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerRestart(context.Background(), name, nil) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
|
2743e2d8 |
assert.Assert(c, waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 15*time.Second) == nil) |
74121a42 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIStart(c *testing.T) { |
f7e417ea |
name := "testing-start" |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
Cmd: append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...),
OpenStdin: true, |
f7e417ea |
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name) |
6345208b |
assert.NilError(c, err) |
f7e417ea |
|
0fd5a654 |
err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) |
6345208b |
assert.NilError(c, err) |
f7e417ea |
// second call to start should give 304 |
0fd5a654 |
// maybe add ContainerStartWithRaw to test it
err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) |
6345208b |
assert.NilError(c, err) |
f4a1e3db |
// TODO(tibor): figure out why this doesn't work on windows |
f7e417ea |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIStop(c *testing.T) { |
f7e417ea |
name := "test-api-stop" |
777ee34b |
runSleepingContainer(c, "-i", "--name", name) |
0fd5a654 |
timeout := 30 * time.Second |
f7e417ea |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerStop(context.Background(), name, &timeout) |
6345208b |
assert.NilError(c, err) |
2743e2d8 |
assert.Assert(c, waitInspect(name, "{{ .State.Running }}", "false", 60*time.Second) == nil) |
f7e417ea |
// second call to start should give 304 |
0fd5a654 |
// maybe add ContainerStartWithRaw to test it
err = cli.ContainerStop(context.Background(), name, &timeout) |
6345208b |
assert.NilError(c, err) |
f7e417ea |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIWait(c *testing.T) { |
f7e417ea |
name := "test-api-wait" |
777ee34b |
sleepCmd := "/bin/sleep" |
18a771a7 |
if testEnv.OSType == "windows" { |
777ee34b |
sleepCmd = "sleep"
} |
94b1bb82 |
dockerCmd(c, "run", "--name", name, "busybox", sleepCmd, "2") |
f7e417ea |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
f7e417ea |
|
101ff26e |
waitResC, errC := cli.ContainerWait(context.Background(), name, "") |
0fd5a654 |
select {
case err = <-errC: |
6345208b |
assert.NilError(c, err) |
101ff26e |
case waitRes := <-waitResC:
assert.Equal(c, waitRes.StatusCode, int64(0)) |
0fd5a654 |
} |
f7e417ea |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICopyNotExistsAnyMore(c *testing.T) { |
f7e417ea |
name := "test-container-api-copy" |
5c295460 |
dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") |
f7e417ea |
postData := types.CopyConfig{
Resource: "/test.txt",
} |
0fd5a654 |
// no copy in client/
res, _, err := request.Post("/containers/"+name+"/copy", request.JSONBody(postData)) |
6345208b |
assert.NilError(c, err)
assert.Equal(c, res.StatusCode, http.StatusNotFound) |
42832890 |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICopyPre124(c *testing.T) { |
f811d5b1 |
testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later |
42832890 |
name := "test-container-api-copy"
dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
postData := types.CopyConfig{
Resource: "/test.txt",
}
|
0fd5a654 |
res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) |
6345208b |
assert.NilError(c, err)
assert.Equal(c, res.StatusCode, http.StatusOK) |
f7e417ea |
found := false |
0fd5a654 |
for tarReader := tar.NewReader(body); ; { |
f7e417ea |
h, err := tarReader.Next()
if err != nil {
if err == io.EOF {
break
}
c.Fatal(err)
}
if h.Name == "test.txt" {
found = true
break
}
} |
d0fc8d08 |
assert.Assert(c, found) |
f7e417ea |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICopyResourcePathEmptyPre124(c *testing.T) { |
f811d5b1 |
testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later |
f7e417ea |
name := "test-container-api-copy-resource-empty" |
5c295460 |
dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") |
f7e417ea |
postData := types.CopyConfig{
Resource: "",
}
|
0fd5a654 |
res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) |
6345208b |
assert.NilError(c, err) |
e4408318 |
if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") { |
6345208b |
assert.Equal(c, res.StatusCode, http.StatusBadRequest) |
e4408318 |
} else { |
0fa116fa |
assert.Assert(c, res.StatusCode != http.StatusOK) |
e4408318 |
} |
0fd5a654 |
b, err := request.ReadBody(body) |
6345208b |
assert.NilError(c, err) |
cc012897 |
assert.Assert(c, is.Regexp("^Path cannot be empty\n$", string(b))) |
f2c9e391 |
|
f7e417ea |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICopyResourcePathNotFoundPre124(c *testing.T) { |
f811d5b1 |
testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later |
f7e417ea |
name := "test-container-api-copy-resource-not-found" |
5c295460 |
dockerCmd(c, "run", "--name", name, "busybox") |
f7e417ea |
postData := types.CopyConfig{
Resource: "/notexist",
}
|
0fd5a654 |
res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) |
6345208b |
assert.NilError(c, err) |
e4408318 |
if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") { |
6345208b |
assert.Equal(c, res.StatusCode, http.StatusInternalServerError) |
e4408318 |
} else { |
6345208b |
assert.Equal(c, res.StatusCode, http.StatusNotFound) |
e4408318 |
} |
0fd5a654 |
b, err := request.ReadBody(body) |
6345208b |
assert.NilError(c, err) |
cc012897 |
assert.Assert(c, is.Regexp("^Could not find the file /notexist in container "+name+"\n$", string(b))) |
f2c9e391 |
|
f7e417ea |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICopyContainerNotFoundPr124(c *testing.T) { |
f811d5b1 |
testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later |
f7e417ea |
postData := types.CopyConfig{
Resource: "/something",
}
|
0fd5a654 |
res, _, err := request.Post("/v1.23/containers/notexists/copy", request.JSONBody(postData)) |
6345208b |
assert.NilError(c, err)
assert.Equal(c, res.StatusCode, http.StatusNotFound) |
f7e417ea |
} |
8771cafa |
|
1d92789b |
func (s *DockerSuite) TestContainerAPIDelete(c *testing.T) { |
a899aa67 |
out := runSleepingContainer(c) |
8771cafa |
id := strings.TrimSpace(out) |
6345208b |
assert.NilError(c, waitRun(id)) |
8771cafa |
|
5c295460 |
dockerCmd(c, "stop", id) |
8771cafa |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{}) |
6345208b |
assert.NilError(c, err) |
8771cafa |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIDeleteNotExist(c *testing.T) { |
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerRemove(context.Background(), "doesnotexist", types.ContainerRemoveOptions{}) |
6345208b |
assert.ErrorContains(c, err, "No such container: doesnotexist") |
8771cafa |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIDeleteForce(c *testing.T) { |
a899aa67 |
out := runSleepingContainer(c) |
8771cafa |
id := strings.TrimSpace(out) |
6345208b |
assert.NilError(c, waitRun(id)) |
8771cafa |
|
0fd5a654 |
removeOptions := types.ContainerRemoveOptions{
Force: true,
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerRemove(context.Background(), id, removeOptions) |
6345208b |
assert.NilError(c, err) |
8771cafa |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIDeleteRemoveLinks(c *testing.T) { |
2b0a7422 |
// Windows does not support links |
f9a3558a |
testRequires(c, DaemonIsLinux) |
5c295460 |
out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top") |
8771cafa |
id := strings.TrimSpace(out) |
6345208b |
assert.NilError(c, waitRun(id)) |
8771cafa |
|
5c295460 |
out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top") |
8771cafa |
id2 := strings.TrimSpace(out) |
2743e2d8 |
assert.Assert(c, waitRun(id2) == nil) |
8771cafa |
|
62a856e9 |
links := inspectFieldJSON(c, id2, "HostConfig.Links") |
6135eec3 |
assert.Equal(c, links, "[\"/tlink1:/tlink2/tlink1\"]", "expected to have links between containers") |
8771cafa |
|
0fd5a654 |
removeOptions := types.ContainerRemoveOptions{
RemoveLinks: true,
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerRemove(context.Background(), "tlink2/tlink1", removeOptions) |
6345208b |
assert.NilError(c, err) |
8771cafa |
|
62a856e9 |
linksPostRm := inspectFieldJSON(c, id2, "HostConfig.Links") |
6135eec3 |
assert.Equal(c, linksPostRm, "null", "call to api deleteContainer links should have removed the specified links") |
8771cafa |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIDeleteConflict(c *testing.T) { |
a899aa67 |
out := runSleepingContainer(c) |
8771cafa |
id := strings.TrimSpace(out) |
6345208b |
assert.NilError(c, waitRun(id)) |
8771cafa |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{})
expected := "cannot remove a running container" |
6345208b |
assert.ErrorContains(c, err, expected) |
8771cafa |
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPIDeleteRemoveVolume(c *testing.T) { |
43b15e92 |
testRequires(c, testEnv.IsLocalDaemon) |
8771cafa |
|
2b0a7422 |
vol := "/testvolume" |
18a771a7 |
if testEnv.OSType == "windows" { |
2b0a7422 |
vol = `c:\testvolume`
}
|
a899aa67 |
out := runSleepingContainer(c, "-v", vol) |
8771cafa |
id := strings.TrimSpace(out) |
6345208b |
assert.NilError(c, waitRun(id)) |
8771cafa |
|
2b0a7422 |
source, err := inspectMountSourceField(id, vol) |
6345208b |
assert.NilError(c, err) |
1c3cb2d3 |
_, err = os.Stat(source) |
6345208b |
assert.NilError(c, err) |
8771cafa |
|
0fd5a654 |
removeOptions := types.ContainerRemoveOptions{
Force: true,
RemoveVolumes: true,
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerRemove(context.Background(), id, removeOptions) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
|
4bdf957c |
_, err = os.Stat(source) |
0fabf3e4 |
assert.Assert(c, os.IsNotExist(err), "expected to get ErrNotExist error, got %v", err) |
8771cafa |
} |
7c574b9e |
// Regression test for https://github.com/docker/docker/issues/6231 |
1d92789b |
func (s *DockerSuite) TestContainerAPIChunkedEncoding(c *testing.T) { |
7c574b9e |
|
0a8386c8 |
config := map[string]interface{}{
"Image": "busybox", |
52f04748 |
"Cmd": append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...), |
0a8386c8 |
"OpenStdin": true,
}
|
42f6fdf0 |
resp, _, err := request.Post("/containers/create", request.JSONBody(config), request.With(func(req *http.Request) error { |
d69d4799 |
// This is a cheat to make the http request do chunked encoding
// Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite
// https://golang.org/src/pkg/net/http/request.go?s=11980:12172
req.ContentLength = -1
return nil |
42f6fdf0 |
})) |
6135eec3 |
assert.Assert(c, err == nil, "error creating container with chunked encoding") |
1e98fb5a |
defer resp.Body.Close() |
6345208b |
assert.Equal(c, resp.StatusCode, http.StatusCreated) |
7c574b9e |
} |
68e9c078 |
|
1d92789b |
func (s *DockerSuite) TestContainerAPIPostContainerStop(c *testing.T) { |
a899aa67 |
out := runSleepingContainer(c) |
68e9c078 |
containerID := strings.TrimSpace(out) |
2743e2d8 |
assert.Assert(c, waitRun(containerID) == nil) |
68e9c078 |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerStop(context.Background(), containerID, nil) |
6345208b |
assert.NilError(c, err) |
2743e2d8 |
assert.Assert(c, waitInspect(containerID, "{{ .State.Running }}", "false", 60*time.Second) == nil) |
68e9c078 |
} |
17d6f00e |
// #14170 |
1d92789b |
func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *testing.T) { |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
Entrypoint: []string{"echo"},
Cmd: []string{"hello", "world"},
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest") |
6345208b |
assert.NilError(c, err) |
17d6f00e |
out, _ := dockerCmd(c, "start", "-a", "echotest") |
6345208b |
assert.Equal(c, strings.TrimSpace(out), "hello world") |
17d6f00e |
config2 := struct {
Image string |
0fd5a654 |
Entrypoint string |
17d6f00e |
Cmd []string |
0fd5a654 |
}{"busybox", "echo", []string{"hello", "world"}}
_, _, err = request.Post("/containers/create?name=echotest2", request.JSONBody(config2)) |
6345208b |
assert.NilError(c, err) |
17d6f00e |
out, _ = dockerCmd(c, "start", "-a", "echotest2") |
6345208b |
assert.Equal(c, strings.TrimSpace(out), "hello world") |
17d6f00e |
}
// #14170 |
1d92789b |
func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *testing.T) { |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
Cmd: []string{"echo", "hello", "world"},
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest") |
6345208b |
assert.NilError(c, err) |
17d6f00e |
out, _ := dockerCmd(c, "start", "-a", "echotest") |
6345208b |
assert.Equal(c, strings.TrimSpace(out), "hello world") |
17d6f00e |
config2 := struct { |
0fd5a654 |
Image string
Entrypoint string
Cmd string
}{"busybox", "echo", "hello world"}
_, _, err = request.Post("/containers/create?name=echotest2", request.JSONBody(config2)) |
6345208b |
assert.NilError(c, err) |
17d6f00e |
out, _ = dockerCmd(c, "start", "-a", "echotest2") |
6345208b |
assert.Equal(c, strings.TrimSpace(out), "hello world") |
17d6f00e |
} |
10a3061c |
// regression #14318 |
80d7bfd5 |
// for backward compatibility testing with and without CAP_ prefix
// and with upper and lowercase |
1d92789b |
func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *testing.T) { |
2b0a7422 |
// Windows doesn't support CapAdd/CapDrop |
f9a3558a |
testRequires(c, DaemonIsLinux) |
10a3061c |
config := struct {
Image string
CapAdd string
CapDrop string |
80d7bfd5 |
}{"busybox", "NET_ADMIN", "cap_sys_admin"} |
0fd5a654 |
res, _, err := request.Post("/containers/create?name=capaddtest0", request.JSONBody(config)) |
6345208b |
assert.NilError(c, err)
assert.Equal(c, res.StatusCode, http.StatusCreated) |
10a3061c |
|
0fd5a654 |
config2 := containertypes.Config{
Image: "busybox",
}
hostConfig := containertypes.HostConfig{ |
80d7bfd5 |
CapAdd: []string{"net_admin", "SYS_ADMIN"},
CapDrop: []string{"SETGID", "CAP_SETPCAP"}, |
0fd5a654 |
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, nil, "capaddtest1") |
6345208b |
assert.NilError(c, err) |
10a3061c |
} |
65121e5f |
|
c358a4cd |
// #14915 |
1d92789b |
func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *testing.T) { |
f811d5b1 |
testRequires(c, DaemonIsLinux) // Windows only support 1.25 or later |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
}
|
6977f468 |
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.18")) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "") |
6345208b |
assert.NilError(c, err) |
c358a4cd |
} |
75f6929b |
// Ensure an error occurs when you have a container read-only rootfs but you
// extract an archive to a symlink in a writable volume which points to a
// directory outside of the volume. |
1d92789b |
func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *testing.T) { |
2b0a7422 |
// Windows does not support read-only rootfs |
ea3afdad |
// Requires local volume mount bind.
// --read-only + userns has remount issues |
43b15e92 |
testRequires(c, testEnv.IsLocalDaemon, NotUserNamespace, DaemonIsLinux) |
75f6929b |
testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-")
defer os.RemoveAll(testVol)
makeTestContentInDir(c, testVol)
cID := makeTestContainer(c, testContainerOptions{
readOnly: true,
volumes: defaultVolumes(testVol), // Our bind mount is at /vol2
})
// Attempt to extract to a symlink in the volume which points to a
// directory outside the volume. This should cause an error because the
// rootfs is read-only. |
3a4bb96a |
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.20")) |
6345208b |
assert.NilError(c, err) |
75f6929b |
|
0fd5a654 |
err = cli.CopyToContainer(context.Background(), cID, "/vol2/symlinkToAbsDir", nil, types.CopyToContainerOptions{}) |
6345208b |
assert.ErrorContains(c, err, "container rootfs is marked read-only") |
75f6929b |
} |
26bd5e3a |
|
1d92789b |
func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *testing.T) { |
2b0a7422 |
// Not supported on Windows |
94464e3a |
testRequires(c, DaemonIsLinux)
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
config := containertypes.Config{
Image: "busybox",
}
hostConfig1 := containertypes.HostConfig{
Resources: containertypes.Resources{
CpusetCpus: "1-42,,",
},
}
name := "wrong-cpuset-cpus"
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, nil, name) |
322e2a7d |
expected := "Invalid value 1-42,, for cpuset cpus" |
6345208b |
assert.ErrorContains(c, err, expected) |
94464e3a |
|
0fd5a654 |
hostConfig2 := containertypes.HostConfig{
Resources: containertypes.Resources{
CpusetMems: "42-3,1--",
},
} |
94464e3a |
name = "wrong-cpuset-mems" |
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, nil, name) |
322e2a7d |
expected = "Invalid value 42-3,1-- for cpuset mems" |
6345208b |
assert.ErrorContains(c, err, expected) |
94464e3a |
} |
d7117a1b |
|
1d92789b |
func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *testing.T) { |
2b0a7422 |
// ShmSize is not supported on Windows |
25c38339 |
testRequires(c, DaemonIsLinux) |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
}
hostConfig := containertypes.HostConfig{
ShmSize: -1, |
ef1d410b |
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "") |
6345208b |
assert.ErrorContains(c, err, "SHM size can not be less than 0") |
ef1d410b |
}
|
1d92789b |
func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *testing.T) { |
2b0a7422 |
// ShmSize is not supported on Windows |
25c38339 |
testRequires(c, DaemonIsLinux) |
4354b348 |
var defaultSHMSize int64 = 67108864 |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
Cmd: []string{"mount"}, |
ef1d410b |
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
ef1d410b |
|
7a9cb29f |
container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "") |
6345208b |
assert.NilError(c, err) |
ef1d410b |
|
0fd5a654 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
6345208b |
assert.NilError(c, err) |
ef1d410b |
|
6dc7f4c1 |
assert.Equal(c, containerJSON.HostConfig.ShmSize, defaultSHMSize) |
ef1d410b |
out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
if !shmRegexp.MatchString(out) {
c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
}
}
|
1d92789b |
func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *testing.T) { |
2b0a7422 |
// ShmSize is not supported on Windows |
25c38339 |
testRequires(c, DaemonIsLinux) |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
Cmd: []string{"mount"}, |
ef1d410b |
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
ef1d410b |
|
7a9cb29f |
container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "") |
6345208b |
assert.NilError(c, err) |
ef1d410b |
|
0fd5a654 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
6345208b |
assert.NilError(c, err) |
ef1d410b |
|
6dc7f4c1 |
assert.Equal(c, containerJSON.HostConfig.ShmSize, int64(67108864)) |
ef1d410b |
out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
if !shmRegexp.MatchString(out) {
c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
}
}
|
1d92789b |
func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *testing.T) { |
2b0a7422 |
// ShmSize is not supported on Windows |
25c38339 |
testRequires(c, DaemonIsLinux) |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
Cmd: []string{"mount"}, |
ef1d410b |
}
|
0fd5a654 |
hostConfig := containertypes.HostConfig{
ShmSize: 1073741824,
} |
ef1d410b |
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
ef1d410b |
|
7a9cb29f |
container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "") |
6345208b |
assert.NilError(c, err) |
ef1d410b |
|
0fd5a654 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
6345208b |
assert.NilError(c, err) |
ef1d410b |
|
6dc7f4c1 |
assert.Equal(c, containerJSON.HostConfig.ShmSize, int64(1073741824)) |
ef1d410b |
out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
if !shmRegex.MatchString(out) {
c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
}
}
|
1d92789b |
func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *testing.T) { |
2b0a7422 |
// Swappiness is not supported on Windows |
25c38339 |
testRequires(c, DaemonIsLinux) |
0fd5a654 |
config := containertypes.Config{
Image: "busybox", |
ef1d410b |
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close() |
ef1d410b |
|
7a9cb29f |
container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "") |
6345208b |
assert.NilError(c, err) |
ef1d410b |
|
0fd5a654 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
6345208b |
assert.NilError(c, err) |
ef1d410b |
|
e4408318 |
if versions.LessThan(testEnv.DaemonAPIVersion(), "1.31") { |
6dc7f4c1 |
assert.Equal(c, *containerJSON.HostConfig.MemorySwappiness, int64(-1)) |
e4408318 |
} else { |
2743e2d8 |
assert.Assert(c, containerJSON.HostConfig.MemorySwappiness == nil) |
e4408318 |
} |
ef1d410b |
} |
d3af7f28 |
// check validation is done daemon side and not only in cli |
1d92789b |
func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *testing.T) { |
2b0a7422 |
// OomScoreAdj is not supported on Windows |
d3af7f28 |
testRequires(c, DaemonIsLinux)
|
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
}
hostConfig := containertypes.HostConfig{
OomScoreAdj: 1001,
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
d3af7f28 |
name := "oomscoreadj-over" |
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name) |
322e2a7d |
|
aae4bcf7 |
expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]" |
6345208b |
assert.ErrorContains(c, err, expected) |
0fd5a654 |
hostConfig = containertypes.HostConfig{
OomScoreAdj: -1001, |
d3af7f28 |
}
name = "oomscoreadj-low" |
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name) |
0fd5a654 |
|
aae4bcf7 |
expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]" |
6345208b |
assert.ErrorContains(c, err, expected) |
d3af7f28 |
} |
9d8071a7 |
|
644a7426 |
// test case for #22210 where an empty container name caused panic. |
1d92789b |
func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *testing.T) { |
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
err = cli.ContainerRemove(context.Background(), "", types.ContainerRemoveOptions{}) |
6345208b |
assert.ErrorContains(c, err, "No such container") |
9d8071a7 |
} |
7bb9c539 |
|
1d92789b |
func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) { |
7bb9c539 |
// Problematic on Windows as Windows does not support stats
testRequires(c, DaemonIsLinux)
name := "testing-network-disabled" |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
Cmd: []string{"top"},
NetworkDisabled: true, |
7bb9c539 |
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name) |
6345208b |
assert.NilError(c, err) |
7bb9c539 |
|
0fd5a654 |
err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) |
6345208b |
assert.NilError(c, err) |
7bb9c539 |
|
2743e2d8 |
assert.Assert(c, waitRun(name) == nil) |
7bb9c539 |
type b struct { |
0fd5a654 |
stats types.ContainerStats
err error |
7bb9c539 |
}
bc := make(chan b, 1)
go func() { |
0fd5a654 |
stats, err := cli.ContainerStats(context.Background(), name, false)
bc <- b{stats, err} |
7bb9c539 |
}()
// allow some time to stream the stats from the container
time.Sleep(4 * time.Second)
dockerCmd(c, "rm", "-f", name)
// collect the results from the stats stream or timeout and fail
// if the stream was not disconnected.
select {
case <-time.After(2 * time.Second):
c.Fatal("stream was not closed after container was removed")
case sr := <-bc: |
2743e2d8 |
assert.Assert(c, sr.err == nil) |
0fd5a654 |
sr.stats.Body.Close() |
7bb9c539 |
}
} |
fc7b904d |
|
1d92789b |
func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) { |
fc7b904d |
type testCase struct { |
0fd5a654 |
config containertypes.Config
hostConfig containertypes.HostConfig
msg string |
fc7b904d |
}
prefix, slash := getPrefixAndSlashFromDaemonPlatform()
destPath := prefix + slash + "foo"
notExistPath := prefix + slash + "notexist"
cases := []testCase{ |
18768fdc |
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "notreal",
Target: destPath,
},
},
},
msg: "mount type unknown", |
18768fdc |
},
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "bind"}}},
msg: "Target must not be empty", |
18768fdc |
},
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "bind",
Target: destPath}}},
msg: "Source must not be empty", |
18768fdc |
},
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "bind",
Source: notExistPath,
Target: destPath}}}, |
e55d6fc8 |
msg: "source path does not exist",
// FIXME(vdemeester) fails into e2e, migrate to integration/container anyway |
574db7a5 |
// msg: "source path does not exist: " + notExistPath, |
18768fdc |
},
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "volume"}}},
msg: "Target must not be empty", |
18768fdc |
},
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "volume",
Source: "hello",
Target: destPath}}},
msg: "", |
18768fdc |
},
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "volume",
Source: "hello2",
Target: destPath,
VolumeOptions: &mounttypes.VolumeOptions{
DriverConfig: &mounttypes.Driver{
Name: "local"}}}}},
msg: "", |
18768fdc |
}, |
fc7b904d |
}
|
43b15e92 |
if testEnv.IsLocalDaemon() { |
fc7b904d |
tmpDir, err := ioutils.TempDir("", "test-mounts-api") |
6345208b |
assert.NilError(c, err) |
fc7b904d |
defer os.RemoveAll(tmpDir)
cases = append(cases, []testCase{ |
18768fdc |
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "bind",
Source: tmpDir,
Target: destPath}}},
msg: "", |
18768fdc |
},
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "bind",
Source: tmpDir,
Target: destPath,
VolumeOptions: &mounttypes.VolumeOptions{}}}},
msg: "VolumeOptions must not be specified", |
18768fdc |
}, |
fc7b904d |
}...)
}
|
11b88be2 |
if DaemonIsWindows() {
cases = append(cases, []testCase{
{
config: containertypes.Config{
Image: "busybox",
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{
{
Type: "volume",
Source: "not-supported-on-windows",
Target: destPath,
VolumeOptions: &mounttypes.VolumeOptions{
DriverConfig: &mounttypes.Driver{
Name: "local",
Options: map[string]string{"type": "tmpfs"},
},
},
},
},
},
msg: `options are not supported on this platform`,
},
}...)
}
|
930a9869 |
if DaemonIsLinux() { |
fc7b904d |
cases = append(cases, []testCase{ |
18768fdc |
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{ |
d5b271c1 |
Mounts: []mounttypes.Mount{
{
Type: "volume",
Source: "missing-device-opt",
Target: destPath,
VolumeOptions: &mounttypes.VolumeOptions{
DriverConfig: &mounttypes.Driver{
Name: "local", |
11b88be2 |
Options: map[string]string{"foobar": "foobaz"},
},
},
},
},
},
msg: `invalid option: "foobar"`,
},
{
config: containertypes.Config{
Image: "busybox",
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{
{
Type: "volume",
Source: "missing-device-opt",
Target: destPath,
VolumeOptions: &mounttypes.VolumeOptions{
DriverConfig: &mounttypes.Driver{
Name: "local", |
d5b271c1 |
Options: map[string]string{"type": "tmpfs"},
},
},
},
},
},
msg: `missing required option: "device"`,
},
{
config: containertypes.Config{
Image: "busybox",
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{
{
Type: "volume",
Source: "missing-type-opt",
Target: destPath,
VolumeOptions: &mounttypes.VolumeOptions{
DriverConfig: &mounttypes.Driver{
Name: "local",
Options: map[string]string{"device": "tmpfs"},
},
},
},
},
},
msg: `missing required option: "type"`,
},
{
config: containertypes.Config{
Image: "busybox",
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{
{
Type: "volume",
Source: "hello4",
Target: destPath,
VolumeOptions: &mounttypes.VolumeOptions{
DriverConfig: &mounttypes.Driver{
Name: "local",
Options: map[string]string{"o": "size=1", "type": "tmpfs", "device": "tmpfs"},
},
},
},
},
}, |
0fd5a654 |
msg: "", |
18768fdc |
},
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "tmpfs",
Target: destPath}}},
msg: "", |
18768fdc |
},
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "tmpfs",
Target: destPath,
TmpfsOptions: &mounttypes.TmpfsOptions{
SizeBytes: 4096 * 1024,
Mode: 0700,
}}}},
msg: "", |
18768fdc |
},
{ |
0fd5a654 |
config: containertypes.Config{ |
18768fdc |
Image: "busybox", |
0fd5a654 |
},
hostConfig: containertypes.HostConfig{
Mounts: []mounttypes.Mount{{
Type: "tmpfs",
Source: "/shouldnotbespecified",
Target: destPath}}},
msg: "Source must not be specified", |
18768fdc |
}, |
fc7b904d |
}...)
} |
ab9c726f |
apiClient, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
ab9c726f |
defer apiClient.Close() |
fc7b904d |
|
11b88be2 |
// TODO add checks for statuscode returned by API |
fc7b904d |
for i, x := range cases { |
ab9c726f |
x := x
c.Run(fmt.Sprintf("case %d", i), func(c *testing.T) { |
7a9cb29f |
_, err = apiClient.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, nil, "") |
ab9c726f |
if len(x.msg) > 0 {
assert.ErrorContains(c, err, x.msg, "%v", cases[i].config)
} else {
assert.NilError(c, err)
}
}) |
fc7b904d |
}
}
|
1d92789b |
func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *testing.T) { |
43b15e92 |
testRequires(c, NotUserNamespace, testEnv.IsLocalDaemon) |
fc7b904d |
// also with data in the host side
prefix, slash := getPrefixAndSlashFromDaemonPlatform()
destPath := prefix + slash + "foo"
tmpDir, err := ioutil.TempDir("", "test-mounts-api-bind") |
6345208b |
assert.NilError(c, err) |
fc7b904d |
defer os.RemoveAll(tmpDir) |
fdc1b220 |
err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 0666) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
config := containertypes.Config{
Image: "busybox",
Cmd: []string{"/bin/sh", "-c", "cat /foo/bar"}, |
fc7b904d |
} |
0fd5a654 |
hostConfig := containertypes.HostConfig{
Mounts: []mounttypes.Mount{
{Type: "bind", Source: tmpDir, Target: destPath},
},
} |
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "test") |
6345208b |
assert.NilError(c, err) |
fc7b904d |
out, _ := dockerCmd(c, "start", "-a", "test") |
6345208b |
assert.Equal(c, out, "hello") |
fc7b904d |
}
// Test Mounts comes out as expected for the MountPoint |
1d92789b |
func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) { |
fc7b904d |
prefix, slash := getPrefixAndSlashFromDaemonPlatform()
destPath := prefix + slash + "foo"
var (
testImg string
) |
18a771a7 |
if testEnv.OSType != "windows" { |
c10f6ef4 |
testImg = "test-mount-config" |
50c4475d |
buildImageSuccessfully(c, testImg, build.WithDockerfile(` |
fc7b904d |
FROM busybox
RUN mkdir `+destPath+` && touch `+destPath+slash+`bar
CMD cat `+destPath+slash+`bar |
c10f6ef4 |
`)) |
fc7b904d |
} else {
testImg = "busybox"
}
type testCase struct { |
5bbf5cc6 |
spec mounttypes.Mount |
fc7b904d |
expected types.MountPoint
}
|
5bbf5cc6 |
var selinuxSharedLabel string |
e4408318 |
// this test label was added after a bug fix in 1.32, thus add requirements min API >= 1.32
// for the sake of making test pass in earlier versions
// bug fixed in https://github.com/moby/moby/pull/34684
if !versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
if runtime.GOOS == "linux" {
selinuxSharedLabel = "z"
} |
5bbf5cc6 |
}
|
fc7b904d |
cases := []testCase{
// use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest
// Validation of the actual `Mount` struct is done in another test is not needed here |
58b96ace |
{ |
5bbf5cc6 |
spec: mounttypes.Mount{Type: "volume", Target: destPath},
expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
},
{
spec: mounttypes.Mount{Type: "volume", Target: destPath + slash},
expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
},
{
spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"},
expected: types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
},
{
spec: mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"},
expected: types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
},
{
spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test3", VolumeOptions: &mounttypes.VolumeOptions{DriverConfig: &mounttypes.Driver{Name: volume.DefaultDriverName}}},
expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", Name: "test3", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, |
58b96ace |
}, |
fc7b904d |
}
|
43b15e92 |
if testEnv.IsLocalDaemon() { |
fc7b904d |
// setup temp dir for testing binds
tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1") |
6345208b |
assert.NilError(c, err) |
fc7b904d |
defer os.RemoveAll(tmpDir1)
cases = append(cases, []testCase{ |
58b96ace |
{ |
5bbf5cc6 |
spec: mounttypes.Mount{ |
58b96ace |
Type: "bind",
Source: tmpDir1,
Target: destPath,
}, |
5bbf5cc6 |
expected: types.MountPoint{ |
58b96ace |
Type: "bind",
RW: true,
Destination: destPath,
Source: tmpDir1,
},
}, |
5bbf5cc6 |
{
spec: mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true},
expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1},
}, |
fc7b904d |
}...)
// for modes only supported on Linux |
930a9869 |
if DaemonIsLinux() { |
fc7b904d |
tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3") |
6345208b |
assert.NilError(c, err) |
fc7b904d |
defer os.RemoveAll(tmpDir3)
|
7bb729e9 |
assert.Assert(c, mountWrapper(tmpDir3, tmpDir3, "none", "bind,shared") == nil) |
fc7b904d |
cases = append(cases, []testCase{ |
5bbf5cc6 |
{
spec: mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath},
expected: types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3},
},
{
spec: mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true},
expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3},
},
{
spec: mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true, BindOptions: &mounttypes.BindOptions{Propagation: "shared"}},
expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3, Propagation: "shared"},
}, |
fc7b904d |
}...)
}
}
|
18a771a7 |
if testEnv.OSType != "windows" { // Windows does not support volume populate |
fc7b904d |
cases = append(cases, []testCase{ |
5bbf5cc6 |
{
spec: mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
},
{
spec: mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
},
{
spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
expected: types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
},
{
spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test5", ReadOnly: true, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
expected: types.MountPoint{Type: "volume", Name: "test5", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
}, |
fc7b904d |
}...)
}
|
58b96ace |
ctx := context.Background()
apiclient := testEnv.APIClient() |
fc7b904d |
for i, x := range cases { |
9ca5ffd5 |
x := x
c.Run(fmt.Sprintf("%d config: %v", i, x.spec), func(c *testing.T) {
container, err := apiclient.ContainerCreate(
ctx,
&containertypes.Config{Image: testImg},
&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}},
&networktypes.NetworkingConfig{}, |
7a9cb29f |
nil, |
9ca5ffd5 |
"")
assert.NilError(c, err) |
58b96ace |
|
9ca5ffd5 |
containerInspect, err := apiclient.ContainerInspect(ctx, container.ID)
assert.NilError(c, err)
mps := containerInspect.Mounts
assert.Assert(c, is.Len(mps, 1))
mountPoint := mps[0] |
58b96ace |
|
9ca5ffd5 |
if x.expected.Source != "" {
assert.Check(c, is.Equal(x.expected.Source, mountPoint.Source))
}
if x.expected.Name != "" {
assert.Check(c, is.Equal(x.expected.Name, mountPoint.Name))
}
if x.expected.Driver != "" {
assert.Check(c, is.Equal(x.expected.Driver, mountPoint.Driver))
}
if x.expected.Propagation != "" {
assert.Check(c, is.Equal(x.expected.Propagation, mountPoint.Propagation))
}
assert.Check(c, is.Equal(x.expected.RW, mountPoint.RW))
assert.Check(c, is.Equal(x.expected.Type, mountPoint.Type))
assert.Check(c, is.Equal(x.expected.Mode, mountPoint.Mode))
assert.Check(c, is.Equal(x.expected.Destination, mountPoint.Destination)) |
58b96ace |
|
9ca5ffd5 |
err = apiclient.ContainerStart(ctx, container.ID, types.ContainerStartOptions{})
assert.NilError(c, err)
poll.WaitOn(c, containerExit(apiclient, container.ID), poll.WithDelay(time.Second)) |
58b96ace |
|
9ca5ffd5 |
err = apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
RemoveVolumes: true,
Force: true,
})
assert.NilError(c, err) |
58b96ace |
|
9ca5ffd5 |
switch { |
58b96ace |
|
9ca5ffd5 |
// Named volumes still exist after the container is removed
case x.spec.Type == "volume" && len(x.spec.Source) > 0:
_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
assert.NilError(c, err) |
58b96ace |
|
9ca5ffd5 |
// Bind mounts are never removed with the container
case x.spec.Type == "bind": |
58b96ace |
|
9ca5ffd5 |
// anonymous volumes are removed
default:
_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
assert.Check(c, client.IsErrNotFound(err))
}
}) |
58b96ace |
}
} |
fc7b904d |
|
58b96ace |
func containerExit(apiclient client.APIClient, name string) func(poll.LogT) poll.Result {
return func(logT poll.LogT) poll.Result {
container, err := apiclient.ContainerInspect(context.Background(), name)
if err != nil {
return poll.Error(err)
}
switch container.State.Status {
case "created", "running":
return poll.Continue("container %s is %s, waiting for exit", name, container.State.Status) |
fc7b904d |
} |
58b96ace |
return poll.Success() |
fc7b904d |
}
} |
18768fdc |
|
1d92789b |
func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *testing.T) { |
18768fdc |
testRequires(c, DaemonIsLinux)
type testCase struct { |
0fd5a654 |
cfg mounttypes.Mount |
18768fdc |
expectedOptions []string
}
target := "/foo"
cases := []testCase{
{ |
0fd5a654 |
cfg: mounttypes.Mount{
Type: "tmpfs",
Target: target}, |
18768fdc |
expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
},
{ |
0fd5a654 |
cfg: mounttypes.Mount{
Type: "tmpfs",
Target: target,
TmpfsOptions: &mounttypes.TmpfsOptions{
SizeBytes: 4096 * 1024, Mode: 0700}}, |
18768fdc |
expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k", "mode=700"},
},
}
|
c8ff5ecc |
cli, err := client.NewClientWithOpts(client.FromEnv) |
6345208b |
assert.NilError(c, err) |
0fd5a654 |
defer cli.Close()
config := containertypes.Config{
Image: "busybox",
Cmd: []string{"/bin/sh", "-c", fmt.Sprintf("mount | grep 'tmpfs on %s'", target)},
} |
18768fdc |
for i, x := range cases {
cName := fmt.Sprintf("test-tmpfs-%d", i) |
0fd5a654 |
hostConfig := containertypes.HostConfig{
Mounts: []mounttypes.Mount{x.cfg}, |
18768fdc |
} |
0fd5a654 |
|
7a9cb29f |
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, cName) |
6345208b |
assert.NilError(c, err) |
18768fdc |
out, _ := dockerCmd(c, "start", "-a", cName)
for _, option := range x.expectedOptions { |
98f2638f |
assert.Assert(c, strings.Contains(out, option)) |
18768fdc |
}
}
} |
114652ab |
// Regression test for #33334
// Makes sure that when a container which has a custom stop signal + restart=always
// gets killed (with SIGKILL) by the kill API, that the restart policy is cancelled. |
1d92789b |
func (s *DockerSuite) TestContainerKillCustomStopSignal(c *testing.T) { |
114652ab |
id := strings.TrimSpace(runSleepingContainer(c, "--stop-signal=SIGTERM", "--restart=always"))
res, _, err := request.Post("/containers/" + id + "/kill") |
6345208b |
assert.NilError(c, err) |
114652ab |
defer res.Body.Close()
b, err := ioutil.ReadAll(res.Body) |
6345208b |
assert.NilError(c, err)
assert.Equal(c, res.StatusCode, http.StatusNoContent, string(b)) |
114652ab |
err = waitInspect(id, "{{.State.Running}} {{.State.Restarting}}", "false false", 30*time.Second) |
6345208b |
assert.NilError(c, err) |
114652ab |
} |