Create a unified RunCommand function with Assert()
Remove some run functions and replace them with the unified run command. Remove DockerCmdWithStdoutStderr Remove many duplicate runCommand functions. Also add dockerCmdWithResult() Allow Result.Assert() to ignore the error message if an exit status is expected. Fix race in DockerSuite.TestDockerInspectMultipleNetwork Fix flaky test DockerSuite.TestRunInteractiveWithRestartPolicy Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
fb42e84772
commit
d7022f2b46
22 changed files with 564 additions and 998 deletions
|
@ -5,13 +5,13 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
|
@ -89,15 +89,12 @@ func (s *DockerSuite) TestApiDockerApiVersion(c *check.C) {
|
|||
defer server.Close()
|
||||
|
||||
// Test using the env var first
|
||||
cmd := exec.Command(dockerBinary, "-H="+server.URL[7:], "version")
|
||||
cmd.Env = appendBaseEnv(false, "DOCKER_API_VERSION=xxx")
|
||||
out, _, _ := runCommandWithOutput(cmd)
|
||||
|
||||
result := icmd.RunCmd(icmd.Cmd{
|
||||
Command: binaryWithArgs([]string{"-H", server.URL[7:], "version"}),
|
||||
Env: []string{"DOCKER_API_VERSION=xxx"},
|
||||
})
|
||||
result.Assert(c, icmd.Expected{Out: "API version: xxx", ExitCode: 1})
|
||||
c.Assert(svrVersion, check.Equals, "/vxxx/version")
|
||||
|
||||
if !strings.Contains(out, "API version: xxx") {
|
||||
c.Fatalf("Out didn't have 'xxx' for the API version, had:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestApiErrorJSON(c *check.C) {
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
|
@ -161,7 +161,11 @@ func (s *DockerSuite) TestAttachPausedContainer(c *check.C) {
|
|||
defer unpauseAllContainers()
|
||||
dockerCmd(c, "run", "-d", "--name=test", "busybox", "top")
|
||||
dockerCmd(c, "pause", "test")
|
||||
out, _, err := dockerCmdWithError("attach", "test")
|
||||
c.Assert(err, checker.NotNil, check.Commentf(out))
|
||||
c.Assert(out, checker.Contains, "You cannot attach to a paused container, unpause it first")
|
||||
|
||||
result := dockerCmdWithResult("attach", "test")
|
||||
result.Assert(c, icmd.Expected{
|
||||
Error: "exit status 1",
|
||||
ExitCode: 1,
|
||||
Err: "You cannot attach to a paused container, unpause it first",
|
||||
})
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ func (s *DockerSuite) TestAttachClosedOnContainerStop(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *DockerSuite) TestAttachAfterDetach(c *check.C) {
|
||||
|
||||
name := "detachtest"
|
||||
|
||||
cpty, tty, err := pty.Open()
|
||||
|
@ -80,7 +79,11 @@ func (s *DockerSuite) TestAttachAfterDetach(c *check.C) {
|
|||
|
||||
select {
|
||||
case err := <-errChan:
|
||||
c.Assert(err, check.IsNil)
|
||||
if err != nil {
|
||||
buff := make([]byte, 200)
|
||||
tty.Read(buff)
|
||||
c.Fatalf("%s: %s", err, buff)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
c.Fatal("timeout while detaching")
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
@ -5063,13 +5064,11 @@ func (s *DockerSuite) TestBuildDockerfileOutsideContext(c *check.C) {
|
|||
filepath.Join(ctx, "dockerfile1"),
|
||||
filepath.Join(ctx, "dockerfile2"),
|
||||
} {
|
||||
out, _, err := dockerCmdWithError("build", "-t", name, "--no-cache", "-f", dockerfilePath, ".")
|
||||
if err == nil {
|
||||
c.Fatalf("Expected error with %s. Out: %s", dockerfilePath, out)
|
||||
}
|
||||
if !strings.Contains(out, "must be within the build context") && !strings.Contains(out, "Cannot locate Dockerfile") {
|
||||
c.Fatalf("Unexpected error with %s. Out: %s", dockerfilePath, out)
|
||||
}
|
||||
result := dockerCmdWithResult("build", "-t", name, "--no-cache", "-f", dockerfilePath, ".")
|
||||
result.Assert(c, icmd.Expected{
|
||||
Err: "must be within the build context",
|
||||
ExitCode: 1,
|
||||
})
|
||||
deleteImages(name)
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration"
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/go-check/check"
|
||||
|
@ -193,7 +194,7 @@ func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) {
|
|||
}
|
||||
|
||||
// Get the exit status of `docker build`, check it exited because killed.
|
||||
if err := buildCmd.Wait(); err != nil && !isKilled(err) {
|
||||
if err := buildCmd.Wait(); err != nil && !integration.IsKilled(err) {
|
||||
c.Fatalf("wait failed during build run: %T %s", err, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/docker/libnetwork/iptables"
|
||||
|
@ -908,9 +909,8 @@ func (s *DockerDaemonSuite) TestDaemonDefaultNetworkInvalidClusterConfig(c *chec
|
|||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Start daemon with docker0 bridge
|
||||
ifconfigCmd := exec.Command("ifconfig", defaultNetworkBridge)
|
||||
_, err = runCommand(ifconfigCmd)
|
||||
c.Assert(err, check.IsNil)
|
||||
result := icmd.RunCommand("ifconfig", defaultNetworkBridge)
|
||||
result.Assert(c, icmd.Expected{})
|
||||
|
||||
err = d.Restart(fmt.Sprintf("--cluster-store=%s", discoveryBackend))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
@ -2235,7 +2235,6 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *che
|
|||
|
||||
pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", cid)
|
||||
t.Assert(err, check.IsNil)
|
||||
pid = strings.TrimSpace(pid)
|
||||
|
||||
// pause the container
|
||||
if _, err := s.d.Cmd("pause", cid); err != nil {
|
||||
|
@ -2248,19 +2247,18 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *che
|
|||
}
|
||||
|
||||
// resume the container
|
||||
runCmd := exec.Command(ctrBinary, "--address", "unix:///var/run/docker/libcontainerd/docker-containerd.sock", "containers", "resume", cid)
|
||||
if out, ec, err := runCommandWithOutput(runCmd); err != nil {
|
||||
t.Fatalf("Failed to run ctr, ExitCode: %d, err: '%v' output: '%s' cid: '%s'\n", ec, err, out, cid)
|
||||
}
|
||||
result := icmd.RunCommand(
|
||||
ctrBinary,
|
||||
"--address", "unix:///var/run/docker/libcontainerd/docker-containerd.sock",
|
||||
"containers", "resume", cid)
|
||||
result.Assert(t, icmd.Expected{})
|
||||
|
||||
// Give time to containerd to process the command if we don't
|
||||
// the resume event might be received after we do the inspect
|
||||
pidCmd := exec.Command("kill", "-0", pid)
|
||||
_, ec, _ := runCommandWithOutput(pidCmd)
|
||||
for ec == 0 {
|
||||
time.Sleep(1 * time.Second)
|
||||
_, ec, _ = runCommandWithOutput(pidCmd)
|
||||
}
|
||||
waitAndAssert(t, defaultReconciliationTimeout, func(*check.C) (interface{}, check.CommentInterface) {
|
||||
result := icmd.RunCommand("kill", "-0", strings.TrimSpace(pid))
|
||||
return result.ExitCode, nil
|
||||
}, checker.Equals, 0)
|
||||
|
||||
// restart the daemon
|
||||
if err := s.d.Start("--live-restore"); err != nil {
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
"github.com/docker/docker/daemon/events/testutils"
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
|
@ -57,11 +58,14 @@ func (s *DockerSuite) TestEventsUntag(c *check.C) {
|
|||
dockerCmd(c, "tag", image, "utest:tag2")
|
||||
dockerCmd(c, "rmi", "utest:tag1")
|
||||
dockerCmd(c, "rmi", "utest:tag2")
|
||||
eventsCmd := exec.Command(dockerBinary, "events", "--since=1")
|
||||
out, exitCode, _, err := runCommandWithOutputForDuration(eventsCmd, time.Duration(time.Millisecond*2500))
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(exitCode, checker.Equals, 0, check.Commentf("Failed to get events"))
|
||||
events := strings.Split(out, "\n")
|
||||
|
||||
result := icmd.RunCmd(icmd.Cmd{
|
||||
Command: []string{dockerBinary, "events", "--since=1"},
|
||||
Timeout: time.Millisecond * 2500,
|
||||
})
|
||||
result.Assert(c, icmd.Expected{Timeout: true})
|
||||
|
||||
events := strings.Split(result.Stdout(), "\n")
|
||||
nEvents := len(events)
|
||||
// The last element after the split above will be an empty string, so we
|
||||
// get the two elements before the last, which are the untags we're
|
||||
|
@ -275,8 +279,8 @@ func (s *DockerSuite) TestEventsImageLoad(c *check.C) {
|
|||
c.Assert(noImageID, checker.Equals, "", check.Commentf("Should not have any image"))
|
||||
dockerCmd(c, "load", "-i", "saveimg.tar")
|
||||
|
||||
cmd := exec.Command("rm", "-rf", "saveimg.tar")
|
||||
runCommand(cmd)
|
||||
result := icmd.RunCommand("rm", "-rf", "saveimg.tar")
|
||||
result.Assert(c, icmd.Expected{})
|
||||
|
||||
out, _ = dockerCmd(c, "images", "-q", "--no-trunc", myImageName)
|
||||
imageID := strings.TrimSpace(out)
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
|
@ -122,10 +123,8 @@ func (s *DockerSuite) TestExecEnv(c *check.C) {
|
|||
func (s *DockerSuite) TestExecExitStatus(c *check.C) {
|
||||
runSleepingContainer(c, "-d", "--name", "top")
|
||||
|
||||
// Test normal (non-detached) case first
|
||||
cmd := exec.Command(dockerBinary, "exec", "top", "sh", "-c", "exit 23")
|
||||
ec, _ := runCommand(cmd)
|
||||
c.Assert(ec, checker.Equals, 23)
|
||||
result := icmd.RunCommand(dockerBinary, "exec", "top", "sh", "-c", "exit 23")
|
||||
result.Assert(c, icmd.Expected{ExitCode: 23, Error: "exit status 23"})
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestExecPausedContainer(c *check.C) {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/engine-api/types"
|
||||
|
@ -477,27 +478,33 @@ func (s *DockerSuite) TestDockerNetworkInspectWithID(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *check.C) {
|
||||
out, _ := dockerCmd(c, "network", "inspect", "host", "none")
|
||||
result := dockerCmdWithResult("network", "inspect", "host", "none")
|
||||
result.Assert(c, icmd.Expected{})
|
||||
|
||||
networkResources := []types.NetworkResource{}
|
||||
err := json.Unmarshal([]byte(out), &networkResources)
|
||||
err := json.Unmarshal([]byte(result.Stdout()), &networkResources)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(networkResources, checker.HasLen, 2)
|
||||
|
||||
// Should print an error, return an exitCode 1 *but* should print the host network
|
||||
out, exitCode, err := dockerCmdWithError("network", "inspect", "host", "nonexistent")
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(exitCode, checker.Equals, 1)
|
||||
c.Assert(out, checker.Contains, "Error: No such network: nonexistent")
|
||||
result = dockerCmdWithResult("network", "inspect", "host", "nonexistent")
|
||||
result.Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "Error: No such network: nonexistent",
|
||||
Out: "host",
|
||||
})
|
||||
|
||||
networkResources = []types.NetworkResource{}
|
||||
inspectOut := strings.SplitN(out, "\nError: No such network: nonexistent\n", 2)[0]
|
||||
err = json.Unmarshal([]byte(inspectOut), &networkResources)
|
||||
err = json.Unmarshal([]byte(result.Stdout()), &networkResources)
|
||||
c.Assert(networkResources, checker.HasLen, 1)
|
||||
|
||||
// Should print an error and return an exitCode, nothing else
|
||||
out, exitCode, err = dockerCmdWithError("network", "inspect", "nonexistent")
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(exitCode, checker.Equals, 1)
|
||||
c.Assert(out, checker.Contains, "Error: No such network: nonexistent")
|
||||
result = dockerCmdWithResult("network", "inspect", "nonexistent")
|
||||
result.Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "Error: No such network: nonexistent",
|
||||
Out: "[]",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerInspectNetworkWithContainerName(c *check.C) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
@ -204,8 +205,11 @@ func (s *DockerSuite) TestPsListContainersFilterStatus(c *check.C) {
|
|||
containerOut = strings.TrimSpace(out)
|
||||
c.Assert(containerOut, checker.Equals, secondID)
|
||||
|
||||
out, _, _ = dockerCmdWithTimeout(time.Second*60, "ps", "-a", "-q", "--filter=status=rubbish")
|
||||
c.Assert(out, checker.Contains, "Unrecognised filter value for status", check.Commentf("Expected error response due to invalid status filter output: %q", out))
|
||||
result := dockerCmdWithTimeout(time.Second*60, "ps", "-a", "-q", "--filter=status=rubbish")
|
||||
result.Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "Unrecognised filter value for status",
|
||||
})
|
||||
|
||||
// Windows doesn't support pausing of containers
|
||||
if daemonPlatform != "windows" {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
@ -61,9 +62,11 @@ func (s *DockerSuite) TestRenameCheckNames(c *check.C) {
|
|||
name := inspectField(c, newName, "Name")
|
||||
c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))
|
||||
|
||||
name, err := inspectFieldWithError("first_name", "Name")
|
||||
c.Assert(err, checker.NotNil, check.Commentf(name))
|
||||
c.Assert(err.Error(), checker.Contains, "No such container, image or task: first_name")
|
||||
result := dockerCmdWithResult("inspect", "-f={{.Name}}", "first_name")
|
||||
result.Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "No such container, image or task: first_name",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestRenameInvalidName(c *check.C) {
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
|
@ -1856,32 +1857,18 @@ func (s *DockerSuite) TestRunExitOnStdinClose(c *check.C) {
|
|||
// Test run -i --restart xxx doesn't hang
|
||||
func (s *DockerSuite) TestRunInteractiveWithRestartPolicy(c *check.C) {
|
||||
name := "test-inter-restart"
|
||||
runCmd := exec.Command(dockerBinary, "run", "-i", "--name", name, "--restart=always", "busybox", "sh")
|
||||
|
||||
stdin, err := runCmd.StdinPipe()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = runCmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(waitRun(name), check.IsNil)
|
||||
|
||||
_, err = stdin.Write([]byte("exit 11\n"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
finish := make(chan error)
|
||||
go func() {
|
||||
finish <- runCmd.Wait()
|
||||
close(finish)
|
||||
result := icmd.StartCmd(icmd.Cmd{
|
||||
Command: []string{dockerBinary, "run", "-i", "--name", name, "--restart=always", "busybox", "sh"},
|
||||
Stdin: bytes.NewBufferString("exit 11"),
|
||||
})
|
||||
c.Assert(result.Error, checker.IsNil)
|
||||
defer func() {
|
||||
dockerCmdWithResult("stop", name).Assert(c, icmd.Expected{})
|
||||
}()
|
||||
delay := 10 * time.Second
|
||||
select {
|
||||
case <-finish:
|
||||
case <-time.After(delay):
|
||||
c.Fatal("run -i --restart hangs")
|
||||
}
|
||||
|
||||
c.Assert(waitRun(name), check.IsNil)
|
||||
dockerCmd(c, "stop", name)
|
||||
result = icmd.WaitOnCmd(10*time.Second, result)
|
||||
result.Assert(c, icmd.Expected{ExitCode: 11})
|
||||
}
|
||||
|
||||
// Test for #2267
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
|
@ -55,14 +56,18 @@ func (s *DockerSuite) TestVolumeCliInspectMulti(c *check.C) {
|
|||
dockerCmd(c, "volume", "create", "--name", "test2")
|
||||
dockerCmd(c, "volume", "create", "--name", "not-shown")
|
||||
|
||||
out, _, err := dockerCmdWithError("volume", "inspect", "--format='{{ .Name }}'", "test1", "test2", "doesntexist", "not-shown")
|
||||
c.Assert(err, checker.NotNil)
|
||||
result := dockerCmdWithResult("volume", "inspect", "--format={{ .Name }}", "test1", "test2", "doesntexist", "not-shown")
|
||||
result.Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "No such volume: doesntexist",
|
||||
})
|
||||
|
||||
out := result.Stdout()
|
||||
outArr := strings.Split(strings.TrimSpace(out), "\n")
|
||||
c.Assert(len(outArr), check.Equals, 3, check.Commentf("\n%s", out))
|
||||
c.Assert(len(outArr), check.Equals, 2, check.Commentf("\n%s", out))
|
||||
|
||||
c.Assert(out, checker.Contains, "test1")
|
||||
c.Assert(out, checker.Contains, "test2")
|
||||
c.Assert(out, checker.Contains, "Error: No such volume: doesntexist")
|
||||
c.Assert(out, checker.Not(checker.Contains), "not-shown")
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
@ -401,10 +402,11 @@ func (s *DockerSuite) TestDockerNetworkMacVlanBridgeInternalMode(c *check.C) {
|
|||
c.Assert(waitRun("second"), check.IsNil)
|
||||
|
||||
// access outside of the network should fail
|
||||
_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
|
||||
c.Assert(err, check.NotNil)
|
||||
result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
|
||||
result.Assert(c, icmd.Expected{Timeout: true})
|
||||
|
||||
// intra-network communications should succeed
|
||||
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
|
@ -440,10 +442,10 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL2InternalMode(c *check.C) {
|
|||
c.Assert(waitRun("second"), check.IsNil)
|
||||
|
||||
// access outside of the network should fail
|
||||
_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
|
||||
c.Assert(err, check.NotNil)
|
||||
result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
|
||||
c.Assert(result.Error, check.NotNil)
|
||||
// intra-network communications should succeed
|
||||
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
|
@ -481,10 +483,10 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL3InternalMode(c *check.C) {
|
|||
c.Assert(waitRun("second"), check.IsNil)
|
||||
|
||||
// access outside of the network should fail
|
||||
_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
|
||||
c.Assert(err, check.NotNil)
|
||||
result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
|
||||
c.Assert(result.Error, check.NotNil)
|
||||
// intra-network communications should succeed
|
||||
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/httputils"
|
||||
"github.com/docker/docker/pkg/integration"
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/docker/engine-api/types"
|
||||
|
@ -229,15 +229,7 @@ func readBody(b io.ReadCloser) ([]byte, error) {
|
|||
}
|
||||
|
||||
func deleteContainer(container string) error {
|
||||
container = strings.TrimSpace(strings.Replace(container, "\n", " ", -1))
|
||||
rmArgs := strings.Split(fmt.Sprintf("rm -fv %v", container), " ")
|
||||
exitCode, err := runCommand(exec.Command(dockerBinary, rmArgs...))
|
||||
// set error manually if not set
|
||||
if exitCode != 0 && err == nil {
|
||||
err = fmt.Errorf("failed to remove container: `docker rm` exit is non-zero")
|
||||
}
|
||||
|
||||
return err
|
||||
return icmd.RunCommand(dockerBinary, "rm", "-fv", container).Error
|
||||
}
|
||||
|
||||
func getAllContainers() (string, error) {
|
||||
|
@ -398,13 +390,7 @@ func getSliceOfPausedContainers() ([]string, error) {
|
|||
}
|
||||
|
||||
func unpauseContainer(container string) error {
|
||||
unpauseCmd := exec.Command(dockerBinary, "unpause", container)
|
||||
exitCode, err := runCommand(unpauseCmd)
|
||||
if exitCode != 0 && err == nil {
|
||||
err = fmt.Errorf("failed to unpause container")
|
||||
}
|
||||
|
||||
return err
|
||||
return icmd.RunCommand(dockerBinary, "unpause", container).Error
|
||||
}
|
||||
|
||||
func unpauseAllContainers() error {
|
||||
|
@ -428,24 +414,12 @@ func unpauseAllContainers() error {
|
|||
}
|
||||
|
||||
func deleteImages(images ...string) error {
|
||||
args := []string{"rmi", "-f"}
|
||||
args = append(args, images...)
|
||||
rmiCmd := exec.Command(dockerBinary, args...)
|
||||
exitCode, err := runCommand(rmiCmd)
|
||||
// set error manually if not set
|
||||
if exitCode != 0 && err == nil {
|
||||
err = fmt.Errorf("failed to remove image: `docker rmi` exit is non-zero")
|
||||
}
|
||||
return err
|
||||
args := []string{dockerBinary, "rmi", "-f"}
|
||||
return icmd.RunCmd(icmd.Cmd{Command: append(args, images...)}).Error
|
||||
}
|
||||
|
||||
func imageExists(image string) error {
|
||||
inspectCmd := exec.Command(dockerBinary, "inspect", image)
|
||||
exitCode, err := runCommand(inspectCmd)
|
||||
if exitCode != 0 && err == nil {
|
||||
err = fmt.Errorf("couldn't find image %q", image)
|
||||
}
|
||||
return err
|
||||
return icmd.RunCommand(dockerBinary, "inspect", image).Error
|
||||
}
|
||||
|
||||
func pullImageIfNotExist(image string) error {
|
||||
|
@ -464,33 +438,49 @@ func dockerCmdWithError(args ...string) (string, int, error) {
|
|||
if err := validateArgs(args...); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
out, code, err := integration.DockerCmdWithError(dockerBinary, args...)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%v: %s", err, out)
|
||||
result := icmd.RunCommand(dockerBinary, args...)
|
||||
if result.Error != nil {
|
||||
return result.Combined(), result.ExitCode, fmt.Errorf(result.Fails(icmd.Expected{}))
|
||||
}
|
||||
return out, code, err
|
||||
return result.Combined(), result.ExitCode, result.Error
|
||||
}
|
||||
|
||||
func dockerCmdWithStdoutStderr(c *check.C, args ...string) (string, string, int) {
|
||||
if err := validateArgs(args...); err != nil {
|
||||
c.Fatalf(err.Error())
|
||||
}
|
||||
return integration.DockerCmdWithStdoutStderr(dockerBinary, c, args...)
|
||||
|
||||
result := icmd.RunCommand(dockerBinary, args...)
|
||||
// TODO: why is c ever nil?
|
||||
if c != nil {
|
||||
result.Assert(c, icmd.Expected{})
|
||||
}
|
||||
return result.Stdout(), result.Stderr(), result.ExitCode
|
||||
}
|
||||
|
||||
func dockerCmd(c *check.C, args ...string) (string, int) {
|
||||
if err := validateArgs(args...); err != nil {
|
||||
c.Fatalf(err.Error())
|
||||
}
|
||||
return integration.DockerCmd(dockerBinary, c, args...)
|
||||
result := icmd.RunCommand(dockerBinary, args...)
|
||||
result.Assert(c, icmd.Expected{})
|
||||
return result.Combined(), result.ExitCode
|
||||
}
|
||||
|
||||
func dockerCmdWithResult(args ...string) *icmd.Result {
|
||||
return icmd.RunCommand(dockerBinary, args...)
|
||||
}
|
||||
|
||||
func binaryWithArgs(args []string) []string {
|
||||
return append([]string{dockerBinary}, args...)
|
||||
}
|
||||
|
||||
// execute a docker command with a timeout
|
||||
func dockerCmdWithTimeout(timeout time.Duration, args ...string) (string, int, error) {
|
||||
func dockerCmdWithTimeout(timeout time.Duration, args ...string) *icmd.Result {
|
||||
if err := validateArgs(args...); err != nil {
|
||||
return "", 0, err
|
||||
return &icmd.Result{Error: err}
|
||||
}
|
||||
return integration.DockerCmdWithTimeout(dockerBinary, timeout, args...)
|
||||
return icmd.RunCmd(icmd.Cmd{Command: binaryWithArgs(args), Timeout: timeout})
|
||||
}
|
||||
|
||||
// execute a docker command in a directory
|
||||
|
@ -498,15 +488,20 @@ func dockerCmdInDir(c *check.C, path string, args ...string) (string, int, error
|
|||
if err := validateArgs(args...); err != nil {
|
||||
c.Fatalf(err.Error())
|
||||
}
|
||||
return integration.DockerCmdInDir(dockerBinary, path, args...)
|
||||
result := icmd.RunCmd(icmd.Cmd{Command: binaryWithArgs(args), Dir: path})
|
||||
return result.Combined(), result.ExitCode, result.Error
|
||||
}
|
||||
|
||||
// execute a docker command in a directory with a timeout
|
||||
func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...string) (string, int, error) {
|
||||
func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...string) *icmd.Result {
|
||||
if err := validateArgs(args...); err != nil {
|
||||
return "", 0, err
|
||||
return &icmd.Result{Error: err}
|
||||
}
|
||||
return integration.DockerCmdInDirWithTimeout(dockerBinary, timeout, path, args...)
|
||||
return icmd.RunCmd(icmd.Cmd{
|
||||
Command: binaryWithArgs(args),
|
||||
Timeout: timeout,
|
||||
Dir: path,
|
||||
})
|
||||
}
|
||||
|
||||
// validateArgs is a checker to ensure tests are not running commands which are
|
||||
|
@ -1355,17 +1350,12 @@ func buildImageCmdArgs(args []string, name, dockerfile string, useCache bool) *e
|
|||
}
|
||||
|
||||
func waitForContainer(contID string, args ...string) error {
|
||||
args = append([]string{"run", "--name", contID}, args...)
|
||||
cmd := exec.Command(dockerBinary, args...)
|
||||
if _, err := runCommand(cmd); err != nil {
|
||||
return err
|
||||
args = append([]string{dockerBinary, "run", "--name", contID}, args...)
|
||||
result := icmd.RunCmd(icmd.Cmd{Command: args})
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
if err := waitRun(contID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return waitRun(contID)
|
||||
}
|
||||
|
||||
// waitRun will wait for the specified container to be running, maximum 5 seconds.
|
||||
|
@ -1391,22 +1381,22 @@ func waitInspectWithArgs(name, expr, expected string, timeout time.Duration, arg
|
|||
|
||||
args := append(arg, "inspect", "-f", expr, name)
|
||||
for {
|
||||
cmd := exec.Command(dockerBinary, args...)
|
||||
out, _, err := runCommandWithOutput(cmd)
|
||||
if err != nil {
|
||||
if !strings.Contains(out, "No such") {
|
||||
return fmt.Errorf("error executing docker inspect: %v\n%s", err, out)
|
||||
result := icmd.RunCommand(dockerBinary, args...)
|
||||
if result.Error != nil {
|
||||
if !strings.Contains(result.Stderr(), "No such") {
|
||||
return fmt.Errorf("error executing docker inspect: %v\n%s",
|
||||
result.Stderr(), result.Stdout())
|
||||
}
|
||||
select {
|
||||
case <-after:
|
||||
return err
|
||||
return result.Error
|
||||
default:
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
out = strings.TrimSpace(out)
|
||||
out := strings.TrimSpace(result.Stdout())
|
||||
if out == expected {
|
||||
break
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration"
|
||||
"github.com/docker/docker/pkg/integration/cmd"
|
||||
)
|
||||
|
||||
func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
|
||||
|
@ -16,36 +17,33 @@ func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
|
|||
return "", "/"
|
||||
}
|
||||
|
||||
func getExitCode(err error) (int, error) {
|
||||
return integration.GetExitCode(err)
|
||||
// TODO: update code to call cmd.RunCmd directly, and remove this function
|
||||
func runCommandWithOutput(execCmd *exec.Cmd) (string, int, error) {
|
||||
result := cmd.RunCmd(transformCmd(execCmd))
|
||||
return result.Combined(), result.ExitCode, result.Error
|
||||
}
|
||||
|
||||
func processExitCode(err error) (exitCode int) {
|
||||
return integration.ProcessExitCode(err)
|
||||
// TODO: update code to call cmd.RunCmd directly, and remove this function
|
||||
func runCommandWithStdoutStderr(execCmd *exec.Cmd) (string, string, int, error) {
|
||||
result := cmd.RunCmd(transformCmd(execCmd))
|
||||
return result.Stdout(), result.Stderr(), result.ExitCode, result.Error
|
||||
}
|
||||
|
||||
func isKilled(err error) bool {
|
||||
return integration.IsKilled(err)
|
||||
// TODO: update code to call cmd.RunCmd directly, and remove this function
|
||||
func runCommand(execCmd *exec.Cmd) (exitCode int, err error) {
|
||||
result := cmd.RunCmd(transformCmd(execCmd))
|
||||
return result.ExitCode, result.Error
|
||||
}
|
||||
|
||||
func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) {
|
||||
return integration.RunCommandWithOutput(cmd)
|
||||
}
|
||||
|
||||
func runCommandWithStdoutStderr(cmd *exec.Cmd) (stdout string, stderr string, exitCode int, err error) {
|
||||
return integration.RunCommandWithStdoutStderr(cmd)
|
||||
}
|
||||
|
||||
func runCommandWithOutputForDuration(cmd *exec.Cmd, duration time.Duration) (output string, exitCode int, timedOut bool, err error) {
|
||||
return integration.RunCommandWithOutputForDuration(cmd, duration)
|
||||
}
|
||||
|
||||
func runCommandWithOutputAndTimeout(cmd *exec.Cmd, timeout time.Duration) (output string, exitCode int, err error) {
|
||||
return integration.RunCommandWithOutputAndTimeout(cmd, timeout)
|
||||
}
|
||||
|
||||
func runCommand(cmd *exec.Cmd) (exitCode int, err error) {
|
||||
return integration.RunCommand(cmd)
|
||||
// Temporary shim for migrating commands to the new function
|
||||
func transformCmd(execCmd *exec.Cmd) cmd.Cmd {
|
||||
return cmd.Cmd{
|
||||
Command: execCmd.Args,
|
||||
Env: execCmd.Env,
|
||||
Dir: execCmd.Dir,
|
||||
Stdin: execCmd.Stdin,
|
||||
Stdout: execCmd.Stdout,
|
||||
}
|
||||
}
|
||||
|
||||
func runCommandPipelineWithOutput(cmds ...*exec.Cmd) (output string, exitCode int, err error) {
|
||||
|
|
266
pkg/integration/cmd/command.go
Normal file
266
pkg/integration/cmd/command.go
Normal file
|
@ -0,0 +1,266 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type testingT interface {
|
||||
Fatalf(string, ...interface{})
|
||||
}
|
||||
|
||||
const (
|
||||
// None is a token to inform Result.Assert that the output should be empty
|
||||
None string = "<NOTHING>"
|
||||
)
|
||||
|
||||
// GetExitCode returns the ExitStatus of the specified error if its type is
|
||||
// exec.ExitError, returns 0 and an error otherwise.
|
||||
func GetExitCode(err error) (int, error) {
|
||||
exitCode := 0
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
return procExit.ExitStatus(), nil
|
||||
}
|
||||
}
|
||||
return exitCode, fmt.Errorf("failed to get exit code")
|
||||
}
|
||||
|
||||
// ProcessExitCode process the specified error and returns the exit status code
|
||||
// if the error was of type exec.ExitError, returns nothing otherwise.
|
||||
func ProcessExitCode(err error) (exitCode int) {
|
||||
if err != nil {
|
||||
var exiterr error
|
||||
if exitCode, exiterr = GetExitCode(err); exiterr != nil {
|
||||
// TODO: Fix this so we check the error's text.
|
||||
// we've failed to retrieve exit code, so we set it to 127
|
||||
exitCode = 127
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Result stores the result of running a command
|
||||
type Result struct {
|
||||
Cmd *exec.Cmd
|
||||
ExitCode int
|
||||
Error error
|
||||
// Timeout is true if the command was killed because it ran for too long
|
||||
Timeout bool
|
||||
outBuffer *bytes.Buffer
|
||||
errBuffer *bytes.Buffer
|
||||
}
|
||||
|
||||
// Assert compares the Result against the Expected struct, and fails the test if
|
||||
// any of the expcetations are not met.
|
||||
func (r *Result) Assert(t testingT, exp Expected) {
|
||||
fails := r.Fails(exp)
|
||||
if fails == "" {
|
||||
return
|
||||
}
|
||||
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
t.Fatalf("at %s:%d\n%s", filepath.Base(file), line, fails)
|
||||
}
|
||||
|
||||
// Fails returns a formatted string which reports on any failed expectations
|
||||
func (r *Result) Fails(exp Expected) string {
|
||||
errors := []string{}
|
||||
add := func(format string, args ...interface{}) {
|
||||
errors = append(errors, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
if exp.ExitCode != r.ExitCode {
|
||||
add("ExitCode was %d expected %d", r.ExitCode, exp.ExitCode)
|
||||
}
|
||||
if exp.Timeout != r.Timeout {
|
||||
if exp.Timeout {
|
||||
add("Expected command to timeout")
|
||||
} else {
|
||||
add("Expected command to finish, but it hit the timeout")
|
||||
}
|
||||
}
|
||||
if !matchOutput(exp.Out, r.Stdout()) {
|
||||
add("Expected stdout to contain %q", exp.Out)
|
||||
}
|
||||
if !matchOutput(exp.Err, r.Stderr()) {
|
||||
add("Expected stderr to contain %q", exp.Err)
|
||||
}
|
||||
switch {
|
||||
// If a non-zero exit code is expected there is going to be an error.
|
||||
// Don't require an error message as well as an exit code because the
|
||||
// error message is going to be "exit status <code> which is not useful
|
||||
case exp.Error == "" && exp.ExitCode != 0:
|
||||
case exp.Error == "" && r.Error != nil:
|
||||
add("Expected no error")
|
||||
case exp.Error != "" && r.Error == nil:
|
||||
add("Expected error to contain %q, but there was no error", exp.Error)
|
||||
case exp.Error != "" && !strings.Contains(r.Error.Error(), exp.Error):
|
||||
add("Expected error to contain %q", exp.Error)
|
||||
}
|
||||
|
||||
if len(errors) == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s\nFailures:\n%s\n", r, strings.Join(errors, "\n"))
|
||||
}
|
||||
|
||||
func matchOutput(expected string, actual string) bool {
|
||||
switch expected {
|
||||
case None:
|
||||
return actual == ""
|
||||
default:
|
||||
return strings.Contains(actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Result) String() string {
|
||||
var timeout string
|
||||
if r.Timeout {
|
||||
timeout = " (timeout)"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`
|
||||
Command: %s
|
||||
ExitCode: %d%s, Error: %s
|
||||
Stdout: %v
|
||||
Stderr: %v
|
||||
`,
|
||||
strings.Join(r.Cmd.Args, " "),
|
||||
r.ExitCode,
|
||||
timeout,
|
||||
r.Error,
|
||||
r.Stdout(),
|
||||
r.Stderr())
|
||||
}
|
||||
|
||||
// Expected is the expected output from a Command. This struct is compared to a
|
||||
// Result struct by Result.Assert().
|
||||
type Expected struct {
|
||||
ExitCode int
|
||||
Timeout bool
|
||||
Error string
|
||||
Out string
|
||||
Err string
|
||||
}
|
||||
|
||||
// Stdout returns the stdout of the process as a string
|
||||
func (r *Result) Stdout() string {
|
||||
return r.outBuffer.String()
|
||||
}
|
||||
|
||||
// Stderr returns the stderr of the process as a string
|
||||
func (r *Result) Stderr() string {
|
||||
return r.errBuffer.String()
|
||||
}
|
||||
|
||||
// Combined returns the stdout and stderr combined into a single string
|
||||
func (r *Result) Combined() string {
|
||||
return r.outBuffer.String() + r.errBuffer.String()
|
||||
}
|
||||
|
||||
// SetExitError sets Error and ExitCode based on Error
|
||||
func (r *Result) SetExitError(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
r.Error = err
|
||||
r.ExitCode = ProcessExitCode(err)
|
||||
}
|
||||
|
||||
// Cmd is a command to run. One of Command or CommandArgs can be used to set the
|
||||
// comand line. Command will be paased to shlex and split into a string slice.
|
||||
// CommandArgs is an already split command line.
|
||||
type Cmd struct {
|
||||
Command []string
|
||||
Timeout time.Duration
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Dir string
|
||||
Env []string
|
||||
}
|
||||
|
||||
// RunCmd runs a command and returns a Result
|
||||
func RunCmd(cmd Cmd) *Result {
|
||||
result := StartCmd(cmd)
|
||||
if result.Error != nil {
|
||||
return result
|
||||
}
|
||||
return WaitOnCmd(cmd.Timeout, result)
|
||||
}
|
||||
|
||||
// RunCommand parses a command line and runs it, returning a result
|
||||
func RunCommand(command string, args ...string) *Result {
|
||||
return RunCmd(Cmd{Command: append([]string{command}, args...)})
|
||||
}
|
||||
|
||||
// StartCmd starts a command, but doesn't wait for it to finish
|
||||
func StartCmd(cmd Cmd) *Result {
|
||||
result := buildCmd(cmd)
|
||||
if result.Error != nil {
|
||||
return result
|
||||
}
|
||||
result.SetExitError(result.Cmd.Start())
|
||||
return result
|
||||
}
|
||||
|
||||
func buildCmd(cmd Cmd) *Result {
|
||||
var execCmd *exec.Cmd
|
||||
switch len(cmd.Command) {
|
||||
case 1:
|
||||
execCmd = exec.Command(cmd.Command[0])
|
||||
default:
|
||||
execCmd = exec.Command(cmd.Command[0], cmd.Command[1:]...)
|
||||
}
|
||||
outBuffer := new(bytes.Buffer)
|
||||
errBuffer := new(bytes.Buffer)
|
||||
|
||||
execCmd.Stdin = cmd.Stdin
|
||||
execCmd.Dir = cmd.Dir
|
||||
execCmd.Env = cmd.Env
|
||||
if cmd.Stdout != nil {
|
||||
execCmd.Stdout = io.MultiWriter(outBuffer, cmd.Stdout)
|
||||
} else {
|
||||
execCmd.Stdout = outBuffer
|
||||
}
|
||||
execCmd.Stderr = errBuffer
|
||||
return &Result{
|
||||
Cmd: execCmd,
|
||||
outBuffer: outBuffer,
|
||||
errBuffer: errBuffer,
|
||||
}
|
||||
}
|
||||
|
||||
// WaitOnCmd waits for a command to complete. If timeout is non-nil then
|
||||
// only wait until the timeout.
|
||||
func WaitOnCmd(timeout time.Duration, result *Result) *Result {
|
||||
if timeout == time.Duration(0) {
|
||||
result.SetExitError(result.Cmd.Wait())
|
||||
return result
|
||||
}
|
||||
|
||||
done := make(chan error, 1)
|
||||
// Wait for command to exit in a goroutine
|
||||
go func() {
|
||||
done <- result.Cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
killErr := result.Cmd.Process.Kill()
|
||||
if killErr != nil {
|
||||
fmt.Printf("failed to kill (pid=%d): %v\n", result.Cmd.Process.Pid, killErr)
|
||||
}
|
||||
result.Timeout = true
|
||||
case err := <-done:
|
||||
result.SetExitError(err)
|
||||
}
|
||||
return result
|
||||
}
|
104
pkg/integration/cmd/command_test.go
Normal file
104
pkg/integration/cmd/command_test.go
Normal file
|
@ -0,0 +1,104 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
)
|
||||
|
||||
func TestRunCommand(t *testing.T) {
|
||||
// TODO Windows: Port this test
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Needs porting to Windows")
|
||||
}
|
||||
|
||||
result := RunCommand("ls")
|
||||
result.Assert(t, Expected{})
|
||||
|
||||
result = RunCommand("doesnotexists")
|
||||
expectedError := `exec: "doesnotexists": executable file not found`
|
||||
result.Assert(t, Expected{ExitCode: 127, Error: expectedError})
|
||||
|
||||
result = RunCommand("ls", "-z")
|
||||
result.Assert(t, Expected{
|
||||
ExitCode: 2,
|
||||
Error: "exit status 2",
|
||||
Err: "invalid option",
|
||||
})
|
||||
assert.Contains(t, result.Combined(), "invalid option")
|
||||
}
|
||||
|
||||
func TestRunCommandWithCombined(t *testing.T) {
|
||||
// TODO Windows: Port this test
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Needs porting to Windows")
|
||||
}
|
||||
|
||||
result := RunCommand("ls", "-a")
|
||||
result.Assert(t, Expected{})
|
||||
|
||||
assert.Contains(t, result.Combined(), "..")
|
||||
assert.Contains(t, result.Stdout(), "..")
|
||||
}
|
||||
|
||||
func TestRunCommandWithTimeoutFinished(t *testing.T) {
|
||||
// TODO Windows: Port this test
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Needs porting to Windows")
|
||||
}
|
||||
|
||||
result := RunCmd(Cmd{
|
||||
Command: []string{"ls", "-a"},
|
||||
Timeout: 50 * time.Millisecond,
|
||||
})
|
||||
result.Assert(t, Expected{Out: ".."})
|
||||
}
|
||||
|
||||
func TestRunCommandWithTimeoutKilled(t *testing.T) {
|
||||
// TODO Windows: Port this test
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Needs porting to Windows")
|
||||
}
|
||||
|
||||
command := []string{"sh", "-c", "while true ; do echo 1 ; sleep .1 ; done"}
|
||||
result := RunCmd(Cmd{Command: command, Timeout: 500 * time.Millisecond})
|
||||
result.Assert(t, Expected{Timeout: true})
|
||||
|
||||
ones := strings.Split(result.Stdout(), "\n")
|
||||
assert.Equal(t, len(ones), 6)
|
||||
}
|
||||
|
||||
func TestRunCommandWithErrors(t *testing.T) {
|
||||
result := RunCommand("/foobar")
|
||||
result.Assert(t, Expected{Error: "foobar", ExitCode: 127})
|
||||
}
|
||||
|
||||
func TestRunCommandWithStdoutStderr(t *testing.T) {
|
||||
result := RunCommand("echo", "hello", "world")
|
||||
result.Assert(t, Expected{Out: "hello world\n", Err: None})
|
||||
}
|
||||
|
||||
func TestRunCommandWithStdoutStderrError(t *testing.T) {
|
||||
result := RunCommand("doesnotexists")
|
||||
|
||||
expected := `exec: "doesnotexists": executable file not found`
|
||||
result.Assert(t, Expected{Out: None, Err: None, ExitCode: 127, Error: expected})
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
expected = "ls: unknown option"
|
||||
default:
|
||||
expected = "ls: invalid option"
|
||||
}
|
||||
|
||||
result = RunCommand("ls", "-z")
|
||||
result.Assert(t, Expected{
|
||||
Out: None,
|
||||
Err: expected,
|
||||
ExitCode: 2,
|
||||
Error: "exit status 2",
|
||||
})
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
// We use the elongated quote mechanism for quoting error returns as
|
||||
// the use of strconv.Quote or %q in fmt.Errorf will escape characters. This
|
||||
// has a big downside on Windows where the args include paths, so instead
|
||||
// of something like c:\directory\file.txt, the output would be
|
||||
// c:\\directory\\file.txt. This is highly misleading.
|
||||
const quote = `"`
|
||||
|
||||
var execCommand = exec.Command
|
||||
|
||||
// DockerCmdWithError executes a docker command that is supposed to fail and returns
|
||||
// the output, the exit code and the error.
|
||||
func DockerCmdWithError(dockerBinary string, args ...string) (string, int, error) {
|
||||
return RunCommandWithOutput(execCommand(dockerBinary, args...))
|
||||
}
|
||||
|
||||
// DockerCmdWithStdoutStderr executes a docker command and returns the content of the
|
||||
// stdout, stderr and the exit code. If a check.C is passed, it will fail and stop tests
|
||||
// if the error is not nil.
|
||||
func DockerCmdWithStdoutStderr(dockerBinary string, c *check.C, args ...string) (string, string, int) {
|
||||
stdout, stderr, status, err := RunCommandWithStdoutStderr(execCommand(dockerBinary, args...))
|
||||
if c != nil {
|
||||
c.Assert(err, check.IsNil, check.Commentf(quote+"%v"+quote+" failed with errors: %s, %v", strings.Join(args, " "), stderr, err))
|
||||
}
|
||||
return stdout, stderr, status
|
||||
}
|
||||
|
||||
// DockerCmd executes a docker command and returns the output and the exit code. If the
|
||||
// command returns an error, it will fail and stop the tests.
|
||||
func DockerCmd(dockerBinary string, c *check.C, args ...string) (string, int) {
|
||||
out, status, err := RunCommandWithOutput(execCommand(dockerBinary, args...))
|
||||
c.Assert(err, check.IsNil, check.Commentf(quote+"%v"+quote+" failed with errors: %s, %v", strings.Join(args, " "), out, err))
|
||||
return out, status
|
||||
}
|
||||
|
||||
// DockerCmdWithTimeout executes a docker command with a timeout, and returns the output,
|
||||
// the exit code and the error (if any).
|
||||
func DockerCmdWithTimeout(dockerBinary string, timeout time.Duration, args ...string) (string, int, error) {
|
||||
out, status, err := RunCommandWithOutputAndTimeout(execCommand(dockerBinary, args...), timeout)
|
||||
if err != nil {
|
||||
return out, status, fmt.Errorf(quote+"%v"+quote+" failed with errors: %v : %q", strings.Join(args, " "), err, out)
|
||||
}
|
||||
return out, status, err
|
||||
}
|
||||
|
||||
// DockerCmdInDir executes a docker command in a directory and returns the output, the
|
||||
// exit code and the error (if any).
|
||||
func DockerCmdInDir(dockerBinary string, path string, args ...string) (string, int, error) {
|
||||
dockerCommand := execCommand(dockerBinary, args...)
|
||||
dockerCommand.Dir = path
|
||||
out, status, err := RunCommandWithOutput(dockerCommand)
|
||||
if err != nil {
|
||||
return out, status, fmt.Errorf(quote+"%v"+quote+" failed with errors: %v : %q", strings.Join(args, " "), err, out)
|
||||
}
|
||||
return out, status, err
|
||||
}
|
||||
|
||||
// DockerCmdInDirWithTimeout executes a docker command in a directory with a timeout and
|
||||
// returns the output, the exit code and the error (if any).
|
||||
func DockerCmdInDirWithTimeout(dockerBinary string, timeout time.Duration, path string, args ...string) (string, int, error) {
|
||||
dockerCommand := execCommand(dockerBinary, args...)
|
||||
dockerCommand.Dir = path
|
||||
out, status, err := RunCommandWithOutputAndTimeout(dockerCommand, timeout)
|
||||
if err != nil {
|
||||
return out, status, fmt.Errorf(quote+"%v"+quote+" failed with errors: %v : %q", strings.Join(args, " "), err, out)
|
||||
}
|
||||
return out, status, err
|
||||
}
|
|
@ -1,405 +0,0 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
const dockerBinary = "docker"
|
||||
|
||||
// Setup go-check for this test
|
||||
func Test(t *testing.T) {
|
||||
check.TestingT(t)
|
||||
}
|
||||
|
||||
func init() {
|
||||
check.Suite(&DockerCmdSuite{})
|
||||
}
|
||||
|
||||
type DockerCmdSuite struct{}
|
||||
|
||||
// Fake the exec.Command to use our mock.
|
||||
func (s *DockerCmdSuite) SetUpTest(c *check.C) {
|
||||
execCommand = fakeExecCommand
|
||||
}
|
||||
|
||||
// And bring it back to normal after the test.
|
||||
func (s *DockerCmdSuite) TearDownTest(c *check.C) {
|
||||
execCommand = exec.Command
|
||||
}
|
||||
|
||||
// DockerCmdWithError tests
|
||||
|
||||
func (s *DockerCmdSuite) TestDockerCmdWithError(c *check.C) {
|
||||
cmds := []struct {
|
||||
binary string
|
||||
args []string
|
||||
expectedOut string
|
||||
expectedExitCode int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
"doesnotexists",
|
||||
[]string{},
|
||||
"Command doesnotexists not found.",
|
||||
1,
|
||||
fmt.Errorf("exit status 1"),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"an", "error"},
|
||||
"an error has occurred",
|
||||
1,
|
||||
fmt.Errorf("exit status 1"),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"an", "exitCode", "127"},
|
||||
"an error has occurred with exitCode 127",
|
||||
127,
|
||||
fmt.Errorf("exit status 127"),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"run", "-ti", "ubuntu", "echo", "hello"},
|
||||
"hello",
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
}
|
||||
for _, cmd := range cmds {
|
||||
out, exitCode, error := DockerCmdWithError(cmd.binary, cmd.args...)
|
||||
c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
|
||||
c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
|
||||
if cmd.expectedError != nil {
|
||||
c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
|
||||
c.Assert(error.Error(), check.Equals, cmd.expectedError.Error(), check.Commentf("Expected error %q for arguments %v, got %q", cmd.expectedError.Error(), cmd.args, error.Error()))
|
||||
} else {
|
||||
c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DockerCmdWithStdoutStderr tests
|
||||
|
||||
type dockerCmdWithStdoutStderrErrorSuite struct{}
|
||||
|
||||
func (s *dockerCmdWithStdoutStderrErrorSuite) Test(c *check.C) {
|
||||
// Should fail, the test too
|
||||
DockerCmdWithStdoutStderr(dockerBinary, c, "an", "error")
|
||||
}
|
||||
|
||||
type dockerCmdWithStdoutStderrSuccessSuite struct{}
|
||||
|
||||
func (s *dockerCmdWithStdoutStderrSuccessSuite) Test(c *check.C) {
|
||||
stdout, stderr, exitCode := DockerCmdWithStdoutStderr(dockerBinary, c, "run", "-ti", "ubuntu", "echo", "hello")
|
||||
c.Assert(stdout, check.Equals, "hello")
|
||||
c.Assert(stderr, check.Equals, "")
|
||||
c.Assert(exitCode, check.Equals, 0)
|
||||
|
||||
}
|
||||
|
||||
func (s *DockerCmdSuite) TestDockerCmdWithStdoutStderrError(c *check.C) {
|
||||
// Run error suite, should fail.
|
||||
output := String{}
|
||||
result := check.Run(&dockerCmdWithStdoutStderrErrorSuite{}, &check.RunConf{Output: &output})
|
||||
c.Check(result.Succeeded, check.Equals, 0)
|
||||
c.Check(result.Failed, check.Equals, 1)
|
||||
}
|
||||
|
||||
func (s *DockerCmdSuite) TestDockerCmdWithStdoutStderrSuccess(c *check.C) {
|
||||
// Run error suite, should fail.
|
||||
output := String{}
|
||||
result := check.Run(&dockerCmdWithStdoutStderrSuccessSuite{}, &check.RunConf{Output: &output})
|
||||
c.Check(result.Succeeded, check.Equals, 1)
|
||||
c.Check(result.Failed, check.Equals, 0)
|
||||
}
|
||||
|
||||
// DockerCmd tests
|
||||
|
||||
type dockerCmdErrorSuite struct{}
|
||||
|
||||
func (s *dockerCmdErrorSuite) Test(c *check.C) {
|
||||
// Should fail, the test too
|
||||
DockerCmd(dockerBinary, c, "an", "error")
|
||||
}
|
||||
|
||||
type dockerCmdSuccessSuite struct{}
|
||||
|
||||
func (s *dockerCmdSuccessSuite) Test(c *check.C) {
|
||||
stdout, exitCode := DockerCmd(dockerBinary, c, "run", "-ti", "ubuntu", "echo", "hello")
|
||||
c.Assert(stdout, check.Equals, "hello")
|
||||
c.Assert(exitCode, check.Equals, 0)
|
||||
|
||||
}
|
||||
|
||||
func (s *DockerCmdSuite) TestDockerCmdError(c *check.C) {
|
||||
// Run error suite, should fail.
|
||||
output := String{}
|
||||
result := check.Run(&dockerCmdErrorSuite{}, &check.RunConf{Output: &output})
|
||||
c.Check(result.Succeeded, check.Equals, 0)
|
||||
c.Check(result.Failed, check.Equals, 1)
|
||||
}
|
||||
|
||||
func (s *DockerCmdSuite) TestDockerCmdSuccess(c *check.C) {
|
||||
// Run error suite, should fail.
|
||||
output := String{}
|
||||
result := check.Run(&dockerCmdSuccessSuite{}, &check.RunConf{Output: &output})
|
||||
c.Check(result.Succeeded, check.Equals, 1)
|
||||
c.Check(result.Failed, check.Equals, 0)
|
||||
}
|
||||
|
||||
// DockerCmdWithTimeout tests
|
||||
|
||||
func (s *DockerCmdSuite) TestDockerCmdWithTimeout(c *check.C) {
|
||||
cmds := []struct {
|
||||
binary string
|
||||
args []string
|
||||
timeout time.Duration
|
||||
expectedOut string
|
||||
expectedExitCode int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
"doesnotexists",
|
||||
[]string{},
|
||||
200 * time.Millisecond,
|
||||
`Command doesnotexists not found.`,
|
||||
1,
|
||||
fmt.Errorf(`"" failed with errors: exit status 1 : "Command doesnotexists not found."`),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"an", "error"},
|
||||
200 * time.Millisecond,
|
||||
`an error has occurred`,
|
||||
1,
|
||||
fmt.Errorf(`"an error" failed with errors: exit status 1 : "an error has occurred"`),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"a", "command", "that", "times", "out"},
|
||||
5 * time.Millisecond,
|
||||
"",
|
||||
0,
|
||||
fmt.Errorf(`"a command that times out" failed with errors: command timed out : ""`),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"run", "-ti", "ubuntu", "echo", "hello"},
|
||||
200 * time.Millisecond,
|
||||
"hello",
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
}
|
||||
for _, cmd := range cmds {
|
||||
out, exitCode, error := DockerCmdWithTimeout(cmd.binary, cmd.timeout, cmd.args...)
|
||||
c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
|
||||
c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
|
||||
if cmd.expectedError != nil {
|
||||
c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
|
||||
c.Assert(error.Error(), check.Equals, cmd.expectedError.Error(), check.Commentf("Expected error %q for arguments %v, got %q", cmd.expectedError.Error(), cmd.args, error.Error()))
|
||||
} else {
|
||||
c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DockerCmdInDir tests
|
||||
|
||||
func (s *DockerCmdSuite) TestDockerCmdInDir(c *check.C) {
|
||||
tempFolder, err := ioutil.TempDir("", "test-docker-cmd-in-dir")
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
cmds := []struct {
|
||||
binary string
|
||||
args []string
|
||||
expectedOut string
|
||||
expectedExitCode int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
"doesnotexists",
|
||||
[]string{},
|
||||
`Command doesnotexists not found.`,
|
||||
1,
|
||||
fmt.Errorf(`"dir:%s" failed with errors: exit status 1 : "Command doesnotexists not found."`, tempFolder),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"an", "error"},
|
||||
`an error has occurred`,
|
||||
1,
|
||||
fmt.Errorf(`"dir:%s an error" failed with errors: exit status 1 : "an error has occurred"`, tempFolder),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"run", "-ti", "ubuntu", "echo", "hello"},
|
||||
"hello",
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
}
|
||||
for _, cmd := range cmds {
|
||||
// We prepend the arguments with dir:thefolder.. the fake command will check
|
||||
// that the current workdir is the same as the one we are passing.
|
||||
args := append([]string{"dir:" + tempFolder}, cmd.args...)
|
||||
out, exitCode, error := DockerCmdInDir(cmd.binary, tempFolder, args...)
|
||||
c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
|
||||
c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
|
||||
if cmd.expectedError != nil {
|
||||
c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
|
||||
c.Assert(error.Error(), check.Equals, cmd.expectedError.Error(), check.Commentf("Expected error %q for arguments %v, got %q", cmd.expectedError.Error(), cmd.args, error.Error()))
|
||||
} else {
|
||||
c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DockerCmdInDirWithTimeout tests
|
||||
|
||||
func (s *DockerCmdSuite) TestDockerCmdInDirWithTimeout(c *check.C) {
|
||||
tempFolder, err := ioutil.TempDir("", "test-docker-cmd-in-dir")
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
cmds := []struct {
|
||||
binary string
|
||||
args []string
|
||||
timeout time.Duration
|
||||
expectedOut string
|
||||
expectedExitCode int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
"doesnotexists",
|
||||
[]string{},
|
||||
200 * time.Millisecond,
|
||||
`Command doesnotexists not found.`,
|
||||
1,
|
||||
fmt.Errorf(`"dir:%s" failed with errors: exit status 1 : "Command doesnotexists not found."`, tempFolder),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"an", "error"},
|
||||
200 * time.Millisecond,
|
||||
`an error has occurred`,
|
||||
1,
|
||||
fmt.Errorf(`"dir:%s an error" failed with errors: exit status 1 : "an error has occurred"`, tempFolder),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"a", "command", "that", "times", "out"},
|
||||
5 * time.Millisecond,
|
||||
"",
|
||||
0,
|
||||
fmt.Errorf(`"dir:%s a command that times out" failed with errors: command timed out : ""`, tempFolder),
|
||||
},
|
||||
{
|
||||
dockerBinary,
|
||||
[]string{"run", "-ti", "ubuntu", "echo", "hello"},
|
||||
200 * time.Millisecond,
|
||||
"hello",
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
}
|
||||
for _, cmd := range cmds {
|
||||
// We prepend the arguments with dir:thefolder.. the fake command will check
|
||||
// that the current workdir is the same as the one we are passing.
|
||||
args := append([]string{"dir:" + tempFolder}, cmd.args...)
|
||||
out, exitCode, error := DockerCmdInDirWithTimeout(cmd.binary, cmd.timeout, tempFolder, args...)
|
||||
c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
|
||||
c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
|
||||
if cmd.expectedError != nil {
|
||||
c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
|
||||
c.Assert(error.Error(), check.Equals, cmd.expectedError.Error(), check.Commentf("Expected error %q for arguments %v, got %q", cmd.expectedError.Error(), cmd.args, error.Error()))
|
||||
} else {
|
||||
c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers :)
|
||||
|
||||
// Type implementing the io.Writer interface for analyzing output.
|
||||
type String struct {
|
||||
value string
|
||||
}
|
||||
|
||||
// The only function required by the io.Writer interface. Will append
|
||||
// written data to the String.value string.
|
||||
func (s *String) Write(p []byte) (n int, err error) {
|
||||
s.value += string(p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Helper function that mock the exec.Command call (and call the test binary)
|
||||
func fakeExecCommand(command string, args ...string) *exec.Cmd {
|
||||
cs := []string{"-test.run=TestHelperProcess", "--", command}
|
||||
cs = append(cs, args...)
|
||||
cmd := exec.Command(os.Args[0], cs...)
|
||||
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func TestHelperProcess(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
|
||||
return
|
||||
}
|
||||
args := os.Args
|
||||
|
||||
// Previous arguments are tests stuff, that looks like :
|
||||
// /tmp/go-build970079519/…/_test/integration.test -test.run=TestHelperProcess --
|
||||
cmd, args := args[3], args[4:]
|
||||
// Handle the case where args[0] is dir:...
|
||||
if len(args) > 0 && strings.HasPrefix(args[0], "dir:") {
|
||||
expectedCwd := args[0][4:]
|
||||
if len(args) > 1 {
|
||||
args = args[1:]
|
||||
}
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to get workingdir: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// This checks that the given path is the same as the currend working dire
|
||||
if expectedCwd != cwd {
|
||||
fmt.Fprintf(os.Stderr, "Current workdir should be %q, but is %q", expectedCwd, cwd)
|
||||
}
|
||||
}
|
||||
switch cmd {
|
||||
case dockerBinary:
|
||||
argsStr := strings.Join(args, " ")
|
||||
switch argsStr {
|
||||
case "an exitCode 127":
|
||||
fmt.Fprintf(os.Stderr, "an error has occurred with exitCode 127")
|
||||
os.Exit(127)
|
||||
case "an error":
|
||||
fmt.Fprintf(os.Stderr, "an error has occurred")
|
||||
os.Exit(1)
|
||||
case "a command that times out":
|
||||
time.Sleep(10 * time.Second)
|
||||
fmt.Fprintf(os.Stdout, "too long, should be killed")
|
||||
// A random exit code (that should never happened in tests)
|
||||
os.Exit(7)
|
||||
case "run -ti ubuntu echo hello":
|
||||
fmt.Fprintf(os.Stdout, "hello")
|
||||
default:
|
||||
fmt.Fprintf(os.Stdout, "no arguments")
|
||||
}
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Command %s not found.", cmd)
|
||||
os.Exit(1)
|
||||
}
|
||||
// some code here to check arguments perhaps?
|
||||
os.Exit(0)
|
||||
}
|
|
@ -2,7 +2,6 @@ package integration
|
|||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -14,35 +13,10 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
icmd "github.com/docker/docker/pkg/integration/cmd"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
)
|
||||
|
||||
// GetExitCode returns the ExitStatus of the specified error if its type is
|
||||
// exec.ExitError, returns 0 and an error otherwise.
|
||||
func GetExitCode(err error) (int, error) {
|
||||
exitCode := 0
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
return procExit.ExitStatus(), nil
|
||||
}
|
||||
}
|
||||
return exitCode, fmt.Errorf("failed to get exit code")
|
||||
}
|
||||
|
||||
// ProcessExitCode process the specified error and returns the exit status code
|
||||
// if the error was of type exec.ExitError, returns nothing otherwise.
|
||||
func ProcessExitCode(err error) (exitCode int) {
|
||||
if err != nil {
|
||||
var exiterr error
|
||||
if exitCode, exiterr = GetExitCode(err); exiterr != nil {
|
||||
// TODO: Fix this so we check the error's text.
|
||||
// we've failed to retrieve exit code, so we set it to 127
|
||||
exitCode = 127
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsKilled process the specified error and returns whether the process was killed or not.
|
||||
func IsKilled(err error) bool {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
|
@ -58,110 +32,14 @@ func IsKilled(err error) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// RunCommandWithOutput runs the specified command and returns the combined output (stdout/stderr)
|
||||
// with the exitCode different from 0 and the error if something bad happened
|
||||
func RunCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) {
|
||||
func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) {
|
||||
exitCode = 0
|
||||
out, err := cmd.CombinedOutput()
|
||||
exitCode = ProcessExitCode(err)
|
||||
exitCode = icmd.ProcessExitCode(err)
|
||||
output = string(out)
|
||||
return
|
||||
}
|
||||
|
||||
// RunCommandWithStdoutStderr runs the specified command and returns stdout and stderr separately
|
||||
// with the exitCode different from 0 and the error if something bad happened
|
||||
func RunCommandWithStdoutStderr(cmd *exec.Cmd) (stdout string, stderr string, exitCode int, err error) {
|
||||
var (
|
||||
stderrBuffer, stdoutBuffer bytes.Buffer
|
||||
)
|
||||
exitCode = 0
|
||||
cmd.Stderr = &stderrBuffer
|
||||
cmd.Stdout = &stdoutBuffer
|
||||
err = cmd.Run()
|
||||
exitCode = ProcessExitCode(err)
|
||||
|
||||
stdout = stdoutBuffer.String()
|
||||
stderr = stderrBuffer.String()
|
||||
return
|
||||
}
|
||||
|
||||
// RunCommandWithOutputForDuration runs the specified command "timeboxed" by the specified duration.
|
||||
// If the process is still running when the timebox is finished, the process will be killed and .
|
||||
// It will returns the output with the exitCode different from 0 and the error if something bad happened
|
||||
// and a boolean whether it has been killed or not.
|
||||
func RunCommandWithOutputForDuration(cmd *exec.Cmd, duration time.Duration) (output string, exitCode int, timedOut bool, err error) {
|
||||
var outputBuffer bytes.Buffer
|
||||
if cmd.Stdout != nil {
|
||||
err = errors.New("cmd.Stdout already set")
|
||||
return
|
||||
}
|
||||
cmd.Stdout = &outputBuffer
|
||||
|
||||
if cmd.Stderr != nil {
|
||||
err = errors.New("cmd.Stderr already set")
|
||||
return
|
||||
}
|
||||
cmd.Stderr = &outputBuffer
|
||||
|
||||
// Start the command in the main thread..
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Fail to start command %v : %v", cmd, err)
|
||||
}
|
||||
|
||||
type exitInfo struct {
|
||||
exitErr error
|
||||
exitCode int
|
||||
}
|
||||
|
||||
done := make(chan exitInfo, 1)
|
||||
|
||||
go func() {
|
||||
// And wait for it to exit in the goroutine :)
|
||||
info := exitInfo{}
|
||||
info.exitErr = cmd.Wait()
|
||||
info.exitCode = ProcessExitCode(info.exitErr)
|
||||
done <- info
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(duration):
|
||||
killErr := cmd.Process.Kill()
|
||||
if killErr != nil {
|
||||
fmt.Printf("failed to kill (pid=%d): %v\n", cmd.Process.Pid, killErr)
|
||||
}
|
||||
timedOut = true
|
||||
case info := <-done:
|
||||
err = info.exitErr
|
||||
exitCode = info.exitCode
|
||||
}
|
||||
output = outputBuffer.String()
|
||||
return
|
||||
}
|
||||
|
||||
var errCmdTimeout = fmt.Errorf("command timed out")
|
||||
|
||||
// RunCommandWithOutputAndTimeout runs the specified command "timeboxed" by the specified duration.
|
||||
// It returns the output with the exitCode different from 0 and the error if something bad happened or
|
||||
// if the process timed out (and has been killed).
|
||||
func RunCommandWithOutputAndTimeout(cmd *exec.Cmd, timeout time.Duration) (output string, exitCode int, err error) {
|
||||
var timedOut bool
|
||||
output, exitCode, timedOut, err = RunCommandWithOutputForDuration(cmd, timeout)
|
||||
if timedOut {
|
||||
err = errCmdTimeout
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RunCommand runs the specified command and returns the exitCode different from 0
|
||||
// and the error if something bad happened.
|
||||
func RunCommand(cmd *exec.Cmd) (exitCode int, err error) {
|
||||
exitCode = 0
|
||||
err = cmd.Run()
|
||||
exitCode = ProcessExitCode(err)
|
||||
return
|
||||
}
|
||||
|
||||
// RunCommandPipelineWithOutput runs the array of commands with the output
|
||||
// of each pipelined with the following (like cmd1 | cmd2 | cmd3 would do).
|
||||
// It returns the final output, the exitCode different from 0 and the error
|
||||
|
@ -205,7 +83,7 @@ func RunCommandPipelineWithOutput(cmds ...*exec.Cmd) (output string, exitCode in
|
|||
}
|
||||
|
||||
// wait on last cmd
|
||||
return RunCommandWithOutput(cmds[len(cmds)-1])
|
||||
return runCommandWithOutput(cmds[len(cmds)-1])
|
||||
}
|
||||
|
||||
// ConvertSliceOfStringsToMap converts a slices of string in a map
|
||||
|
@ -341,11 +219,9 @@ func RunAtDifferentDate(date time.Time, block func()) {
|
|||
const timeLayout = "010203042006"
|
||||
// Ensure we bring time back to now
|
||||
now := time.Now().Format(timeLayout)
|
||||
dateReset := exec.Command("date", now)
|
||||
defer RunCommand(dateReset)
|
||||
defer icmd.RunCommand("date", now)
|
||||
|
||||
dateChange := exec.Command("date", date.Format(timeLayout))
|
||||
RunCommand(dateChange)
|
||||
icmd.RunCommand("date", date.Format(timeLayout))
|
||||
block()
|
||||
return
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -54,203 +53,6 @@ func TestIsKilledTrueWithKilledProcess(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithOutput(t *testing.T) {
|
||||
var (
|
||||
echoHelloWorldCmd *exec.Cmd
|
||||
expected string
|
||||
)
|
||||
if runtime.GOOS != "windows" {
|
||||
echoHelloWorldCmd = exec.Command("echo", "hello", "world")
|
||||
expected = "hello world\n"
|
||||
} else {
|
||||
echoHelloWorldCmd = exec.Command("cmd", "/s", "/c", "echo", "hello", "world")
|
||||
expected = "hello world\r\n"
|
||||
}
|
||||
|
||||
out, exitCode, err := RunCommandWithOutput(echoHelloWorldCmd)
|
||||
if out != expected || exitCode != 0 || err != nil {
|
||||
t.Fatalf("Expected command to output %s, got %s, %v with exitCode %v", expected, out, err, exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithOutputError(t *testing.T) {
|
||||
var (
|
||||
p string
|
||||
wrongCmd *exec.Cmd
|
||||
expected string
|
||||
expectedExitCode int
|
||||
)
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
p = "$PATH"
|
||||
wrongCmd = exec.Command("ls", "-z")
|
||||
expected = `ls: invalid option -- 'z'
|
||||
Try 'ls --help' for more information.
|
||||
`
|
||||
expectedExitCode = 2
|
||||
} else {
|
||||
p = "%PATH%"
|
||||
wrongCmd = exec.Command("cmd", "/s", "/c", "dir", "/Z")
|
||||
expected = "Invalid switch - " + strconv.Quote("Z") + ".\r\n"
|
||||
expectedExitCode = 1
|
||||
}
|
||||
cmd := exec.Command("doesnotexists")
|
||||
out, exitCode, err := RunCommandWithOutput(cmd)
|
||||
expectedError := `exec: "doesnotexists": executable file not found in ` + p
|
||||
if out != "" || exitCode != 127 || err == nil || err.Error() != expectedError {
|
||||
t.Fatalf("Expected command to output %s, got %s, %v with exitCode %v", expectedError, out, err, exitCode)
|
||||
}
|
||||
|
||||
out, exitCode, err = RunCommandWithOutput(wrongCmd)
|
||||
|
||||
if out != expected || exitCode != expectedExitCode || err == nil || !strings.Contains(err.Error(), "exit status "+strconv.Itoa(expectedExitCode)) {
|
||||
t.Fatalf("Expected command to output %s, got out:xxx%sxxx, err:%v with exitCode %v", expected, out, err, exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithStdoutStderr(t *testing.T) {
|
||||
echoHelloWorldCmd := exec.Command("echo", "hello", "world")
|
||||
stdout, stderr, exitCode, err := RunCommandWithStdoutStderr(echoHelloWorldCmd)
|
||||
expected := "hello world\n"
|
||||
if stdout != expected || stderr != "" || exitCode != 0 || err != nil {
|
||||
t.Fatalf("Expected command to output %s, got stdout:%s, stderr:%s, err:%v with exitCode %v", expected, stdout, stderr, err, exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithStdoutStderrError(t *testing.T) {
|
||||
p := "$PATH"
|
||||
if runtime.GOOS == "windows" {
|
||||
p = "%PATH%"
|
||||
}
|
||||
cmd := exec.Command("doesnotexists")
|
||||
stdout, stderr, exitCode, err := RunCommandWithStdoutStderr(cmd)
|
||||
expectedError := `exec: "doesnotexists": executable file not found in ` + p
|
||||
if stdout != "" || stderr != "" || exitCode != 127 || err == nil || err.Error() != expectedError {
|
||||
t.Fatalf("Expected command to output out:%s, stderr:%s, got stdout:%s, stderr:%s, err:%v with exitCode %v", "", "", stdout, stderr, err, exitCode)
|
||||
}
|
||||
|
||||
wrongLsCmd := exec.Command("ls", "-z")
|
||||
expected := `ls: invalid option -- 'z'
|
||||
Try 'ls --help' for more information.
|
||||
`
|
||||
|
||||
stdout, stderr, exitCode, err = RunCommandWithStdoutStderr(wrongLsCmd)
|
||||
if stdout != "" && stderr != expected || exitCode != 2 || err == nil || err.Error() != "exit status 2" {
|
||||
t.Fatalf("Expected command to output out:%s, stderr:%s, got stdout:%s, stderr:%s, err:%v with exitCode %v", "", expectedError, stdout, stderr, err, exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithOutputForDurationFinished(t *testing.T) {
|
||||
// TODO Windows: Port this test
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Needs porting to Windows")
|
||||
}
|
||||
|
||||
cmd := exec.Command("ls")
|
||||
out, exitCode, timedOut, err := RunCommandWithOutputForDuration(cmd, 50*time.Millisecond)
|
||||
if out == "" || exitCode != 0 || timedOut || err != nil {
|
||||
t.Fatalf("Expected the command to run for less 50 milliseconds and thus not time out, but did not : out:[%s], exitCode:[%d], timedOut:[%v], err:[%v]", out, exitCode, timedOut, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithOutputForDurationKilled(t *testing.T) {
|
||||
// TODO Windows: Port this test
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Needs porting to Windows")
|
||||
}
|
||||
cmd := exec.Command("sh", "-c", "while true ; do echo 1 ; sleep .1 ; done")
|
||||
out, exitCode, timedOut, err := RunCommandWithOutputForDuration(cmd, 500*time.Millisecond)
|
||||
ones := strings.Split(out, "\n")
|
||||
if len(ones) != 6 || exitCode != 0 || !timedOut || err != nil {
|
||||
t.Fatalf("Expected the command to run for 500 milliseconds (and thus print six lines (five with 1, one empty) and time out, but did not : out:[%s], exitCode:%d, timedOut:%v, err:%v", out, exitCode, timedOut, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithOutputForDurationErrors(t *testing.T) {
|
||||
cmd := exec.Command("ls")
|
||||
cmd.Stdout = os.Stdout
|
||||
if _, _, _, err := RunCommandWithOutputForDuration(cmd, 1*time.Millisecond); err == nil || err.Error() != "cmd.Stdout already set" {
|
||||
t.Fatalf("Expected an error as cmd.Stdout was already set, did not (err:%s).", err)
|
||||
}
|
||||
cmd = exec.Command("ls")
|
||||
cmd.Stderr = os.Stderr
|
||||
if _, _, _, err := RunCommandWithOutputForDuration(cmd, 1*time.Millisecond); err == nil || err.Error() != "cmd.Stderr already set" {
|
||||
t.Fatalf("Expected an error as cmd.Stderr was already set, did not (err:%s).", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithOutputAndTimeoutFinished(t *testing.T) {
|
||||
// TODO Windows: Port this test
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Needs porting to Windows")
|
||||
}
|
||||
|
||||
cmd := exec.Command("ls")
|
||||
out, exitCode, err := RunCommandWithOutputAndTimeout(cmd, 50*time.Millisecond)
|
||||
if out == "" || exitCode != 0 || err != nil {
|
||||
t.Fatalf("Expected the command to run for less 50 milliseconds and thus not time out, but did not : out:[%s], exitCode:[%d], err:[%v]", out, exitCode, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithOutputAndTimeoutKilled(t *testing.T) {
|
||||
// TODO Windows: Port this test
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Needs porting to Windows")
|
||||
}
|
||||
|
||||
cmd := exec.Command("sh", "-c", "while true ; do echo 1 ; sleep .1 ; done")
|
||||
out, exitCode, err := RunCommandWithOutputAndTimeout(cmd, 500*time.Millisecond)
|
||||
ones := strings.Split(out, "\n")
|
||||
if len(ones) != 6 || exitCode != 0 || err == nil || err.Error() != "command timed out" {
|
||||
t.Fatalf("Expected the command to run for 500 milliseconds (and thus print six lines (five with 1, one empty) and time out with an error 'command timed out', but did not : out:[%s], exitCode:%d, err:%v", out, exitCode, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithOutputAndTimeoutErrors(t *testing.T) {
|
||||
cmd := exec.Command("ls")
|
||||
cmd.Stdout = os.Stdout
|
||||
if _, _, err := RunCommandWithOutputAndTimeout(cmd, 1*time.Millisecond); err == nil || err.Error() != "cmd.Stdout already set" {
|
||||
t.Fatalf("Expected an error as cmd.Stdout was already set, did not (err:%s).", err)
|
||||
}
|
||||
cmd = exec.Command("ls")
|
||||
cmd.Stderr = os.Stderr
|
||||
if _, _, err := RunCommandWithOutputAndTimeout(cmd, 1*time.Millisecond); err == nil || err.Error() != "cmd.Stderr already set" {
|
||||
t.Fatalf("Expected an error as cmd.Stderr was already set, did not (err:%s).", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommand(t *testing.T) {
|
||||
// TODO Windows: Port this test
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Needs porting to Windows")
|
||||
}
|
||||
|
||||
p := "$PATH"
|
||||
if runtime.GOOS == "windows" {
|
||||
p = "%PATH%"
|
||||
}
|
||||
lsCmd := exec.Command("ls")
|
||||
exitCode, err := RunCommand(lsCmd)
|
||||
if exitCode != 0 || err != nil {
|
||||
t.Fatalf("Expected runCommand to run the command successfully, got: exitCode:%d, err:%v", exitCode, err)
|
||||
}
|
||||
|
||||
var expectedError string
|
||||
|
||||
exitCode, err = RunCommand(exec.Command("doesnotexists"))
|
||||
expectedError = `exec: "doesnotexists": executable file not found in ` + p
|
||||
if exitCode != 127 || err == nil || err.Error() != expectedError {
|
||||
t.Fatalf("Expected runCommand to run the command successfully, got: exitCode:%d, err:%v", exitCode, err)
|
||||
}
|
||||
wrongLsCmd := exec.Command("ls", "-z")
|
||||
expected := 2
|
||||
expectedError = `exit status 2`
|
||||
exitCode, err = RunCommand(wrongLsCmd)
|
||||
if exitCode != expected || err == nil || err.Error() != expectedError {
|
||||
t.Fatalf("Expected runCommand to run the command successfully, got: exitCode:%d, err:%v", exitCode, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandPipelineWithOutputWithNotEnoughCmds(t *testing.T) {
|
||||
_, _, err := RunCommandPipelineWithOutput(exec.Command("ls"))
|
||||
expectedError := "pipeline does not have multiple cmds"
|
||||
|
|
Loading…
Add table
Reference in a new issue