Procházet zdrojové kódy

Merge pull request #25426 from dnephin/better-int-testing-cmd

Remove duplicate RunCommand functions used for integration-cli
Daniel Nephin před 9 roky
rodič
revize
b29558ed5d

+ 7 - 10
integration-cli/docker_api_test.go

@@ -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)
-
-	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)
-	}
+	result := icmd.RunCmd(icmd.Cmd{
+		Command: binaryWithArgs("-H="+server.URL[7:], "version"),
+		Env:     appendBaseEnv(false, "DOCKER_API_VERSION=xxx"),
+	})
+	c.Assert(result, icmd.Matches, icmd.Expected{Out: "API version:  xxx", ExitCode: 1})
+	c.Assert(svrVersion, check.Equals, "/vxxx/version", check.Commentf("%s", result.Compare(icmd.Success)))
 }
 
 func (s *DockerSuite) TestApiErrorJSON(c *check.C) {

+ 8 - 4
integration-cli/docker_cli_attach_test.go

@@ -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")
+	c.Assert(result, icmd.Matches, icmd.Expected{
+		Error:    "exit status 1",
+		ExitCode: 1,
+		Err:      "You cannot attach to a paused container, unpause it first",
+	})
 }

+ 5 - 2
integration-cli/docker_cli_attach_unix_test.go

@@ -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")
 	}

+ 13 - 14
integration-cli/docker_cli_build_test.go

@@ -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"
 )
