This change will allocate network settings (IP and public ports) at container creation rather than start and keep them throughout the lifetime of the container (i.e. until it gets destroyed) instead of discarding them when the container is stopped. Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com> Upstream-commit: a4875937293f3b0a8ffc569608bbca40a456e9c8 Component: engine
2285 lines
64 KiB
Go
2285 lines
64 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/docker/docker/pkg/mount"
|
|
"github.com/docker/docker/pkg/networkfs/resolvconf"
|
|
"github.com/kr/pty"
|
|
)
|
|
|
|
// "test123" should be printed by docker run
|
|
func TestRunEchoStdout(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "busybox", "echo", "test123")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
}
|
|
|
|
if out != "test123\n" {
|
|
t.Errorf("container should've printed 'test123'")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - echo test123")
|
|
}
|
|
|
|
// "test" should be printed
|
|
func TestRunEchoStdoutWithMemoryLimit(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "-m", "4m", "busybox", "echo", "test")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
}
|
|
|
|
out = strings.Trim(out, "\r\n")
|
|
|
|
if expected := "test"; out != expected {
|
|
t.Errorf("container should've printed %q but printed %q", expected, out)
|
|
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - echo with memory limit")
|
|
}
|
|
|
|
// "test" should be printed
|
|
func TestRunEchoStdoutWitCPULimit(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "-c", "1000", "busybox", "echo", "test")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
}
|
|
|
|
if out != "test\n" {
|
|
t.Errorf("container should've printed 'test'")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - echo with CPU limit")
|
|
}
|
|
|
|
// "test" should be printed
|
|
func TestRunEchoStdoutWithCPUAndMemoryLimit(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "-c", "1000", "-m", "4m", "busybox", "echo", "test")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
}
|
|
|
|
if out != "test\n" {
|
|
t.Errorf("container should've printed 'test', got %q instead", out)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - echo with CPU and memory limit")
|
|
}
|
|
|
|
// "test" should be printed
|
|
func TestRunEchoNamedContainer(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "--name", "testfoonamedcontainer", "busybox", "echo", "test")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
}
|
|
|
|
if out != "test\n" {
|
|
t.Errorf("container should've printed 'test'")
|
|
}
|
|
|
|
if err := deleteContainer("testfoonamedcontainer"); err != nil {
|
|
t.Errorf("failed to remove the named container: %v", err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - echo with named container")
|
|
}
|
|
|
|
// docker run should not leak file descriptors
|
|
func TestRunLeakyFileDescriptors(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "busybox", "ls", "-C", "/proc/self/fd")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
}
|
|
|
|
// normally, we should only get 0, 1, and 2, but 3 gets created by "ls" when it does "opendir" on the "fd" directory
|
|
if out != "0 1 2 3\n" {
|
|
t.Errorf("container should've printed '0 1 2 3', not: %s", out)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - check file descriptor leakage")
|
|
}
|
|
|
|
// it should be possible to ping Google DNS resolver
|
|
// this will fail when Internet access is unavailable
|
|
func TestRunPingGoogle(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "busybox", "ping", "-c", "1", "8.8.8.8")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
}
|
|
|
|
errorOut(err, t, "container should've been able to ping 8.8.8.8")
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - ping 8.8.8.8")
|
|
}
|
|
|
|
// the exit code should be 0
|
|
// some versions of lxc might make this test fail
|
|
func TestRunExitCodeZero(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "busybox", "true")
|
|
exitCode, err := runCommand(runCmd)
|
|
errorOut(err, t, fmt.Sprintf("%s", err))
|
|
|
|
if exitCode != 0 {
|
|
t.Errorf("container should've exited with exit code 0")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - exit with 0")
|
|
}
|
|
|
|
// the exit code should be 1
|
|
// some versions of lxc might make this test fail
|
|
func TestRunExitCodeOne(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "busybox", "false")
|
|
exitCode, err := runCommand(runCmd)
|
|
if err != nil && !strings.Contains("exit status 1", fmt.Sprintf("%s", err)) {
|
|
t.Fatal(err)
|
|
}
|
|
if exitCode != 1 {
|
|
t.Errorf("container should've exited with exit code 1")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - exit with 1")
|
|
}
|
|
|
|
// it should be possible to pipe in data via stdin to a process running in a container
|
|
// some versions of lxc might make this test fail
|
|
func TestRunStdinPipe(t *testing.T) {
|
|
runCmd := exec.Command("bash", "-c", `echo "blahblah" | docker run -i -a stdin busybox cat`)
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
}
|
|
|
|
out = stripTrailingCharacters(out)
|
|
|
|
inspectCmd := exec.Command(dockerBinary, "inspect", out)
|
|
inspectOut, _, err := runCommandWithOutput(inspectCmd)
|
|
errorOut(err, t, fmt.Sprintf("out should've been a container id: %s %s", out, inspectOut))
|
|
|
|
waitCmd := exec.Command(dockerBinary, "wait", out)
|
|
_, _, err = runCommandWithOutput(waitCmd)
|
|
errorOut(err, t, fmt.Sprintf("error thrown while waiting for container: %s", out))
|
|
|
|
logsCmd := exec.Command(dockerBinary, "logs", out)
|
|
containerLogs, _, err := runCommandWithOutput(logsCmd)
|
|
errorOut(err, t, fmt.Sprintf("error thrown while trying to get container logs: %s", err))
|
|
|
|
containerLogs = stripTrailingCharacters(containerLogs)
|
|
|
|
if containerLogs != "blahblah" {
|
|
t.Errorf("logs didn't print the container's logs %s", containerLogs)
|
|
}
|
|
|
|
rmCmd := exec.Command(dockerBinary, "rm", out)
|
|
_, _, err = runCommandWithOutput(rmCmd)
|
|
errorOut(err, t, fmt.Sprintf("rm failed to remove container %s", err))
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - pipe in with -i -a stdin")
|
|
}
|
|
|
|
// the container's ID should be printed when starting a container in detached mode
|
|
func TestRunDetachedContainerIDPrinting(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "true")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
}
|
|
|
|
out = stripTrailingCharacters(out)
|
|
|
|
inspectCmd := exec.Command(dockerBinary, "inspect", out)
|
|
inspectOut, _, err := runCommandWithOutput(inspectCmd)
|
|
errorOut(err, t, fmt.Sprintf("out should've been a container id: %s %s", out, inspectOut))
|
|
|
|
waitCmd := exec.Command(dockerBinary, "wait", out)
|
|
_, _, err = runCommandWithOutput(waitCmd)
|
|
errorOut(err, t, fmt.Sprintf("error thrown while waiting for container: %s", out))
|
|
|
|
rmCmd := exec.Command(dockerBinary, "rm", out)
|
|
rmOut, _, err := runCommandWithOutput(rmCmd)
|
|
errorOut(err, t, "rm failed to remove container")
|
|
|
|
rmOut = stripTrailingCharacters(rmOut)
|
|
if rmOut != out {
|
|
t.Errorf("rm didn't print the container ID %s %s", out, rmOut)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - print container ID in detached mode")
|
|
}
|
|
|
|
// the working directory should be set correctly
|
|
func TestRunWorkingDirectory(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "-w", "/root", "busybox", "pwd")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
}
|
|
|
|
out = stripTrailingCharacters(out)
|
|
|
|
if out != "/root" {
|
|
t.Errorf("-w failed to set working directory")
|
|
}
|
|
|
|
runCmd = exec.Command(dockerBinary, "run", "--workdir", "/root", "busybox", "pwd")
|
|
out, _, _, err = runCommandWithStdoutStderr(runCmd)
|
|
errorOut(err, t, out)
|
|
|
|
out = stripTrailingCharacters(out)
|
|
|
|
if out != "/root" {
|
|
t.Errorf("--workdir failed to set working directory")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - run with working directory set by -w")
|
|
logDone("run - run with working directory set by --workdir")
|
|
}
|
|
|
|
// pinging Google's DNS resolver should fail when we disable the networking
|
|
func TestRunWithoutNetworking(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "--net=none", "busybox", "ping", "-c", "1", "8.8.8.8")
|
|
out, _, exitCode, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil && exitCode != 1 {
|
|
t.Fatal(out, err)
|
|
}
|
|
if exitCode != 1 {
|
|
t.Errorf("--net=none should've disabled the network; the container shouldn't have been able to ping 8.8.8.8")
|
|
}
|
|
|
|
runCmd = exec.Command(dockerBinary, "run", "-n=false", "busybox", "ping", "-c", "1", "8.8.8.8")
|
|
out, _, exitCode, err = runCommandWithStdoutStderr(runCmd)
|
|
if err != nil && exitCode != 1 {
|
|
t.Fatal(out, err)
|
|
}
|
|
if exitCode != 1 {
|
|
t.Errorf("-n=false should've disabled the network; the container shouldn't have been able to ping 8.8.8.8")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - disable networking with --net=none")
|
|
logDone("run - disable networking with -n=false")
|
|
}
|
|
|
|
// Regression test for #4741
|
|
func TestRunWithVolumesAsFiles(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "--name", "test-data", "--volume", "/etc/hosts:/target-file", "busybox", "true")
|
|
out, stderr, exitCode, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil && exitCode != 0 {
|
|
t.Fatal("1", out, stderr, err)
|
|
}
|
|
|
|
runCmd = exec.Command(dockerBinary, "run", "--volumes-from", "test-data", "busybox", "cat", "/target-file")
|
|
out, stderr, exitCode, err = runCommandWithStdoutStderr(runCmd)
|
|
if err != nil && exitCode != 0 {
|
|
t.Fatal("2", out, stderr, err)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - regression test for #4741 - volumes from as files")
|
|
}
|
|
|
|
// Regression test for #4979
|
|
func TestRunWithVolumesFromExited(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "--name", "test-data", "--volume", "/some/dir", "busybox", "touch", "/some/dir/file")
|
|
out, stderr, exitCode, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil && exitCode != 0 {
|
|
t.Fatal("1", out, stderr, err)
|
|
}
|
|
|
|
runCmd = exec.Command(dockerBinary, "run", "--volumes-from", "test-data", "busybox", "cat", "/some/dir/file")
|
|
out, stderr, exitCode, err = runCommandWithStdoutStderr(runCmd)
|
|
if err != nil && exitCode != 0 {
|
|
t.Fatal("2", out, stderr, err)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - regression test for #4979 - volumes-from on exited container")
|
|
}
|
|
|
|
// Regression test for #4830
|
|
func TestRunWithRelativePath(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "-v", "tmp:/other-tmp", "busybox", "true")
|
|
if _, _, _, err := runCommandWithStdoutStderr(runCmd); err == nil {
|
|
t.Fatalf("relative path should result in an error")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - volume with relative path")
|
|
}
|
|
|
|
func TestRunVolumesMountedAsReadonly(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-v", "/test:/test:ro", "busybox", "touch", "/test/somefile")
|
|
if code, err := runCommand(cmd); err == nil || code == 0 {
|
|
t.Fatalf("run should fail because volume is ro: exit code %d", code)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - volumes as readonly mount")
|
|
}
|
|
|
|
func TestRunVolumesFromInReadonlyMode(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test", "busybox", "true")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent:ro", "busybox", "touch", "/test/file")
|
|
if code, err := runCommand(cmd); err == nil || code == 0 {
|
|
t.Fatalf("run should fail because volume is ro: exit code %d", code)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - volumes from as readonly mount")
|
|
}
|
|
|
|
// Regression test for #1201
|
|
func TestRunVolumesFromInReadWriteMode(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test", "busybox", "true")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent", "busybox", "touch", "/test/file")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - volumes from as read write mount")
|
|
}
|
|
|
|
func TestVolumesFromGetsProperMode(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test:/test:ro", "busybox", "true")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Expect this "rw" mode to be be ignored since the inheritted volume is "ro"
|
|
cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent:rw", "busybox", "touch", "/test/file")
|
|
if _, err := runCommand(cmd); err == nil {
|
|
t.Fatal("Expected volumes-from to inherit read-only volume even when passing in `rw`")
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "run", "--name", "parent2", "-v", "/test:/test:ro", "busybox", "true")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Expect this to be read-only since both are "ro"
|
|
cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent2:ro", "busybox", "touch", "/test/file")
|
|
if _, err := runCommand(cmd); err == nil {
|
|
t.Fatal("Expected volumes-from to inherit read-only volume even when passing in `ro`")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - volumes from ignores `rw` if inherrited volume is `ro`")
|
|
}
|
|
|
|
// Test for #1351
|
|
func TestRunApplyVolumesFromBeforeVolumes(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test", "busybox", "touch", "/test/foo")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent", "-v", "/test", "busybox", "cat", "/test/foo")
|
|
if out, _, err := runCommandWithOutput(cmd); err != nil {
|
|
t.Fatal(out, err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - volumes from mounted first")
|
|
}
|
|
|
|
func TestRunMultipleVolumesFrom(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--name", "parent1", "-v", "/test", "busybox", "touch", "/test/foo")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "run", "--name", "parent2", "-v", "/other", "busybox", "touch", "/other/bar")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent1", "--volumes-from", "parent2",
|
|
"busybox", "sh", "-c", "cat /test/foo && cat /other/bar")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - multiple volumes from")
|
|
}
|
|
|
|
// this tests verifies the ID format for the container
|
|
func TestRunVerifyContainerID(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-d", "busybox", "true")
|
|
out, exit, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if exit != 0 {
|
|
t.Fatalf("expected exit code 0 received %d", exit)
|
|
}
|
|
match, err := regexp.MatchString("^[0-9a-f]{64}$", strings.TrimSuffix(out, "\n"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !match {
|
|
t.Fatalf("Invalid container ID: %s", out)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - verify container ID")
|
|
}
|
|
|
|
// Test that creating a container with a volume doesn't crash. Regression test for #995.
|
|
func TestRunCreateVolume(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-v", "/var/lib/data", "busybox", "true")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - create docker managed volume")
|
|
}
|
|
|
|
// Test that creating a volume with a symlink in its path works correctly. Test for #5152.
|
|
// Note that this bug happens only with symlinks with a target that starts with '/'.
|
|
func TestRunCreateVolumeWithSymlink(t *testing.T) {
|
|
buildCmd := exec.Command(dockerBinary, "build", "-t", "docker-test-createvolumewithsymlink", "-")
|
|
buildCmd.Stdin = strings.NewReader(`FROM busybox
|
|
RUN mkdir /foo && ln -s /foo /bar`)
|
|
buildCmd.Dir = workingDirectory
|
|
err := buildCmd.Run()
|
|
if err != nil {
|
|
t.Fatalf("could not build 'docker-test-createvolumewithsymlink': %v", err)
|
|
}
|
|
|
|
cmd := exec.Command(dockerBinary, "run", "-v", "/bar/foo", "--name", "test-createvolumewithsymlink", "docker-test-createvolumewithsymlink", "sh", "-c", "mount | grep -q /foo/foo")
|
|
exitCode, err := runCommand(cmd)
|
|
if err != nil || exitCode != 0 {
|
|
t.Fatalf("[run] err: %v, exitcode: %d", err, exitCode)
|
|
}
|
|
|
|
var volPath string
|
|
cmd = exec.Command(dockerBinary, "inspect", "-f", "{{range .Volumes}}{{.}}{{end}}", "test-createvolumewithsymlink")
|
|
volPath, exitCode, err = runCommandWithOutput(cmd)
|
|
if err != nil || exitCode != 0 {
|
|
t.Fatalf("[inspect] err: %v, exitcode: %d", err, exitCode)
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "rm", "-v", "test-createvolumewithsymlink")
|
|
exitCode, err = runCommand(cmd)
|
|
if err != nil || exitCode != 0 {
|
|
t.Fatalf("[rm] err: %v, exitcode: %d", err, exitCode)
|
|
}
|
|
|
|
f, err := os.Open(volPath)
|
|
defer f.Close()
|
|
if !os.IsNotExist(err) {
|
|
t.Fatalf("[open] (expecting 'file does not exist' error) err: %v, volPath: %s", err, volPath)
|
|
}
|
|
|
|
deleteImages("docker-test-createvolumewithsymlink")
|
|
deleteAllContainers()
|
|
|
|
logDone("run - create volume with symlink")
|
|
}
|
|
|
|
// Tests that a volume path that has a symlink exists in a container mounting it with `--volumes-from`.
|
|
func TestRunVolumesFromSymlinkPath(t *testing.T) {
|
|
buildCmd := exec.Command(dockerBinary, "build", "-t", "docker-test-volumesfromsymlinkpath", "-")
|
|
buildCmd.Stdin = strings.NewReader(`FROM busybox
|
|
RUN mkdir /baz && ln -s /baz /foo
|
|
VOLUME ["/foo/bar"]`)
|
|
buildCmd.Dir = workingDirectory
|
|
err := buildCmd.Run()
|
|
if err != nil {
|
|
t.Fatalf("could not build 'docker-test-volumesfromsymlinkpath': %v", err)
|
|
}
|
|
|
|
cmd := exec.Command(dockerBinary, "run", "--name", "test-volumesfromsymlinkpath", "docker-test-volumesfromsymlinkpath")
|
|
exitCode, err := runCommand(cmd)
|
|
if err != nil || exitCode != 0 {
|
|
t.Fatalf("[run] (volume) err: %v, exitcode: %d", err, exitCode)
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "run", "--volumes-from", "test-volumesfromsymlinkpath", "busybox", "sh", "-c", "ls /foo | grep -q bar")
|
|
exitCode, err = runCommand(cmd)
|
|
if err != nil || exitCode != 0 {
|
|
t.Fatalf("[run] err: %v, exitcode: %d", err, exitCode)
|
|
}
|
|
|
|
deleteImages("docker-test-volumesfromsymlinkpath")
|
|
deleteAllContainers()
|
|
|
|
logDone("run - volumes-from symlink path")
|
|
}
|
|
|
|
func TestRunExitCode(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "/bin/sh", "-c", "exit 72")
|
|
|
|
exit, err := runCommand(cmd)
|
|
if err == nil {
|
|
t.Fatal("should not have a non nil error")
|
|
}
|
|
if exit != 72 {
|
|
t.Fatalf("expected exit code 72 received %d", exit)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - correct exit code")
|
|
}
|
|
|
|
func TestRunUserDefaultsToRoot(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "id")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if !strings.Contains(out, "uid=0(root) gid=0(root)") {
|
|
t.Fatalf("expected root user got %s", out)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - default user")
|
|
}
|
|
|
|
func TestRunUserByName(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-u", "root", "busybox", "id")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if !strings.Contains(out, "uid=0(root) gid=0(root)") {
|
|
t.Fatalf("expected root user got %s", out)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - user by name")
|
|
}
|
|
|
|
func TestRunUserByID(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-u", "1", "busybox", "id")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if !strings.Contains(out, "uid=1(daemon) gid=1(daemon)") {
|
|
t.Fatalf("expected daemon user got %s", out)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - user by id")
|
|
}
|
|
|
|
func TestRunUserByIDBig(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-u", "2147483648", "busybox", "id")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal("No error, but must be.", out)
|
|
}
|
|
if !strings.Contains(out, "Uids and gids must be in range") {
|
|
t.Fatalf("expected error about uids range, got %s", out)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - user by id, id too big")
|
|
}
|
|
|
|
func TestRunUserByIDNegative(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-u", "-1", "busybox", "id")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal("No error, but must be.", out)
|
|
}
|
|
if !strings.Contains(out, "Uids and gids must be in range") {
|
|
t.Fatalf("expected error about uids range, got %s", out)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - user by id, id negative")
|
|
}
|
|
|
|
func TestRunUserByIDZero(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-u", "0", "busybox", "id")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if !strings.Contains(out, "uid=0(root) gid=0(root) groups=10(wheel)") {
|
|
t.Fatalf("expected daemon user got %s", out)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - user by id, zero uid")
|
|
}
|
|
|
|
func TestRunUserNotFound(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-u", "notme", "busybox", "id")
|
|
|
|
_, err := runCommand(cmd)
|
|
if err == nil {
|
|
t.Fatal("unknown user should cause container to fail")
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - user not found")
|
|
}
|
|
|
|
func TestRunTwoConcurrentContainers(t *testing.T) {
|
|
group := sync.WaitGroup{}
|
|
group.Add(2)
|
|
|
|
for i := 0; i < 2; i++ {
|
|
go func() {
|
|
defer group.Done()
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "sleep", "2")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
group.Wait()
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - two concurrent containers")
|
|
}
|
|
|
|
func TestRunEnvironment(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-h", "testing", "-e=FALSE=true", "-e=TRUE", "-e=TRICKY", "-e=HOME=", "busybox", "env")
|
|
cmd.Env = append(os.Environ(),
|
|
"TRUE=false",
|
|
"TRICKY=tri\ncky\n",
|
|
)
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
actualEnv := strings.Split(out, "\n")
|
|
if actualEnv[len(actualEnv)-1] == "" {
|
|
actualEnv = actualEnv[:len(actualEnv)-1]
|
|
}
|
|
sort.Strings(actualEnv)
|
|
|
|
goodEnv := []string{
|
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
"HOSTNAME=testing",
|
|
"FALSE=true",
|
|
"TRUE=false",
|
|
"TRICKY=tri",
|
|
"cky",
|
|
"",
|
|
"HOME=/root",
|
|
}
|
|
sort.Strings(goodEnv)
|
|
if len(goodEnv) != len(actualEnv) {
|
|
t.Fatalf("Wrong environment: should be %d variables, not: '%s'\n", len(goodEnv), strings.Join(actualEnv, ", "))
|
|
}
|
|
for i := range goodEnv {
|
|
if actualEnv[i] != goodEnv[i] {
|
|
t.Fatalf("Wrong environment variable: should be %s, not %s", goodEnv[i], actualEnv[i])
|
|
}
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - verify environment")
|
|
}
|
|
|
|
func TestRunContainerNetwork(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "ping", "-c", "1", "127.0.0.1")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test container network via ping")
|
|
}
|
|
|
|
// Issue #4681
|
|
func TestRunLoopbackWhenNetworkDisabled(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--net=none", "busybox", "ping", "-c", "1", "127.0.0.1")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test container loopback when networking disabled")
|
|
}
|
|
|
|
func TestRunNetHostNotAllowedWithLinks(t *testing.T) {
|
|
_, _, err := cmd(t, "run", "--name", "linked", "busybox", "true")
|
|
|
|
cmd := exec.Command(dockerBinary, "run", "--net=host", "--link", "linked:linked", "busybox", "true")
|
|
_, _, err = runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal("Expected error")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - don't allow --net=host to be used with links")
|
|
}
|
|
|
|
func TestRunLoopbackOnlyExistsWhenNetworkingDisabled(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--net=none", "busybox", "ip", "-o", "-4", "a", "show", "up")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
var (
|
|
count = 0
|
|
parts = strings.Split(out, "\n")
|
|
)
|
|
|
|
for _, l := range parts {
|
|
if l != "" {
|
|
count++
|
|
}
|
|
}
|
|
|
|
if count != 1 {
|
|
t.Fatalf("Wrong interface count in container %d", count)
|
|
}
|
|
|
|
if !strings.HasPrefix(out, "1: lo") {
|
|
t.Fatalf("Wrong interface in test container: expected [1: lo], got %s", out)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test loopback only exists when networking disabled")
|
|
}
|
|
|
|
// #7851 hostname outside container shows FQDN, inside only shortname
|
|
// For testing purposes it is not required to set host's hostname directly
|
|
// and use "--net=host" (as the original issue submitter did), as the same
|
|
// codepath is executed with "docker run -h <hostname>". Both were manually
|
|
// tested, but this testcase takes the simpler path of using "run -h .."
|
|
func TestRunFullHostnameSet(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-h", "foo.bar.baz", "busybox", "hostname")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual != "foo.bar.baz" {
|
|
t.Fatalf("expected hostname 'foo.bar.baz', received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test fully qualified hostname set with -h")
|
|
}
|
|
|
|
func TestRunPrivilegedCanMknod(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--privileged", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual != "ok" {
|
|
t.Fatalf("expected output ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test privileged can mknod")
|
|
}
|
|
|
|
func TestRunUnPrivilegedCanMknod(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual != "ok" {
|
|
t.Fatalf("expected output ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test un-privileged can mknod")
|
|
}
|
|
|
|
func TestRunCapDropInvalid(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--cap-drop=CHPASS", "busybox", "ls")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
logDone("run - test --cap-drop=CHPASS invalid")
|
|
}
|
|
|
|
func TestRunCapDropCannotMknod(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--cap-drop=MKNOD", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual == "ok" {
|
|
t.Fatalf("expected output not ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test --cap-drop=MKNOD cannot mknod")
|
|
}
|
|
|
|
func TestRunCapDropCannotMknodLowerCase(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--cap-drop=mknod", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual == "ok" {
|
|
t.Fatalf("expected output not ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test --cap-drop=mknod cannot mknod lowercase")
|
|
}
|
|
|
|
func TestRunCapDropALLCannotMknod(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--cap-drop=ALL", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual == "ok" {
|
|
t.Fatalf("expected output not ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test --cap-drop=ALL cannot mknod")
|
|
}
|
|
|
|
func TestRunCapDropALLAddMknodCannotMknod(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--cap-drop=ALL", "--cap-add=MKNOD", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual != "ok" {
|
|
t.Fatalf("expected output ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test --cap-drop=ALL --cap-add=MKNOD can mknod")
|
|
}
|
|
|
|
func TestRunCapAddInvalid(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--cap-add=CHPASS", "busybox", "ls")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
logDone("run - test --cap-add=CHPASS invalid")
|
|
}
|
|
|
|
func TestRunCapAddCanDownInterface(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--cap-add=NET_ADMIN", "busybox", "sh", "-c", "ip link set eth0 down && echo ok")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual != "ok" {
|
|
t.Fatalf("expected output ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test --cap-add=NET_ADMIN can set eth0 down")
|
|
}
|
|
|
|
func TestRunCapAddALLCanDownInterface(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--cap-add=ALL", "busybox", "sh", "-c", "ip link set eth0 down && echo ok")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual != "ok" {
|
|
t.Fatalf("expected output ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test --cap-add=ALL can set eth0 down")
|
|
}
|
|
|
|
func TestRunCapAddALLDropNetAdminCanDownInterface(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--cap-add=ALL", "--cap-drop=NET_ADMIN", "busybox", "sh", "-c", "ip link set eth0 down && echo ok")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual == "ok" {
|
|
t.Fatalf("expected output not ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test --cap-add=ALL --cap-drop=NET_ADMIN cannot set eth0 down")
|
|
}
|
|
|
|
func TestRunPrivilegedCanMount(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--privileged", "busybox", "sh", "-c", "mount -t tmpfs none /tmp && echo ok")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual != "ok" {
|
|
t.Fatalf("expected output ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test privileged can mount")
|
|
}
|
|
|
|
func TestRunUnPrivilegedCannotMount(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "sh", "-c", "mount -t tmpfs none /tmp && echo ok")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual == "ok" {
|
|
t.Fatalf("expected output not ok received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test un-privileged cannot mount")
|
|
}
|
|
|
|
func TestRunSysNotWritableInNonPrivilegedContainers(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "touch", "/sys/kernel/profiling")
|
|
if code, err := runCommand(cmd); err == nil || code == 0 {
|
|
t.Fatal("sys should not be writable in a non privileged container")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - sys not writable in non privileged container")
|
|
}
|
|
|
|
func TestRunSysWritableInPrivilegedContainers(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--privileged", "busybox", "touch", "/sys/kernel/profiling")
|
|
if code, err := runCommand(cmd); err != nil || code != 0 {
|
|
t.Fatalf("sys should be writable in privileged container")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - sys writable in privileged container")
|
|
}
|
|
|
|
func TestRunProcNotWritableInNonPrivilegedContainers(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "touch", "/proc/sysrq-trigger")
|
|
if code, err := runCommand(cmd); err == nil || code == 0 {
|
|
t.Fatal("proc should not be writable in a non privileged container")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - proc not writable in non privileged container")
|
|
}
|
|
|
|
func TestRunProcWritableInPrivilegedContainers(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--privileged", "busybox", "touch", "/proc/sysrq-trigger")
|
|
if code, err := runCommand(cmd); err != nil || code != 0 {
|
|
t.Fatalf("proc should be writable in privileged container")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - proc writable in privileged container")
|
|
}
|
|
|
|
func TestRunWithCpuset(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--cpuset", "0", "busybox", "true")
|
|
if code, err := runCommand(cmd); err != nil || code != 0 {
|
|
t.Fatalf("container should run successfuly with cpuset of 0: %s", err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - cpuset 0")
|
|
}
|
|
|
|
func TestRunDeviceNumbers(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "sh", "-c", "ls -l /dev/null")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
deviceLineFields := strings.Fields(out)
|
|
deviceLineFields[6] = ""
|
|
deviceLineFields[7] = ""
|
|
deviceLineFields[8] = ""
|
|
expected := []string{"crw-rw-rw-", "1", "root", "root", "1,", "3", "", "", "", "/dev/null"}
|
|
|
|
if !(reflect.DeepEqual(deviceLineFields, expected)) {
|
|
t.Fatalf("expected output\ncrw-rw-rw- 1 root root 1, 3 May 24 13:29 /dev/null\n received\n %s\n", out)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test device numbers")
|
|
}
|
|
|
|
func TestRunThatCharacterDevicesActLikeCharacterDevices(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "sh", "-c", "dd if=/dev/zero of=/zero bs=1k count=5 2> /dev/null ; du -h /zero")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual[0] == '0' {
|
|
t.Fatalf("expected a new file called /zero to be create that is greater than 0 bytes long, but du says: %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test that character devices work.")
|
|
}
|
|
|
|
func TestRunUnprivilegedWithChroot(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "chroot", "/", "true")
|
|
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - unprivileged with chroot")
|
|
}
|
|
|
|
func TestRunAddingOptionalDevices(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--device", "/dev/zero:/dev/nulo", "busybox", "sh", "-c", "ls /dev/nulo")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual != "/dev/nulo" {
|
|
t.Fatalf("expected output /dev/nulo, received %s", actual)
|
|
}
|
|
deleteAllContainers()
|
|
|
|
logDone("run - test --device argument")
|
|
}
|
|
|
|
func TestRunModeHostname(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-h=testhostname", "busybox", "cat", "/etc/hostname")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actual := strings.Trim(out, "\r\n"); actual != "testhostname" {
|
|
t.Fatalf("expected 'testhostname', but says: '%s'", actual)
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "run", "--net=host", "busybox", "cat", "/etc/hostname")
|
|
|
|
out, _, err = runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
hostname, err := os.Hostname()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if actual := strings.Trim(out, "\r\n"); actual != hostname {
|
|
t.Fatalf("expected '%s', but says: '%s'", hostname, actual)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - hostname and several network modes")
|
|
}
|
|
|
|
func TestRunRootWorkdir(t *testing.T) {
|
|
s, _, err := cmd(t, "run", "--workdir", "/", "busybox", "pwd")
|
|
if err != nil {
|
|
t.Fatal(s, err)
|
|
}
|
|
if s != "/\n" {
|
|
t.Fatalf("pwd returned '%s' (expected /\\n)", s)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - workdir /")
|
|
}
|
|
|
|
func TestRunAllowBindMountingRoot(t *testing.T) {
|
|
s, _, err := cmd(t, "run", "-v", "/:/host", "busybox", "ls", "/host")
|
|
if err != nil {
|
|
t.Fatal(s, err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - bind mount / as volume")
|
|
}
|
|
|
|
func TestRunDisallowBindMountingRootToRoot(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-v", "/:/", "busybox", "ls", "/host")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal(out, err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - bind mount /:/ as volume should fail")
|
|
}
|
|
|
|
// Test recursive bind mount works by default
|
|
func TestRunWithVolumesIsRecursive(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir("", "docker_recursive_mount_test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
// Create a temporary tmpfs mount.
|
|
tmpfsDir := filepath.Join(tmpDir, "tmpfs")
|
|
if err := os.MkdirAll(tmpfsDir, 0777); err != nil {
|
|
t.Fatalf("failed to mkdir at %s - %s", tmpfsDir, err)
|
|
}
|
|
if err := mount.Mount("tmpfs", tmpfsDir, "tmpfs", ""); err != nil {
|
|
t.Fatalf("failed to create a tmpfs mount at %s - %s", tmpfsDir, err)
|
|
}
|
|
|
|
f, err := ioutil.TempFile(tmpfsDir, "touch-me")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer f.Close()
|
|
|
|
runCmd := exec.Command(dockerBinary, "run", "--name", "test-data", "--volume", fmt.Sprintf("%s:/tmp:ro", tmpDir), "busybox:latest", "ls", "/tmp/tmpfs")
|
|
out, stderr, exitCode, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil && exitCode != 0 {
|
|
t.Fatal(out, stderr, err)
|
|
}
|
|
if !strings.Contains(out, filepath.Base(f.Name())) {
|
|
t.Fatal("Recursive bind mount test failed. Expected file not found")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - volumes are bind mounted recursively")
|
|
}
|
|
|
|
func TestRunDnsDefaultOptions(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "busybox", "cat", "/etc/resolv.conf")
|
|
|
|
actual, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, actual)
|
|
}
|
|
|
|
resolvConf, err := ioutil.ReadFile("/etc/resolv.conf")
|
|
if os.IsNotExist(err) {
|
|
t.Fatalf("/etc/resolv.conf does not exist")
|
|
}
|
|
|
|
if actual != string(resolvConf) {
|
|
t.Fatalf("expected resolv.conf is not the same of actual")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - dns default options")
|
|
}
|
|
|
|
func TestRunDnsOptions(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "--dns=127.0.0.1", "--dns-search=mydomain", "busybox", "cat", "/etc/resolv.conf")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
actual := strings.Replace(strings.Trim(out, "\r\n"), "\n", " ", -1)
|
|
if actual != "nameserver 127.0.0.1 search mydomain" {
|
|
t.Fatalf("expected 'nameserver 127.0.0.1 search mydomain', but says: '%s'", actual)
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "run", "--dns=127.0.0.1", "--dns-search=.", "busybox", "cat", "/etc/resolv.conf")
|
|
|
|
out, _, err = runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
actual = strings.Replace(strings.Trim(strings.Trim(out, "\r\n"), " "), "\n", " ", -1)
|
|
if actual != "nameserver 127.0.0.1" {
|
|
t.Fatalf("expected 'nameserver 127.0.0.1', but says: '%s'", actual)
|
|
}
|
|
|
|
logDone("run - dns options")
|
|
}
|
|
|
|
func TestRunDnsOptionsBasedOnHostResolvConf(t *testing.T) {
|
|
resolvConf, err := ioutil.ReadFile("/etc/resolv.conf")
|
|
if os.IsNotExist(err) {
|
|
t.Fatalf("/etc/resolv.conf does not exist")
|
|
}
|
|
|
|
hostNamservers := resolvconf.GetNameservers(resolvConf)
|
|
hostSearch := resolvconf.GetSearchDomains(resolvConf)
|
|
|
|
cmd := exec.Command(dockerBinary, "run", "--dns=127.0.0.1", "busybox", "cat", "/etc/resolv.conf")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
if actualNameservers := resolvconf.GetNameservers([]byte(out)); string(actualNameservers[0]) != "127.0.0.1" {
|
|
t.Fatalf("expected '127.0.0.1', but says: '%s'", string(actualNameservers[0]))
|
|
}
|
|
|
|
actualSearch := resolvconf.GetSearchDomains([]byte(out))
|
|
if len(actualSearch) != len(hostSearch) {
|
|
t.Fatalf("expected '%s' search domain(s), but it has: '%s'", len(hostSearch), len(actualSearch))
|
|
}
|
|
for i := range actualSearch {
|
|
if actualSearch[i] != hostSearch[i] {
|
|
t.Fatalf("expected '%s' domain, but says: '%s'", actualSearch[i], hostSearch[i])
|
|
}
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "run", "--dns-search=mydomain", "busybox", "cat", "/etc/resolv.conf")
|
|
|
|
out, _, err = runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
actualNameservers := resolvconf.GetNameservers([]byte(out))
|
|
if len(actualNameservers) != len(hostNamservers) {
|
|
t.Fatalf("expected '%s' nameserver(s), but it has: '%s'", len(hostNamservers), len(actualNameservers))
|
|
}
|
|
for i := range actualNameservers {
|
|
if actualNameservers[i] != hostNamservers[i] {
|
|
t.Fatalf("expected '%s' nameserver, but says: '%s'", actualNameservers[i], hostNamservers[i])
|
|
}
|
|
}
|
|
|
|
if actualSearch = resolvconf.GetSearchDomains([]byte(out)); string(actualSearch[0]) != "mydomain" {
|
|
t.Fatalf("expected 'mydomain', but says: '%s'", string(actualSearch[0]))
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - dns options based on host resolv.conf")
|
|
}
|
|
|
|
func TestRunAddHost(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
cmd := exec.Command(dockerBinary, "run", "--add-host=extra:86.75.30.9", "busybox", "grep", "extra", "/etc/hosts")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
actual := strings.Trim(out, "\r\n")
|
|
if actual != "86.75.30.9\textra" {
|
|
t.Fatalf("expected '86.75.30.9\textra', but says: '%s'", actual)
|
|
}
|
|
|
|
logDone("run - add-host option")
|
|
}
|
|
|
|
// Regression test for #6983
|
|
func TestRunAttachStdErrOnlyTTYMode(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-t", "-a", "stderr", "busybox", "true")
|
|
|
|
exitCode, err := runCommand(cmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if exitCode != 0 {
|
|
t.Fatalf("Container should have exited with error code 0")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - Attach stderr only with -t")
|
|
}
|
|
|
|
// Regression test for #6983
|
|
func TestRunAttachStdOutOnlyTTYMode(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-t", "-a", "stdout", "busybox", "true")
|
|
|
|
exitCode, err := runCommand(cmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if exitCode != 0 {
|
|
t.Fatalf("Container should have exited with error code 0")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - Attach stdout only with -t")
|
|
}
|
|
|
|
// Regression test for #6983
|
|
func TestRunAttachStdOutAndErrTTYMode(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-t", "-a", "stdout", "-a", "stderr", "busybox", "true")
|
|
|
|
exitCode, err := runCommand(cmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if exitCode != 0 {
|
|
t.Fatalf("Container should have exited with error code 0")
|
|
}
|
|
|
|
deleteAllContainers()
|
|
|
|
logDone("run - Attach stderr and stdout with -t")
|
|
}
|
|
|
|
func TestRunState(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
cmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top")
|
|
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
id := strings.TrimSpace(out)
|
|
state, err := inspectField(id, "State.Running")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if state != "true" {
|
|
t.Fatal("Container state is 'not running'")
|
|
}
|
|
pid1, err := inspectField(id, "State.Pid")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if pid1 == "0" {
|
|
t.Fatal("Container state Pid 0")
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "stop", id)
|
|
out, _, err = runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
state, err = inspectField(id, "State.Running")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if state != "false" {
|
|
t.Fatal("Container state is 'running'")
|
|
}
|
|
pid2, err := inspectField(id, "State.Pid")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if pid2 == pid1 {
|
|
t.Fatalf("Container state Pid %s, but expected %s", pid2, pid1)
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "start", id)
|
|
out, _, err = runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
state, err = inspectField(id, "State.Running")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if state != "true" {
|
|
t.Fatal("Container state is 'not running'")
|
|
}
|
|
pid3, err := inspectField(id, "State.Pid")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if pid3 == pid1 {
|
|
t.Fatalf("Container state Pid %s, but expected %s", pid2, pid1)
|
|
}
|
|
logDone("run - test container state.")
|
|
}
|
|
|
|
// Test for #1737
|
|
func TestRunCopyVolumeUidGid(t *testing.T) {
|
|
name := "testrunvolumesuidgid"
|
|
defer deleteImages(name)
|
|
defer deleteAllContainers()
|
|
_, err := buildImage(name,
|
|
`FROM busybox
|
|
RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
|
|
RUN echo 'dockerio:x:1001:' >> /etc/group
|
|
RUN mkdir -p /hello && touch /hello/test && chown dockerio.dockerio /hello`,
|
|
true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Test that the uid and gid is copied from the image to the volume
|
|
cmd := exec.Command(dockerBinary, "run", "--rm", "-v", "/hello", name, "sh", "-c", "ls -l / | grep hello | awk '{print $3\":\"$4}'")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
out = strings.TrimSpace(out)
|
|
if out != "dockerio:dockerio" {
|
|
t.Fatalf("Wrong /hello ownership: %s, expected dockerio:dockerio", out)
|
|
}
|
|
|
|
logDone("run - copy uid/gid for volume")
|
|
}
|
|
|
|
// Test for #1582
|
|
func TestRunCopyVolumeContent(t *testing.T) {
|
|
name := "testruncopyvolumecontent"
|
|
defer deleteImages(name)
|
|
defer deleteAllContainers()
|
|
_, err := buildImage(name,
|
|
`FROM busybox
|
|
RUN mkdir -p /hello/local && echo hello > /hello/local/world`,
|
|
true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Test that the content is copied from the image to the volume
|
|
cmd := exec.Command(dockerBinary, "run", "--rm", "-v", "/hello", name, "sh", "-c", "find", "/hello")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if !(strings.Contains(out, "/hello/local/world") && strings.Contains(out, "/hello/local")) {
|
|
t.Fatal("Container failed to transfer content to volume")
|
|
}
|
|
logDone("run - copy volume content")
|
|
}
|
|
|
|
func TestRunCleanupCmdOnEntrypoint(t *testing.T) {
|
|
name := "testrunmdcleanuponentrypoint"
|
|
defer deleteImages(name)
|
|
defer deleteAllContainers()
|
|
if _, err := buildImage(name,
|
|
`FROM busybox
|
|
ENTRYPOINT ["echo"]
|
|
CMD ["testingpoint"]`,
|
|
true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
runCmd := exec.Command(dockerBinary, "run", "--entrypoint", "whoami", name)
|
|
out, exit, err := runCommandWithOutput(runCmd)
|
|
if err != nil {
|
|
t.Fatalf("Error: %v, out: %q", err, out)
|
|
}
|
|
if exit != 0 {
|
|
t.Fatalf("expected exit code 0 received %d, out: %q", exit, out)
|
|
}
|
|
out = strings.TrimSpace(out)
|
|
if out != "root" {
|
|
t.Fatalf("Expected output root, got %q", out)
|
|
}
|
|
logDone("run - cleanup cmd on --entrypoint")
|
|
}
|
|
|
|
// TestRunWorkdirExistsAndIsFile checks that if 'docker run -w' with existing file can be detected
|
|
func TestRunWorkdirExistsAndIsFile(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
runCmd := exec.Command(dockerBinary, "run", "-w", "/bin/cat", "busybox")
|
|
out, exit, err := runCommandWithOutput(runCmd)
|
|
if !(err != nil && exit == 1 && strings.Contains(out, "Cannot mkdir: /bin/cat is not a directory")) {
|
|
t.Fatalf("Docker must complains about making dir, but we got out: %s, exit: %d, err: %s", out, exit, err)
|
|
}
|
|
logDone("run - error on existing file for workdir")
|
|
}
|
|
|
|
func TestRunExitOnStdinClose(t *testing.T) {
|
|
name := "testrunexitonstdinclose"
|
|
defer deleteAllContainers()
|
|
runCmd := exec.Command(dockerBinary, "run", "--name", name, "-i", "busybox", "/bin/cat")
|
|
|
|
stdin, err := runCmd.StdinPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
stdout, err := runCmd.StdoutPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := runCmd.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := stdin.Write([]byte("hello\n")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
r := bufio.NewReader(stdout)
|
|
line, err := r.ReadString('\n')
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
line = strings.TrimSpace(line)
|
|
if line != "hello" {
|
|
t.Fatalf("Output should be 'hello', got '%q'", line)
|
|
}
|
|
if err := stdin.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
finish := make(chan struct{})
|
|
go func() {
|
|
if err := runCmd.Wait(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
close(finish)
|
|
}()
|
|
select {
|
|
case <-finish:
|
|
case <-time.After(1 * time.Second):
|
|
t.Fatal("docker run failed to exit on stdin close")
|
|
}
|
|
state, err := inspectField(name, "State.Running")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if state != "false" {
|
|
t.Fatal("Container must be stopped after stdin closing")
|
|
}
|
|
logDone("run - exit on stdin closing")
|
|
}
|
|
|
|
// Test for #2267
|
|
func TestRunWriteHostsFileAndNotCommit(t *testing.T) {
|
|
name := "writehosts"
|
|
cmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sh", "-c", "echo test2267 >> /etc/hosts && cat /etc/hosts")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if !strings.Contains(out, "test2267") {
|
|
t.Fatal("/etc/hosts should contain 'test2267'")
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "diff", name)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
out, _, err = runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if len(strings.Trim(out, "\r\n")) != 0 {
|
|
t.Fatal("diff should be empty")
|
|
}
|
|
|
|
logDone("run - write to /etc/hosts and not commited")
|
|
}
|
|
|
|
// Test for #2267
|
|
func TestRunWriteHostnameFileAndNotCommit(t *testing.T) {
|
|
name := "writehostname"
|
|
cmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sh", "-c", "echo test2267 >> /etc/hostname && cat /etc/hostname")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if !strings.Contains(out, "test2267") {
|
|
t.Fatal("/etc/hostname should contain 'test2267'")
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "diff", name)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
out, _, err = runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if len(strings.Trim(out, "\r\n")) != 0 {
|
|
t.Fatal("diff should be empty")
|
|
}
|
|
|
|
logDone("run - write to /etc/hostname and not commited")
|
|
}
|
|
|
|
// Test for #2267
|
|
func TestRunWriteResolvFileAndNotCommit(t *testing.T) {
|
|
name := "writeresolv"
|
|
cmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sh", "-c", "echo test2267 >> /etc/resolv.conf && cat /etc/resolv.conf")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if !strings.Contains(out, "test2267") {
|
|
t.Fatal("/etc/resolv.conf should contain 'test2267'")
|
|
}
|
|
|
|
cmd = exec.Command(dockerBinary, "diff", name)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
out, _, err = runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if len(strings.Trim(out, "\r\n")) != 0 {
|
|
t.Fatal("diff should be empty")
|
|
}
|
|
|
|
logDone("run - write to /etc/resolv.conf and not commited")
|
|
}
|
|
|
|
func TestRunWithBadDevice(t *testing.T) {
|
|
name := "baddevice"
|
|
cmd := exec.Command(dockerBinary, "run", "--name", name, "--device", "/etc", "busybox", "true")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatal("Run should fail with bad device")
|
|
}
|
|
expected := `"/etc": not a device node`
|
|
if !strings.Contains(out, expected) {
|
|
t.Fatalf("Output should contain %q, actual out: %q", expected, out)
|
|
}
|
|
logDone("run - error with bad device")
|
|
}
|
|
|
|
func TestRunEntrypoint(t *testing.T) {
|
|
name := "entrypoint"
|
|
cmd := exec.Command(dockerBinary, "run", "--name", name, "--entrypoint", "/bin/echo", "busybox", "-n", "foobar")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
expected := "foobar"
|
|
if out != expected {
|
|
t.Fatalf("Output should be %q, actual out: %q", expected, out)
|
|
}
|
|
logDone("run - entrypoint")
|
|
}
|
|
|
|
func TestRunBindMounts(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir("", "docker-test-container")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer os.RemoveAll(tmpDir)
|
|
writeFile(path.Join(tmpDir, "touch-me"), "", t)
|
|
|
|
// Test reading from a read-only bind mount
|
|
cmd := exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp:ro", tmpDir), "busybox", "ls", "/tmp")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if !strings.Contains(out, "touch-me") {
|
|
t.Fatal("Container failed to read from bind mount")
|
|
}
|
|
|
|
// test writing to bind mount
|
|
cmd = exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp:rw", tmpDir), "busybox", "touch", "/tmp/holla")
|
|
out, _, err = runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
readFile(path.Join(tmpDir, "holla"), t) // Will fail if the file doesn't exist
|
|
|
|
// test mounting to an illegal destination directory
|
|
cmd = exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:.", tmpDir), "busybox", "ls", ".")
|
|
_, err = runCommand(cmd)
|
|
if err == nil {
|
|
t.Fatal("Container bind mounted illegal directory")
|
|
}
|
|
|
|
// test mount a file
|
|
cmd = exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s/holla:/tmp/holla:rw", tmpDir), "busybox", "sh", "-c", "echo -n 'yotta' > /tmp/holla")
|
|
_, err = runCommand(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
content := readFile(path.Join(tmpDir, "holla"), t) // Will fail if the file doesn't exist
|
|
expected := "yotta"
|
|
if content != expected {
|
|
t.Fatalf("Output should be %q, actual out: %q", expected, content)
|
|
}
|
|
|
|
logDone("run - bind mounts")
|
|
}
|
|
|
|
func TestRunMutableNetworkFiles(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
|
|
for _, fn := range []string{"resolv.conf", "hosts"} {
|
|
deleteAllContainers()
|
|
|
|
out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c1", "busybox", "sh", "-c", fmt.Sprintf("echo success >/etc/%s; while true; do sleep 1; done", fn)))
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
contID := strings.TrimSpace(out)
|
|
|
|
f, err := os.Open(filepath.Join("/var/lib/docker/containers", contID, fn))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
content, err := ioutil.ReadAll(f)
|
|
f.Close()
|
|
|
|
if strings.TrimSpace(string(content)) != "success" {
|
|
t.Fatal("Content was not what was modified in the container", string(content))
|
|
}
|
|
|
|
out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c2", "busybox", "sh", "-c", fmt.Sprintf("while true; do cat /etc/%s; sleep 1; done", fn)))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
contID = strings.TrimSpace(out)
|
|
|
|
resolvConfPath := filepath.Join("/var/lib/docker/containers", contID, fn)
|
|
|
|
f, err = os.OpenFile(resolvConfPath, os.O_WRONLY|os.O_SYNC|os.O_APPEND, 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err := f.Seek(0, 0); err != nil {
|
|
f.Close()
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := f.Truncate(0); err != nil {
|
|
f.Close()
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err := f.Write([]byte("success2\n")); err != nil {
|
|
f.Close()
|
|
t.Fatal(err)
|
|
}
|
|
|
|
f.Close()
|
|
|
|
time.Sleep(2 * time.Second) // don't race sleep
|
|
|
|
out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "logs", "c2"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
lines := strings.Split(out, "\n")
|
|
if strings.TrimSpace(lines[len(lines)-2]) != "success2" {
|
|
t.Fatalf("Did not find the correct output in /etc/%s: %s %#v", fn, out, lines)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRunStableIPAndPort(t *testing.T) {
|
|
const nContainers = 2
|
|
var ids, ips, ports [nContainers]string
|
|
|
|
// Setup: Create a couple of containers and collect their IPs and public ports.
|
|
for i := 0; i < nContainers; i++ {
|
|
runCmd := exec.Command(dockerBinary, "run", "-d", "-p", "1234", "busybox", "top")
|
|
out, _, err := runCommandWithOutput(runCmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ids[i] = strings.TrimSpace(out)
|
|
ips[i], err = inspectField(ids[i], "NetworkSettings.IPAddress")
|
|
errorOut(err, t, out)
|
|
if ips[i] == "" {
|
|
t.Fatal("IP allocation failed")
|
|
}
|
|
|
|
portCmd := exec.Command(dockerBinary, "port", ids[i], "1234")
|
|
ports[i], _, err = runCommandWithOutput(portCmd)
|
|
errorOut(err, t, out)
|
|
if ports[i] == "" {
|
|
t.Fatal("Port allocation failed")
|
|
}
|
|
}
|
|
|
|
// Stop them all.
|
|
for _, id := range ids {
|
|
cmd := exec.Command(dockerBinary, "stop", id)
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
}
|
|
|
|
// Create a new container and ensure it's not getting the IP or port of some stopped container.
|
|
{
|
|
runCmd := exec.Command(dockerBinary, "run", "-d", "-p", "1234", "busybox", "top")
|
|
out, _, err := runCommandWithOutput(runCmd)
|
|
errorOut(err, t, out)
|
|
|
|
id := strings.TrimSpace(out)
|
|
ip, err := inspectField(id, "NetworkSettings.IPAddress")
|
|
errorOut(err, t, out)
|
|
|
|
portCmd := exec.Command(dockerBinary, "port", id, "1234")
|
|
port, _, err := runCommandWithOutput(portCmd)
|
|
errorOut(err, t, out)
|
|
|
|
for i := range ids {
|
|
if ip == ips[i] {
|
|
t.Fatalf("Conflicting IP: %s", ip)
|
|
}
|
|
if port == ports[i] {
|
|
t.Fatalf("Conflicting port: %s", port)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start the containers back, and ensure they are getting the same IPs and ports.
|
|
for i, id := range ids {
|
|
runCmd := exec.Command(dockerBinary, "start", id)
|
|
out, _, err := runCommandWithOutput(runCmd)
|
|
errorOut(err, t, out)
|
|
|
|
ip, err := inspectField(id, "NetworkSettings.IPAddress")
|
|
errorOut(err, t, out)
|
|
portCmd := exec.Command(dockerBinary, "port", ids[i], "1234")
|
|
port, _, err := runCommandWithOutput(portCmd)
|
|
errorOut(err, t, out)
|
|
|
|
if ips[i] != ip {
|
|
t.Fatalf("Container started with a different IP: %s != %s", ip, ips[i])
|
|
}
|
|
if ports[i] != port {
|
|
t.Fatalf("Container started with a different port: %s != %s", port, ports[i])
|
|
}
|
|
}
|
|
|
|
deleteAllContainers()
|
|
logDone("run - ips and ports must not change")
|
|
}
|
|
|
|
// Ensure that CIDFile gets deleted if it's empty
|
|
// Perform this test by making `docker run` fail
|
|
func TestRunCidFileCleanupIfEmpty(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir("", "TestRunCidFile")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
tmpCidFile := path.Join(tmpDir, "cid")
|
|
cmd := exec.Command(dockerBinary, "run", "--cidfile", tmpCidFile, "scratch")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
t.Log(out)
|
|
if err == nil {
|
|
t.Fatal("Run without command must fail")
|
|
}
|
|
|
|
if _, err := os.Stat(tmpCidFile); err == nil {
|
|
t.Fatalf("empty CIDFile '%s' should've been deleted", tmpCidFile)
|
|
}
|
|
deleteAllContainers()
|
|
logDone("run - cleanup empty cidfile on fail")
|
|
}
|
|
|
|
// #2098 - Docker cidFiles only contain short version of the containerId
|
|
//sudo docker run --cidfile /tmp/docker_test.cid ubuntu echo "test"
|
|
// TestRunCidFile tests that run --cidfile returns the longid
|
|
func TestRunCidFileCheckIDLength(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir("", "TestRunCidFile")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tmpCidFile := path.Join(tmpDir, "cid")
|
|
defer os.RemoveAll(tmpDir)
|
|
cmd := exec.Command(dockerBinary, "run", "-d", "--cidfile", tmpCidFile, "busybox", "true")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
id := strings.TrimSpace(out)
|
|
buffer, err := ioutil.ReadFile(tmpCidFile)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cid := string(buffer)
|
|
if len(cid) != 64 {
|
|
t.Fatalf("--cidfile should be a long id, not '%s'", id)
|
|
}
|
|
if cid != id {
|
|
t.Fatalf("cid must be equal to %s, got %s", id, cid)
|
|
}
|
|
deleteAllContainers()
|
|
logDone("run - cidfile contains long id")
|
|
}
|
|
|
|
func TestRunNetworkNotInitializedNoneMode(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-d", "--net=none", "busybox", "top")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
id := strings.TrimSpace(out)
|
|
res, err := inspectField(id, "NetworkSettings.IPAddress")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res != "" {
|
|
t.Fatal("For 'none' mode network must not be initialized, but container got IP: %s", res)
|
|
}
|
|
deleteAllContainers()
|
|
logDone("run - network must not be initialized in 'none' mode")
|
|
}
|
|
|
|
func TestRunDeallocatePortOnMissingIptablesRule(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-d", "-p", "23:23", "busybox", "top")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
id := strings.TrimSpace(out)
|
|
ip, err := inspectField(id, "NetworkSettings.IPAddress")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
iptCmd := exec.Command("iptables", "-D", "FORWARD", "-d", fmt.Sprintf("%s/32", ip),
|
|
"!", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-m", "tcp", "--dport", "23", "-j", "ACCEPT")
|
|
out, _, err = runCommandWithOutput(iptCmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
if err := deleteContainer(id); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cmd = exec.Command(dockerBinary, "run", "-d", "-p", "23:23", "busybox", "top")
|
|
out, _, err = runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
deleteAllContainers()
|
|
logDone("run - port should be deallocated even on iptables error")
|
|
}
|
|
|
|
func TestRunPortInUse(t *testing.T) {
|
|
port := "1234"
|
|
l, err := net.Listen("tcp", ":"+port)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer l.Close()
|
|
cmd := exec.Command(dockerBinary, "run", "-p", port+":80", "busybox", "true")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err == nil {
|
|
t.Fatalf("Binding on used port must fail")
|
|
}
|
|
if !strings.Contains(out, "address already in use") {
|
|
t.Fatalf("Out must be about \"address already in use\", got %s", out)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
logDone("run - fail if port already in use")
|
|
}
|
|
|
|
// Regression test for #7792
|
|
func TestRunMountOrdering(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir("", "docker_nested_mount_test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
tmpDir2, err := ioutil.TempDir("", "docker_nested_mount_test2")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpDir2)
|
|
|
|
// Create a temporary tmpfs mount.
|
|
fooDir := filepath.Join(tmpDir, "foo")
|
|
if err := os.MkdirAll(filepath.Join(tmpDir, "foo"), 0755); err != nil {
|
|
t.Fatalf("failed to mkdir at %s - %s", fooDir, err)
|
|
}
|
|
|
|
if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", fooDir), []byte{}, 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", tmpDir), []byte{}, 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", tmpDir2), []byte{}, 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cmd := exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp", tmpDir), "-v", fmt.Sprintf("%s:/tmp/foo", fooDir), "-v", fmt.Sprintf("%s:/tmp/tmp2", tmpDir2), "-v", fmt.Sprintf("%s:/tmp/tmp2/foo", fooDir), "busybox:latest", "sh", "-c", "ls /tmp/touch-me && ls /tmp/foo/touch-me && ls /tmp/tmp2/touch-me && ls /tmp/tmp2/foo/touch-me")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(out, err)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
logDone("run - volumes are mounted in the correct order")
|
|
}
|
|
|
|
func TestRunExecDir(t *testing.T) {
|
|
cmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top")
|
|
out, _, err := runCommandWithOutput(cmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
id := strings.TrimSpace(out)
|
|
execDir := filepath.Join(execDriverPath, id)
|
|
stateFile := filepath.Join(execDir, "state.json")
|
|
contFile := filepath.Join(execDir, "container.json")
|
|
|
|
{
|
|
fi, err := os.Stat(execDir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !fi.IsDir() {
|
|
t.Fatalf("%q must be a directory", execDir)
|
|
}
|
|
fi, err = os.Stat(stateFile)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fi, err = os.Stat(contFile)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
stopCmd := exec.Command(dockerBinary, "stop", id)
|
|
out, _, err = runCommandWithOutput(stopCmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
{
|
|
fi, err := os.Stat(execDir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !fi.IsDir() {
|
|
t.Fatalf("%q must be a directory", execDir)
|
|
}
|
|
fi, err = os.Stat(stateFile)
|
|
if err == nil {
|
|
t.Fatalf("Statefile %q is exists for stopped container!", stateFile)
|
|
}
|
|
if !os.IsNotExist(err) {
|
|
t.Fatalf("Error should be about non-existing, got %s", err)
|
|
}
|
|
fi, err = os.Stat(contFile)
|
|
if err == nil {
|
|
t.Fatalf("Container file %q is exists for stopped container!", contFile)
|
|
}
|
|
if !os.IsNotExist(err) {
|
|
t.Fatalf("Error should be about non-existing, got %s", err)
|
|
}
|
|
}
|
|
startCmd := exec.Command(dockerBinary, "start", id)
|
|
out, _, err = runCommandWithOutput(startCmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
{
|
|
fi, err := os.Stat(execDir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !fi.IsDir() {
|
|
t.Fatalf("%q must be a directory", execDir)
|
|
}
|
|
fi, err = os.Stat(stateFile)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fi, err = os.Stat(contFile)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
rmCmd := exec.Command(dockerBinary, "rm", "-f", id)
|
|
out, _, err = runCommandWithOutput(rmCmd)
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
{
|
|
_, err := os.Stat(execDir)
|
|
if err == nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err == nil {
|
|
t.Fatalf("Exec directory %q is exists for removed container!", execDir)
|
|
}
|
|
if !os.IsNotExist(err) {
|
|
t.Fatalf("Error should be about non-existing, got %s", err)
|
|
}
|
|
}
|
|
|
|
logDone("run - check execdriver dir behavior")
|
|
}
|
|
|
|
// #6509
|
|
func TestRunRedirectStdout(t *testing.T) {
|
|
|
|
defer deleteAllContainers()
|
|
|
|
checkRedirect := func(command string) {
|
|
_, tty, err := pty.Open()
|
|
if err != nil {
|
|
t.Fatalf("Could not open pty: %v", err)
|
|
}
|
|
cmd := exec.Command("sh", "-c", command)
|
|
cmd.Stdin = tty
|
|
cmd.Stdout = tty
|
|
cmd.Stderr = tty
|
|
ch := make(chan struct{})
|
|
if err := cmd.Start(); err != nil {
|
|
t.Fatalf("start err: %v", err)
|
|
}
|
|
go func() {
|
|
if err := cmd.Wait(); err != nil {
|
|
t.Fatalf("wait err=%v", err)
|
|
}
|
|
close(ch)
|
|
}()
|
|
|
|
select {
|
|
case <-time.After(time.Second):
|
|
t.Fatal("command timeout")
|
|
case <-ch:
|
|
}
|
|
}
|
|
|
|
checkRedirect(dockerBinary + " run -i busybox cat /etc/passwd | grep -q root")
|
|
checkRedirect(dockerBinary + " run busybox cat /etc/passwd | grep -q root")
|
|
|
|
logDone("run - redirect stdout")
|
|
}
|
|
|
|
// Regression test for https://github.com/docker/docker/issues/8259
|
|
func TestRunReuseBindVolumeThatIsSymlink(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir(os.TempDir(), "testlink")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
linkPath := os.TempDir() + "/testlink2"
|
|
if err := os.Symlink(tmpDir, linkPath); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(linkPath)
|
|
|
|
// Create first container
|
|
cmd := exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp/test", linkPath), "busybox", "ls", "-lh", "/tmp/test")
|
|
if _, err := runCommand(cmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create second container with same symlinked path
|
|
// This will fail if the referenced issue is hit with a "Volume exists" error
|
|
cmd = exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp/test", linkPath), "busybox", "ls", "-lh", "/tmp/test")
|
|
if out, _, err := runCommandWithOutput(cmd); err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
deleteAllContainers()
|
|
logDone("run - can remount old bindmount volume")
|
|
}
|