@@ -240,7 +241,7 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementEnv(c *check.C) {
 
 	envResult := []string{}
 
-	if err = unmarshalJSON([]byte(res), &envResult); err != nil {
+	if err = json.Unmarshal([]byte(res), &envResult); err != nil {
 		c.Fatal(err)
 	}
 
@@ -297,7 +298,7 @@ func (s *DockerSuite) TestBuildHandleEscapes(c *check.C) {
 
 	res := inspectFieldJSON(c, name, "Config.Volumes")
 
-	if err = unmarshalJSON([]byte(res), &result); err != nil {
+	if err = json.Unmarshal([]byte(res), &result); err != nil {
 		c.Fatal(err)
 	}
 
@@ -320,7 +321,7 @@ func (s *DockerSuite) TestBuildHandleEscapes(c *check.C) {
 
 	res = inspectFieldJSON(c, name, "Config.Volumes")
 
-	if err = unmarshalJSON([]byte(res), &result); err != nil {
+	if err = json.Unmarshal([]byte(res), &result); err != nil {
 		c.Fatal(err)
 	}
 
@@ -347,7 +348,7 @@ func (s *DockerSuite) TestBuildHandleEscapes(c *check.C) {
 
 	res = inspectFieldJSON(c, name, "Config.Volumes")
 
-	if err = unmarshalJSON([]byte(res), &result); err != nil {
+	if err = json.Unmarshal([]byte(res), &result); err != nil {
 		c.Fatal(err)
 	}
 
@@ -1704,7 +1705,7 @@ func (s *DockerSuite) TestBuildWithVolumes(c *check.C) {
 	}
 	res := inspectFieldJSON(c, name, "Config.Volumes")
 
-	err = unmarshalJSON([]byte(res), &result)
+	err = json.Unmarshal([]byte(res), &result)
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -1833,9 +1834,9 @@ func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
 			ADD wc2 c:/wc2
 			WORKDIR c:/
 			RUN sh -c "[ $(cat c:/wc1) = 'hellowc1' ]"
-			RUN sh -c "[ $(cat c:/wc2) = 'worldwc2' ]"			
+			RUN sh -c "[ $(cat c:/wc2) = 'worldwc2' ]"
 
-			# Trailing slash on COPY/ADD, Windows-style path. 
+			# Trailing slash on COPY/ADD, Windows-style path.
 			WORKDIR /wd1
 			COPY wd1 c:/wd1/
 			WORKDIR /wd2
@@ -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, ".")
+		c.Assert(result, icmd.Matches, icmd.Expected{
+			Err:      "must be within the build context",
+			ExitCode: 1,
+		})
 		deleteImages(name)
 	}
 

+ 2 - 1
integration-cli/docker_cli_build_unix_test.go

@@ -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)
 	}
 

+ 12 - 14
integration-cli/docker_cli_daemon_test.go

@@ -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)
+	c.Assert(result, icmd.Matches, icmd.Success)
 
 	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)
+	t.Assert(result, icmd.Matches, icmd.Success)
 
 	// 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 {

+ 11 - 7
integration-cli/docker_cli_events_test.go

@@ -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,
+	})
+	c.Assert(result, icmd.Matches, 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")
+	c.Assert(result, icmd.Matches, icmd.Success)
 
 	out, _ = dockerCmd(c, "images", "-q", "--no-trunc", myImageName)
 	imageID := strings.TrimSpace(out)

+ 3 - 4
integration-cli/docker_cli_exec_test.go

@@ -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")
+	c.Assert(result, icmd.Matches, icmd.Expected{ExitCode: 23, Error: "exit status 23"})
 }
 
 func (s *DockerSuite) TestExecPausedContainer(c *check.C) {

+ 2 - 2
integration-cli/docker_cli_inspect_test.go

@@ -227,7 +227,7 @@ func (s *DockerSuite) TestInspectBindMountPoint(c *check.C) {
 	vol := inspectFieldJSON(c, "test", "Mounts")
 
 	var mp []types.MountPoint
-	err := unmarshalJSON([]byte(vol), &mp)
+	err := json.Unmarshal([]byte(vol), &mp)
 	c.Assert(err, checker.IsNil)
 
 	// check that there is only one mountpoint
@@ -253,7 +253,7 @@ func (s *DockerSuite) TestInspectNamedMountPoint(c *check.C) {
 	vol := inspectFieldJSON(c, "test", "Mounts")
 
 	var mp []types.MountPoint
-	err := unmarshalJSON([]byte(vol), &mp)
+	err := json.Unmarshal([]byte(vol), &mp)
 	c.Assert(err, checker.IsNil)
 
 	// check that there is only one mountpoint

+ 3 - 2
integration-cli/docker_cli_links_test.go

@@ -1,6 +1,7 @@
 package main
 
 import (
+	"encoding/json"
 	"fmt"
 	"regexp"
 	"strings"
@@ -97,7 +98,7 @@ func (s *DockerSuite) TestLinksInspectLinksStarted(c *check.C) {
 	dockerCmd(c, "run", "-d", "--name", "testinspectlink", "--link", "container1:alias1", "--link", "container2:alias2", "busybox", "top")
 	links := inspectFieldJSON(c, "testinspectlink", "HostConfig.Links")
 
-	err := unmarshalJSON([]byte(links), &result)
+	err := json.Unmarshal([]byte(links), &result)
 	c.Assert(err, checker.IsNil)
 
 	output := convertSliceOfStringsToMap(result)
@@ -116,7 +117,7 @@ func (s *DockerSuite) TestLinksInspectLinksStopped(c *check.C) {
 	dockerCmd(c, "run", "-d", "--name", "testinspectlink", "--link", "container1:alias1", "--link", "container2:alias2", "busybox", "true")
 	links := inspectFieldJSON(c, "testinspectlink", "HostConfig.Links")
 
-	err := unmarshalJSON([]byte(links), &result)
+	err := json.Unmarshal([]byte(links), &result)
 	c.Assert(err, checker.IsNil)
 
 	output := convertSliceOfStringsToMap(result)

+ 19 - 12
integration-cli/docker_cli_network_unix_test.go

@@ -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")
+	c.Assert(result, icmd.Matches, icmd.Success)
+
 	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")
+	c.Assert(result, icmd.Matches, 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")
+	c.Assert(result, icmd.Matches, icmd.Expected{
+		ExitCode: 1,
+		Err:      "Error: No such network: nonexistent",
+		Out:      "[]",
+	})
 }
 
 func (s *DockerSuite) TestDockerInspectNetworkWithContainerName(c *check.C) {

+ 6 - 2
integration-cli/docker_cli_ps_test.go

@@ -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")
+	c.Assert(result, icmd.Matches, icmd.Expected{
+		ExitCode: 1,
+		Err:      "Unrecognised filter value for status",
+	})
 
 	// Windows doesn't support pausing of containers
 	if daemonPlatform != "windows" {

+ 6 - 3
integration-cli/docker_cli_rename_test.go

@@ -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")
+	c.Assert(result, icmd.Matches, icmd.Expected{
+		ExitCode: 1,
+		Err:      "No such container, image or task: first_name",
+	})
 }
 
 func (s *DockerSuite) TestRenameInvalidName(c *check.C) {

+ 13 - 25
integration-cli/docker_cli_run_test.go

@@ -3,6 +3,7 @@ package main
 import (
 	"bufio"
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"net"
@@ -20,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"
@@ -1855,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.Success)
 	}()
-	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)
+	c.Assert(result, icmd.Matches, icmd.Expected{ExitCode: 11})
 }
 
 // Test for #2267
@@ -2397,7 +2385,7 @@ func (s *DockerSuite) TestRunAllowPortRangeThroughExpose(c *check.C) {
 	id := strings.TrimSpace(out)
 	portstr := inspectFieldJSON(c, id, "NetworkSettings.Ports")
 	var ports nat.PortMap
-	if err := unmarshalJSON([]byte(portstr), &ports); err != nil {
+	if err := json.Unmarshal([]byte(portstr), &ports); err != nil {
 		c.Fatal(err)
 	}
 	for port, binding := range ports {
@@ -2827,7 +2815,7 @@ func (s *DockerSuite) TestRunAllowPortRangeThroughPublish(c *check.C) {
 	portstr := inspectFieldJSON(c, id, "NetworkSettings.Ports")
 
 	var ports nat.PortMap
-	err := unmarshalJSON([]byte(portstr), &ports)
+	err := json.Unmarshal([]byte(portstr), &ports)
 	c.Assert(err, checker.IsNil, check.Commentf("failed to unmarshal: %v", portstr))
 	for port, binding := range ports {
 		portnum, _ := strconv.Atoi(strings.Split(string(port), "/")[0])

+ 9 - 4
integration-cli/docker_cli_volume_test.go

@@ -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")
+	c.Assert(result, icmd.Matches, 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")
 }
 

+ 11 - 9
integration-cli/docker_experimental_network_test.go

@@ -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")
+	c.Assert(result, icmd.Matches, 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, icmd.Matches, 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)
 }
 
@@ -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, icmd.Matches, 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)
 }
 

+ 62 - 69
integration-cli/docker_utils.go

@@ -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"
@@ -228,16 +228,9 @@ func readBody(b io.ReadCloser) ([]byte, error) {
 	return ioutil.ReadAll(b)
 }
 
-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
+func deleteContainer(container ...string) error {
+	result := icmd.RunCommand(dockerBinary, append([]string{"rm", "-fv"}, container...)...)
+	return result.Compare(icmd.Success)
 }
 
 func getAllContainers() (string, error) {
@@ -256,13 +249,15 @@ func deleteAllContainers() error {
 		fmt.Println(containers)
 		return err
 	}
+	if containers == "" {
+		return nil
+	}
 
-	if containers != "" {
-		if err = deleteContainer(containers); err != nil {
-			return err
-		}
+	err = deleteContainer(strings.Split(strings.TrimSpace(containers), "\n")...)
+	if err != nil {
+		fmt.Println(err.Error())
 	}
-	return nil
+	return err
 }
 
 func deleteAllNetworks() error {
@@ -398,13 +393,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 +417,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 +441,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, result.Compare(icmd.Success)
 	}
-	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 {
+		c.Assert(result, icmd.Matches, icmd.Success)
+	}
+	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...)
+	c.Assert(result, icmd.Matches, icmd.Success)
+	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 +491,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
@@ -867,7 +865,7 @@ var errMountNotFound = errors.New("mount point not found")
 
 func inspectMountPointJSON(j, destination string) (types.MountPoint, error) {
 	var mp []types.MountPoint
-	if err := unmarshalJSON([]byte(j), &mp); err != nil {
+	if err := json.Unmarshal([]byte(j), &mp); err != nil {
 		return types.MountPoint{}, err
 	}
 
@@ -1355,17 +1353,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
-	}
-
-	if err := waitRun(contID); 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
 	}
-
-	return nil
+	return waitRun(contID)
 }
 
 // waitRun will wait for the specified container to be running, maximum 5 seconds.
@@ -1391,22 +1384,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
 		}

+ 22 - 28
integration-cli/utils.go

@@ -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,46 +17,39 @@ 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) {
 	return integration.RunCommandPipelineWithOutput(cmds...)
 }
 
-func unmarshalJSON(data []byte, result interface{}) error {
-	return integration.UnmarshalJSON(data, result)
-}
-
 func convertSliceOfStringsToMap(input []string) map[string]struct{} {
 	return integration.ConvertSliceOfStringsToMap(input)
 }

+ 302 - 0
pkg/integration/cmd/command.go

@@ -0,0 +1,302 @@
+package cmd
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"syscall"
+	"time"
+
+	"github.com/go-check/check"
+)
+
+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) {
+	err := r.Compare(exp)
+	if err == nil {
+		return
+	}
+
+	_, file, line, _ := runtime.Caller(1)
+	t.Fatalf("at %s:%d\n%s", filepath.Base(file), line, err.Error())
+}
+
+// Compare returns an formatted error with the command, stdout, stderr, exit
+// code, and any failed expectations
+func (r *Result) Compare(exp Expected) error {
+	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 nil
+	}
+	return fmt.Errorf("%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
+}
+
+// Success is the default expected result
+var Success = Expected{}
+
+// 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)
+}
+
+type matches struct{}
+
+// Info returns the CheckerInfo
+func (m *matches) Info() *check.CheckerInfo {
+	return &check.CheckerInfo{
+		Name:   "CommandMatches",
+		Params: []string{"result", "expected"},
+	}
+}
+
+// Check compares a result against the expected
+func (m *matches) Check(params []interface{}, names []string) (bool, string) {
+	result, ok := params[0].(*Result)
+	if !ok {
+		return false, fmt.Sprintf("result must be a *Result, not %T", params[0])
+	}
+	expected, ok := params[1].(Expected)
+	if !ok {
+		return false, fmt.Sprintf("expected must be an Expected, not %T", params[1])
+	}
+
+	err := result.Compare(expected)
+	if err == nil {
+		return true, ""
+	}
+	return false, err.Error()
+}
+
+// Matches is a gocheck.Checker for comparing a Result against an Expected
+var Matches = &matches{}
+
+// Cmd contains the arguments and options for a process to run as part of a test
+// suite.
+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 - 0
pkg/integration/cmd/command_test.go

@@ -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",
+	})
+}

+ 0 - 78
pkg/integration/dockerCmd_utils.go

@@ -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
-}

+ 0 - 405
pkg/integration/dockerCmd_utils_test.go

@@ -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)
-}

+ 6 - 140
pkg/integration/utils.go

@@ -2,8 +2,6 @@ package integration
 
 import (
 	"archive/tar"
-	"bytes"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -15,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 {
@@ -59,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
@@ -206,16 +83,7 @@ func RunCommandPipelineWithOutput(cmds ...*exec.Cmd) (output string, exitCode in
 	}
 
 	// wait on last cmd
-	return RunCommandWithOutput(cmds[len(cmds)-1])
-}
-
-// UnmarshalJSON deserialize a JSON in the given interface.
-func UnmarshalJSON(data []byte, result interface{}) error {
-	if err := json.Unmarshal(data, result); err != nil {
-		return err
-	}
-
-	return nil
+	return runCommandWithOutput(cmds[len(cmds)-1])
 }
 
 // ConvertSliceOfStringsToMap converts a slices of string in a map
@@ -351,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
 }

+ 0 - 213
pkg/integration/utils_test.go

@@ -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"
@@ -294,21 +96,6 @@ func TestRunCommandPipelineWithOutput(t *testing.T) {
 	}
 }
 
-// Simple simple test as it is just a passthrough for json.Unmarshal
-func TestUnmarshalJSON(t *testing.T) {
-	emptyResult := struct{}{}
-	if err := UnmarshalJSON([]byte(""), &emptyResult); err == nil {
-		t.Fatalf("Expected an error, got nothing")
-	}
-	result := struct{ Name string }{}
-	if err := UnmarshalJSON([]byte(`{"name": "name"}`), &result); err != nil {
-		t.Fatal(err)
-	}
-	if result.Name != "name" {
-		t.Fatalf("Expected result.name to be 'name', was '%s'", result.Name)
-	}
-}
-
 func TestConvertSliceOfStringsToMap(t *testing.T) {
 	input := []string{"a", "b"}
 	actual := ConvertSliceOfStringsToMap(input)