Browse code

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>

Daniel Nephin authored on 2016/08/05 01:57:34
Showing 22 changed files
... ...
@@ -5,13 +5,13 @@ import (
5 5
 	"net/http"
6 6
 	"net/http/httptest"
7 7
 	"net/http/httputil"
8
-	"os/exec"
9 8
 	"strconv"
10 9
 	"strings"
11 10
 	"time"
12 11
 
13 12
 	"github.com/docker/docker/api"
14 13
 	"github.com/docker/docker/pkg/integration/checker"
14
+	icmd "github.com/docker/docker/pkg/integration/cmd"
15 15
 	"github.com/go-check/check"
16 16
 )
17 17
 
... ...
@@ -89,15 +89,12 @@ func (s *DockerSuite) TestApiDockerApiVersion(c *check.C) {
89 89
 	defer server.Close()
90 90
 
91 91
 	// Test using the env var first
92
-	cmd := exec.Command(dockerBinary, "-H="+server.URL[7:], "version")
93
-	cmd.Env = appendBaseEnv(false, "DOCKER_API_VERSION=xxx")
94
-	out, _, _ := runCommandWithOutput(cmd)
95
-
92
+	result := icmd.RunCmd(icmd.Cmd{
93
+		Command: binaryWithArgs([]string{"-H", server.URL[7:], "version"}),
94
+		Env:     []string{"DOCKER_API_VERSION=xxx"},
95
+	})
96
+	result.Assert(c, icmd.Expected{Out: "API version:  xxx", ExitCode: 1})
96 97
 	c.Assert(svrVersion, check.Equals, "/vxxx/version")
97
-
98
-	if !strings.Contains(out, "API version:  xxx") {
99
-		c.Fatalf("Out didn't have 'xxx' for the API version, had:\n%s", out)
100
-	}
101 98
 }
102 99
 
103 100
 func (s *DockerSuite) TestApiErrorJSON(c *check.C) {
... ...
@@ -10,7 +10,7 @@ import (
10 10
 	"sync"
11 11
 	"time"
12 12
 
13
-	"github.com/docker/docker/pkg/integration/checker"
13
+	icmd "github.com/docker/docker/pkg/integration/cmd"
14 14
 	"github.com/go-check/check"
15 15
 )
16 16
 
... ...
@@ -161,7 +161,11 @@ func (s *DockerSuite) TestAttachPausedContainer(c *check.C) {
161 161
 	defer unpauseAllContainers()
162 162
 	dockerCmd(c, "run", "-d", "--name=test", "busybox", "top")
163 163
 	dockerCmd(c, "pause", "test")
164
-	out, _, err := dockerCmdWithError("attach", "test")
165
-	c.Assert(err, checker.NotNil, check.Commentf(out))
166
-	c.Assert(out, checker.Contains, "You cannot attach to a paused container, unpause it first")
164
+
165
+	result := dockerCmdWithResult("attach", "test")
166
+	result.Assert(c, icmd.Expected{
167
+		Error:    "exit status 1",
168
+		ExitCode: 1,
169
+		Err:      "You cannot attach to a paused container, unpause it first",
170
+	})
167 171
 }
... ...
@@ -56,7 +56,6 @@ func (s *DockerSuite) TestAttachClosedOnContainerStop(c *check.C) {
56 56
 }
57 57
 
58 58
 func (s *DockerSuite) TestAttachAfterDetach(c *check.C) {
59
-
60 59
 	name := "detachtest"
61 60
 
62 61
 	cpty, tty, err := pty.Open()
... ...
@@ -80,7 +79,11 @@ func (s *DockerSuite) TestAttachAfterDetach(c *check.C) {
80 80
 
81 81
 	select {
82 82
 	case err := <-errChan:
83
-		c.Assert(err, check.IsNil)
83
+		if err != nil {
84
+			buff := make([]byte, 200)
85
+			tty.Read(buff)
86
+			c.Fatalf("%s: %s", err, buff)
87
+		}
84 88
 	case <-time.After(5 * time.Second):
85 89
 		c.Fatal("timeout while detaching")
86 90
 	}
... ...
@@ -20,6 +20,7 @@ import (
20 20
 	"github.com/docker/docker/builder/dockerfile/command"
21 21
 	"github.com/docker/docker/pkg/archive"
22 22
 	"github.com/docker/docker/pkg/integration/checker"
23
+	icmd "github.com/docker/docker/pkg/integration/cmd"
23 24
 	"github.com/docker/docker/pkg/stringutils"
24 25
 	"github.com/go-check/check"
25 26
 )
... ...
@@ -5063,13 +5064,11 @@ func (s *DockerSuite) TestBuildDockerfileOutsideContext(c *check.C) {
5063 5063
 		filepath.Join(ctx, "dockerfile1"),
5064 5064
 		filepath.Join(ctx, "dockerfile2"),
5065 5065
 	} {
5066
-		out, _, err := dockerCmdWithError("build", "-t", name, "--no-cache", "-f", dockerfilePath, ".")
5067
-		if err == nil {
5068
-			c.Fatalf("Expected error with %s. Out: %s", dockerfilePath, out)
5069
-		}
5070
-		if !strings.Contains(out, "must be within the build context") && !strings.Contains(out, "Cannot locate Dockerfile") {
5071
-			c.Fatalf("Unexpected error with %s. Out: %s", dockerfilePath, out)
5072
-		}
5066
+		result := dockerCmdWithResult("build", "-t", name, "--no-cache", "-f", dockerfilePath, ".")
5067
+		result.Assert(c, icmd.Expected{
5068
+			Err:      "must be within the build context",
5069
+			ExitCode: 1,
5070
+		})
5073 5071
 		deleteImages(name)
5074 5072
 	}
5075 5073
 
... ...
@@ -14,6 +14,7 @@ import (
14 14
 	"strings"
15 15
 	"time"
16 16
 
17
+	"github.com/docker/docker/pkg/integration"
17 18
 	"github.com/docker/docker/pkg/integration/checker"
18 19
 	"github.com/docker/go-units"
19 20
 	"github.com/go-check/check"
... ...
@@ -193,7 +194,7 @@ func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) {
193 193
 	}
194 194
 
195 195
 	// Get the exit status of `docker build`, check it exited because killed.
196
-	if err := buildCmd.Wait(); err != nil && !isKilled(err) {
196
+	if err := buildCmd.Wait(); err != nil && !integration.IsKilled(err) {
197 197
 		c.Fatalf("wait failed during build run: %T %s", err, err)
198 198
 	}
199 199
 
... ...
@@ -21,6 +21,7 @@ import (
21 21
 	"time"
22 22
 
23 23
 	"github.com/docker/docker/pkg/integration/checker"
24
+	icmd "github.com/docker/docker/pkg/integration/cmd"
24 25
 	"github.com/docker/docker/pkg/mount"
25 26
 	"github.com/docker/go-units"
26 27
 	"github.com/docker/libnetwork/iptables"
... ...
@@ -908,9 +909,8 @@ func (s *DockerDaemonSuite) TestDaemonDefaultNetworkInvalidClusterConfig(c *chec
908 908
 	c.Assert(err, checker.IsNil)
909 909
 
910 910
 	// Start daemon with docker0 bridge
911
-	ifconfigCmd := exec.Command("ifconfig", defaultNetworkBridge)
912
-	_, err = runCommand(ifconfigCmd)
913
-	c.Assert(err, check.IsNil)
911
+	result := icmd.RunCommand("ifconfig", defaultNetworkBridge)
912
+	result.Assert(c, icmd.Expected{})
914 913
 
915 914
 	err = d.Restart(fmt.Sprintf("--cluster-store=%s", discoveryBackend))
916 915
 	c.Assert(err, checker.IsNil)
... ...
@@ -2235,7 +2235,6 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *che
2235 2235
 
2236 2236
 	pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", cid)
2237 2237
 	t.Assert(err, check.IsNil)
2238
-	pid = strings.TrimSpace(pid)
2239 2238
 
2240 2239
 	// pause the container
2241 2240
 	if _, err := s.d.Cmd("pause", cid); err != nil {
... ...
@@ -2248,19 +2247,18 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *che
2248 2248
 	}
2249 2249
 
2250 2250
 	// resume the container
2251
-	runCmd := exec.Command(ctrBinary, "--address", "unix:///var/run/docker/libcontainerd/docker-containerd.sock", "containers", "resume", cid)
2252
-	if out, ec, err := runCommandWithOutput(runCmd); err != nil {
2253
-		t.Fatalf("Failed to run ctr, ExitCode: %d, err: '%v' output: '%s' cid: '%s'\n", ec, err, out, cid)
2254
-	}
2251
+	result := icmd.RunCommand(
2252
+		ctrBinary,
2253
+		"--address", "unix:///var/run/docker/libcontainerd/docker-containerd.sock",
2254
+		"containers", "resume", cid)
2255
+	result.Assert(t, icmd.Expected{})
2255 2256
 
2256 2257
 	// Give time to containerd to process the command if we don't
2257 2258
 	// the resume event might be received after we do the inspect
2258
-	pidCmd := exec.Command("kill", "-0", pid)
2259
-	_, ec, _ := runCommandWithOutput(pidCmd)
2260
-	for ec == 0 {
2261
-		time.Sleep(1 * time.Second)
2262
-		_, ec, _ = runCommandWithOutput(pidCmd)
2263
-	}
2259
+	waitAndAssert(t, defaultReconciliationTimeout, func(*check.C) (interface{}, check.CommentInterface) {
2260
+		result := icmd.RunCommand("kill", "-0", strings.TrimSpace(pid))
2261
+		return result.ExitCode, nil
2262
+	}, checker.Equals, 0)
2264 2263
 
2265 2264
 	// restart the daemon
2266 2265
 	if err := s.d.Start("--live-restore"); err != nil {
... ...
@@ -13,6 +13,7 @@ import (
13 13
 
14 14
 	"github.com/docker/docker/daemon/events/testutils"
15 15
 	"github.com/docker/docker/pkg/integration/checker"
16
+	icmd "github.com/docker/docker/pkg/integration/cmd"
16 17
 	"github.com/go-check/check"
17 18
 )
18 19
 
... ...
@@ -57,11 +58,14 @@ func (s *DockerSuite) TestEventsUntag(c *check.C) {
57 57
 	dockerCmd(c, "tag", image, "utest:tag2")
58 58
 	dockerCmd(c, "rmi", "utest:tag1")
59 59
 	dockerCmd(c, "rmi", "utest:tag2")
60
-	eventsCmd := exec.Command(dockerBinary, "events", "--since=1")
61
-	out, exitCode, _, err := runCommandWithOutputForDuration(eventsCmd, time.Duration(time.Millisecond*2500))
62
-	c.Assert(err, checker.IsNil)
63
-	c.Assert(exitCode, checker.Equals, 0, check.Commentf("Failed to get events"))
64
-	events := strings.Split(out, "\n")
60
+
61
+	result := icmd.RunCmd(icmd.Cmd{
62
+		Command: []string{dockerBinary, "events", "--since=1"},
63
+		Timeout: time.Millisecond * 2500,
64
+	})
65
+	result.Assert(c, icmd.Expected{Timeout: true})
66
+
67
+	events := strings.Split(result.Stdout(), "\n")
65 68
 	nEvents := len(events)
66 69
 	// The last element after the split above will be an empty string, so we
67 70
 	// get the two elements before the last, which are the untags we're
... ...
@@ -275,8 +279,8 @@ func (s *DockerSuite) TestEventsImageLoad(c *check.C) {
275 275
 	c.Assert(noImageID, checker.Equals, "", check.Commentf("Should not have any image"))
276 276
 	dockerCmd(c, "load", "-i", "saveimg.tar")
277 277
 
278
-	cmd := exec.Command("rm", "-rf", "saveimg.tar")
279
-	runCommand(cmd)
278
+	result := icmd.RunCommand("rm", "-rf", "saveimg.tar")
279
+	result.Assert(c, icmd.Expected{})
280 280
 
281 281
 	out, _ = dockerCmd(c, "images", "-q", "--no-trunc", myImageName)
282 282
 	imageID := strings.TrimSpace(out)
... ...
@@ -16,6 +16,7 @@ import (
16 16
 	"time"
17 17
 
18 18
 	"github.com/docker/docker/pkg/integration/checker"
19
+	icmd "github.com/docker/docker/pkg/integration/cmd"
19 20
 	"github.com/go-check/check"
20 21
 )
21 22
 
... ...
@@ -122,10 +123,8 @@ func (s *DockerSuite) TestExecEnv(c *check.C) {
122 122
 func (s *DockerSuite) TestExecExitStatus(c *check.C) {
123 123
 	runSleepingContainer(c, "-d", "--name", "top")
124 124
 
125
-	// Test normal (non-detached) case first
126
-	cmd := exec.Command(dockerBinary, "exec", "top", "sh", "-c", "exit 23")
127
-	ec, _ := runCommand(cmd)
128
-	c.Assert(ec, checker.Equals, 23)
125
+	result := icmd.RunCommand(dockerBinary, "exec", "top", "sh", "-c", "exit 23")
126
+	result.Assert(c, icmd.Expected{ExitCode: 23, Error: "exit status 23"})
129 127
 }
130 128
 
131 129
 func (s *DockerSuite) TestExecPausedContainer(c *check.C) {
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	"time"
16 16
 
17 17
 	"github.com/docker/docker/pkg/integration/checker"
18
+	icmd "github.com/docker/docker/pkg/integration/cmd"
18 19
 	"github.com/docker/docker/pkg/stringid"
19 20
 	"github.com/docker/docker/runconfig"
20 21
 	"github.com/docker/engine-api/types"
... ...
@@ -477,27 +478,33 @@ func (s *DockerSuite) TestDockerNetworkInspectWithID(c *check.C) {
477 477
 }
478 478
 
479 479
 func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *check.C) {
480
-	out, _ := dockerCmd(c, "network", "inspect", "host", "none")
480
+	result := dockerCmdWithResult("network", "inspect", "host", "none")
481
+	result.Assert(c, icmd.Expected{})
482
+
481 483
 	networkResources := []types.NetworkResource{}
482
-	err := json.Unmarshal([]byte(out), &networkResources)
484
+	err := json.Unmarshal([]byte(result.Stdout()), &networkResources)
483 485
 	c.Assert(err, check.IsNil)
484 486
 	c.Assert(networkResources, checker.HasLen, 2)
485 487
 
486 488
 	// Should print an error, return an exitCode 1 *but* should print the host network
487
-	out, exitCode, err := dockerCmdWithError("network", "inspect", "host", "nonexistent")
488
-	c.Assert(err, checker.NotNil)
489
-	c.Assert(exitCode, checker.Equals, 1)
490
-	c.Assert(out, checker.Contains, "Error: No such network: nonexistent")
489
+	result = dockerCmdWithResult("network", "inspect", "host", "nonexistent")
490
+	result.Assert(c, icmd.Expected{
491
+		ExitCode: 1,
492
+		Err:      "Error: No such network: nonexistent",
493
+		Out:      "host",
494
+	})
495
+
491 496
 	networkResources = []types.NetworkResource{}
492
-	inspectOut := strings.SplitN(out, "\nError: No such network: nonexistent\n", 2)[0]
493
-	err = json.Unmarshal([]byte(inspectOut), &networkResources)
497
+	err = json.Unmarshal([]byte(result.Stdout()), &networkResources)
494 498
 	c.Assert(networkResources, checker.HasLen, 1)
495 499
 
496 500
 	// Should print an error and return an exitCode, nothing else
497
-	out, exitCode, err = dockerCmdWithError("network", "inspect", "nonexistent")
498
-	c.Assert(err, checker.NotNil)
499
-	c.Assert(exitCode, checker.Equals, 1)
500
-	c.Assert(out, checker.Contains, "Error: No such network: nonexistent")
501
+	result = dockerCmdWithResult("network", "inspect", "nonexistent")
502
+	result.Assert(c, icmd.Expected{
503
+		ExitCode: 1,
504
+		Err:      "Error: No such network: nonexistent",
505
+		Out:      "[]",
506
+	})
501 507
 }
502 508
 
503 509
 func (s *DockerSuite) TestDockerInspectNetworkWithContainerName(c *check.C) {
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"time"
13 13
 
14 14
 	"github.com/docker/docker/pkg/integration/checker"
15
+	icmd "github.com/docker/docker/pkg/integration/cmd"
15 16
 	"github.com/docker/docker/pkg/stringid"
16 17
 	"github.com/go-check/check"
17 18
 )
... ...
@@ -204,8 +205,11 @@ func (s *DockerSuite) TestPsListContainersFilterStatus(c *check.C) {
204 204
 	containerOut = strings.TrimSpace(out)
205 205
 	c.Assert(containerOut, checker.Equals, secondID)
206 206
 
207
-	out, _, _ = dockerCmdWithTimeout(time.Second*60, "ps", "-a", "-q", "--filter=status=rubbish")
208
-	c.Assert(out, checker.Contains, "Unrecognised filter value for status", check.Commentf("Expected error response due to invalid status filter output: %q", out))
207
+	result := dockerCmdWithTimeout(time.Second*60, "ps", "-a", "-q", "--filter=status=rubbish")
208
+	result.Assert(c, icmd.Expected{
209
+		ExitCode: 1,
210
+		Err:      "Unrecognised filter value for status",
211
+	})
209 212
 
210 213
 	// Windows doesn't support pausing of containers
211 214
 	if daemonPlatform != "windows" {
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"strings"
5 5
 
6 6
 	"github.com/docker/docker/pkg/integration/checker"
7
+	icmd "github.com/docker/docker/pkg/integration/cmd"
7 8
 	"github.com/docker/docker/pkg/stringid"
8 9
 	"github.com/go-check/check"
9 10
 )
... ...
@@ -61,9 +62,11 @@ func (s *DockerSuite) TestRenameCheckNames(c *check.C) {
61 61
 	name := inspectField(c, newName, "Name")
62 62
 	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))
63 63
 
64
-	name, err := inspectFieldWithError("first_name", "Name")
65
-	c.Assert(err, checker.NotNil, check.Commentf(name))
66
-	c.Assert(err.Error(), checker.Contains, "No such container, image or task: first_name")
64
+	result := dockerCmdWithResult("inspect", "-f={{.Name}}", "first_name")
65
+	result.Assert(c, icmd.Expected{
66
+		ExitCode: 1,
67
+		Err:      "No such container, image or task: first_name",
68
+	})
67 69
 }
68 70
 
69 71
 func (s *DockerSuite) TestRenameInvalidName(c *check.C) {
... ...
@@ -21,6 +21,7 @@ import (
21 21
 	"time"
22 22
 
23 23
 	"github.com/docker/docker/pkg/integration/checker"
24
+	icmd "github.com/docker/docker/pkg/integration/cmd"
24 25
 	"github.com/docker/docker/pkg/mount"
25 26
 	"github.com/docker/docker/pkg/stringid"
26 27
 	"github.com/docker/docker/pkg/stringutils"
... ...
@@ -1856,32 +1857,18 @@ func (s *DockerSuite) TestRunExitOnStdinClose(c *check.C) {
1856 1856
 // Test run -i --restart xxx doesn't hang
1857 1857
 func (s *DockerSuite) TestRunInteractiveWithRestartPolicy(c *check.C) {
1858 1858
 	name := "test-inter-restart"
1859
-	runCmd := exec.Command(dockerBinary, "run", "-i", "--name", name, "--restart=always", "busybox", "sh")
1860 1859
 
1861
-	stdin, err := runCmd.StdinPipe()
1862
-	c.Assert(err, checker.IsNil)
1863
-
1864
-	err = runCmd.Start()
1865
-	c.Assert(err, checker.IsNil)
1866
-	c.Assert(waitRun(name), check.IsNil)
1867
-
1868
-	_, err = stdin.Write([]byte("exit 11\n"))
1869
-	c.Assert(err, checker.IsNil)
1870
-
1871
-	finish := make(chan error)
1872
-	go func() {
1873
-		finish <- runCmd.Wait()
1874
-		close(finish)
1860
+	result := icmd.StartCmd(icmd.Cmd{
1861
+		Command: []string{dockerBinary, "run", "-i", "--name", name, "--restart=always", "busybox", "sh"},
1862
+		Stdin:   bytes.NewBufferString("exit 11"),
1863
+	})
1864
+	c.Assert(result.Error, checker.IsNil)
1865
+	defer func() {
1866
+		dockerCmdWithResult("stop", name).Assert(c, icmd.Expected{})
1875 1867
 	}()
1876
-	delay := 10 * time.Second
1877
-	select {
1878
-	case <-finish:
1879
-	case <-time.After(delay):
1880
-		c.Fatal("run -i --restart hangs")
1881
-	}
1882 1868
 
1883
-	c.Assert(waitRun(name), check.IsNil)
1884
-	dockerCmd(c, "stop", name)
1869
+	result = icmd.WaitOnCmd(10*time.Second, result)
1870
+	result.Assert(c, icmd.Expected{ExitCode: 11})
1885 1871
 }
1886 1872
 
1887 1873
 // Test for #2267
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"strings"
9 9
 
10 10
 	"github.com/docker/docker/pkg/integration/checker"
11
+	icmd "github.com/docker/docker/pkg/integration/cmd"
11 12
 	"github.com/go-check/check"
12 13
 )
13 14
 
... ...
@@ -55,14 +56,18 @@ func (s *DockerSuite) TestVolumeCliInspectMulti(c *check.C) {
55 55
 	dockerCmd(c, "volume", "create", "--name", "test2")
56 56
 	dockerCmd(c, "volume", "create", "--name", "not-shown")
57 57
 
58
-	out, _, err := dockerCmdWithError("volume", "inspect", "--format='{{ .Name }}'", "test1", "test2", "doesntexist", "not-shown")
59
-	c.Assert(err, checker.NotNil)
58
+	result := dockerCmdWithResult("volume", "inspect", "--format={{ .Name }}", "test1", "test2", "doesntexist", "not-shown")
59
+	result.Assert(c, icmd.Expected{
60
+		ExitCode: 1,
61
+		Err:      "No such volume: doesntexist",
62
+	})
63
+
64
+	out := result.Stdout()
60 65
 	outArr := strings.Split(strings.TrimSpace(out), "\n")
61
-	c.Assert(len(outArr), check.Equals, 3, check.Commentf("\n%s", out))
66
+	c.Assert(len(outArr), check.Equals, 2, check.Commentf("\n%s", out))
62 67
 
63 68
 	c.Assert(out, checker.Contains, "test1")
64 69
 	c.Assert(out, checker.Contains, "test2")
65
-	c.Assert(out, checker.Contains, "Error: No such volume: doesntexist")
66 70
 	c.Assert(out, checker.Not(checker.Contains), "not-shown")
67 71
 }
68 72
 
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"time"
9 9
 
10 10
 	"github.com/docker/docker/pkg/integration/checker"
11
+	icmd "github.com/docker/docker/pkg/integration/cmd"
11 12
 	"github.com/docker/docker/pkg/parsers/kernel"
12 13
 	"github.com/go-check/check"
13 14
 )
... ...
@@ -401,10 +402,11 @@ func (s *DockerSuite) TestDockerNetworkMacVlanBridgeInternalMode(c *check.C) {
401 401
 	c.Assert(waitRun("second"), check.IsNil)
402 402
 
403 403
 	// access outside of the network should fail
404
-	_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
405
-	c.Assert(err, check.NotNil)
404
+	result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
405
+	result.Assert(c, icmd.Expected{Timeout: true})
406
+
406 407
 	// intra-network communications should succeed
407
-	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
408
+	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
408 409
 	c.Assert(err, check.IsNil)
409 410
 }
410 411
 
... ...
@@ -440,10 +442,10 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL2InternalMode(c *check.C) {
440 440
 	c.Assert(waitRun("second"), check.IsNil)
441 441
 
442 442
 	// access outside of the network should fail
443
-	_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
444
-	c.Assert(err, check.NotNil)
443
+	result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
444
+	c.Assert(result.Error, check.NotNil)
445 445
 	// intra-network communications should succeed
446
-	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
446
+	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
447 447
 	c.Assert(err, check.IsNil)
448 448
 }
449 449
 
... ...
@@ -481,10 +483,10 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL3InternalMode(c *check.C) {
481 481
 	c.Assert(waitRun("second"), check.IsNil)
482 482
 
483 483
 	// access outside of the network should fail
484
-	_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
485
-	c.Assert(err, check.NotNil)
484
+	result := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
485
+	c.Assert(result.Error, check.NotNil)
486 486
 	// intra-network communications should succeed
487
-	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
487
+	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
488 488
 	c.Assert(err, check.IsNil)
489 489
 }
490 490
 
... ...
@@ -24,7 +24,7 @@ import (
24 24
 
25 25
 	"github.com/docker/docker/opts"
26 26
 	"github.com/docker/docker/pkg/httputils"
27
-	"github.com/docker/docker/pkg/integration"
27
+	icmd "github.com/docker/docker/pkg/integration/cmd"
28 28
 	"github.com/docker/docker/pkg/ioutils"
29 29
 	"github.com/docker/docker/pkg/stringutils"
30 30
 	"github.com/docker/engine-api/types"
... ...
@@ -229,15 +229,7 @@ func readBody(b io.ReadCloser) ([]byte, error) {
229 229
 }
230 230
 
231 231
 func deleteContainer(container string) error {
232
-	container = strings.TrimSpace(strings.Replace(container, "\n", " ", -1))
233
-	rmArgs := strings.Split(fmt.Sprintf("rm -fv %v", container), " ")
234
-	exitCode, err := runCommand(exec.Command(dockerBinary, rmArgs...))
235
-	// set error manually if not set
236
-	if exitCode != 0 && err == nil {
237
-		err = fmt.Errorf("failed to remove container: `docker rm` exit is non-zero")
238
-	}
239
-
240
-	return err
232
+	return icmd.RunCommand(dockerBinary, "rm", "-fv", container).Error
241 233
 }
242 234
 
243 235
 func getAllContainers() (string, error) {
... ...
@@ -398,13 +390,7 @@ func getSliceOfPausedContainers() ([]string, error) {
398 398
 }
399 399
 
400 400
 func unpauseContainer(container string) error {
401
-	unpauseCmd := exec.Command(dockerBinary, "unpause", container)
402
-	exitCode, err := runCommand(unpauseCmd)
403
-	if exitCode != 0 && err == nil {
404
-		err = fmt.Errorf("failed to unpause container")
405
-	}
406
-
407
-	return err
401
+	return icmd.RunCommand(dockerBinary, "unpause", container).Error
408 402
 }
409 403
 
410 404
 func unpauseAllContainers() error {
... ...
@@ -428,24 +414,12 @@ func unpauseAllContainers() error {
428 428
 }
429 429
 
430 430
 func deleteImages(images ...string) error {
431
-	args := []string{"rmi", "-f"}
432
-	args = append(args, images...)
433
-	rmiCmd := exec.Command(dockerBinary, args...)
434
-	exitCode, err := runCommand(rmiCmd)
435
-	// set error manually if not set
436
-	if exitCode != 0 && err == nil {
437
-		err = fmt.Errorf("failed to remove image: `docker rmi` exit is non-zero")
438
-	}
439
-	return err
431
+	args := []string{dockerBinary, "rmi", "-f"}
432
+	return icmd.RunCmd(icmd.Cmd{Command: append(args, images...)}).Error
440 433
 }
441 434
 
442 435
 func imageExists(image string) error {
443
-	inspectCmd := exec.Command(dockerBinary, "inspect", image)
444
-	exitCode, err := runCommand(inspectCmd)
445
-	if exitCode != 0 && err == nil {
446
-		err = fmt.Errorf("couldn't find image %q", image)
447
-	}
448
-	return err
436
+	return icmd.RunCommand(dockerBinary, "inspect", image).Error
449 437
 }
450 438
 
451 439
 func pullImageIfNotExist(image string) error {
... ...
@@ -464,33 +438,49 @@ func dockerCmdWithError(args ...string) (string, int, error) {
464 464
 	if err := validateArgs(args...); err != nil {
465 465
 		return "", 0, err
466 466
 	}
467
-	out, code, err := integration.DockerCmdWithError(dockerBinary, args...)
468
-	if err != nil {
469
-		err = fmt.Errorf("%v: %s", err, out)
467
+	result := icmd.RunCommand(dockerBinary, args...)
468
+	if result.Error != nil {
469
+		return result.Combined(), result.ExitCode, fmt.Errorf(result.Fails(icmd.Expected{}))
470 470
 	}
471
-	return out, code, err
471
+	return result.Combined(), result.ExitCode, result.Error
472 472
 }
473 473
 
474 474
 func dockerCmdWithStdoutStderr(c *check.C, args ...string) (string, string, int) {
475 475
 	if err := validateArgs(args...); err != nil {
476 476
 		c.Fatalf(err.Error())
477 477
 	}
478
-	return integration.DockerCmdWithStdoutStderr(dockerBinary, c, args...)
478
+
479
+	result := icmd.RunCommand(dockerBinary, args...)
480
+	// TODO: why is c ever nil?
481
+	if c != nil {
482
+		result.Assert(c, icmd.Expected{})
483
+	}
484
+	return result.Stdout(), result.Stderr(), result.ExitCode
479 485
 }
480 486
 
481 487
 func dockerCmd(c *check.C, args ...string) (string, int) {
482 488
 	if err := validateArgs(args...); err != nil {
483 489
 		c.Fatalf(err.Error())
484 490
 	}
485
-	return integration.DockerCmd(dockerBinary, c, args...)
491
+	result := icmd.RunCommand(dockerBinary, args...)
492
+	result.Assert(c, icmd.Expected{})
493
+	return result.Combined(), result.ExitCode
494
+}
495
+
496
+func dockerCmdWithResult(args ...string) *icmd.Result {
497
+	return icmd.RunCommand(dockerBinary, args...)
498
+}
499
+
500
+func binaryWithArgs(args []string) []string {
501
+	return append([]string{dockerBinary}, args...)
486 502
 }
487 503
 
488 504
 // execute a docker command with a timeout
489
-func dockerCmdWithTimeout(timeout time.Duration, args ...string) (string, int, error) {
505
+func dockerCmdWithTimeout(timeout time.Duration, args ...string) *icmd.Result {
490 506
 	if err := validateArgs(args...); err != nil {
491
-		return "", 0, err
507
+		return &icmd.Result{Error: err}
492 508
 	}
493
-	return integration.DockerCmdWithTimeout(dockerBinary, timeout, args...)
509
+	return icmd.RunCmd(icmd.Cmd{Command: binaryWithArgs(args), Timeout: timeout})
494 510
 }
495 511
 
496 512
 // execute a docker command in a directory
... ...
@@ -498,15 +488,20 @@ func dockerCmdInDir(c *check.C, path string, args ...string) (string, int, error
498 498
 	if err := validateArgs(args...); err != nil {
499 499
 		c.Fatalf(err.Error())
500 500
 	}
501
-	return integration.DockerCmdInDir(dockerBinary, path, args...)
501
+	result := icmd.RunCmd(icmd.Cmd{Command: binaryWithArgs(args), Dir: path})
502
+	return result.Combined(), result.ExitCode, result.Error
502 503
 }
503 504
 
504 505
 // execute a docker command in a directory with a timeout
505
-func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...string) (string, int, error) {
506
+func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...string) *icmd.Result {
506 507
 	if err := validateArgs(args...); err != nil {
507
-		return "", 0, err
508
+		return &icmd.Result{Error: err}
508 509
 	}
509
-	return integration.DockerCmdInDirWithTimeout(dockerBinary, timeout, path, args...)
510
+	return icmd.RunCmd(icmd.Cmd{
511
+		Command: binaryWithArgs(args),
512
+		Timeout: timeout,
513
+		Dir:     path,
514
+	})
510 515
 }
511 516
 
512 517
 // 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
1355 1355
 }
1356 1356
 
1357 1357
 func waitForContainer(contID string, args ...string) error {
1358
-	args = append([]string{"run", "--name", contID}, args...)
1359
-	cmd := exec.Command(dockerBinary, args...)
1360
-	if _, err := runCommand(cmd); err != nil {
1361
-		return err
1358
+	args = append([]string{dockerBinary, "run", "--name", contID}, args...)
1359
+	result := icmd.RunCmd(icmd.Cmd{Command: args})
1360
+	if result.Error != nil {
1361
+		return result.Error
1362 1362
 	}
1363
-
1364
-	if err := waitRun(contID); err != nil {
1365
-		return err
1366
-	}
1367
-
1368
-	return nil
1363
+	return waitRun(contID)
1369 1364
 }
1370 1365
 
1371 1366
 // 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
1391 1391
 
1392 1392
 	args := append(arg, "inspect", "-f", expr, name)
1393 1393
 	for {
1394
-		cmd := exec.Command(dockerBinary, args...)
1395
-		out, _, err := runCommandWithOutput(cmd)
1396
-		if err != nil {
1397
-			if !strings.Contains(out, "No such") {
1398
-				return fmt.Errorf("error executing docker inspect: %v\n%s", err, out)
1394
+		result := icmd.RunCommand(dockerBinary, args...)
1395
+		if result.Error != nil {
1396
+			if !strings.Contains(result.Stderr(), "No such") {
1397
+				return fmt.Errorf("error executing docker inspect: %v\n%s",
1398
+					result.Stderr(), result.Stdout())
1399 1399
 			}
1400 1400
 			select {
1401 1401
 			case <-after:
1402
-				return err
1402
+				return result.Error
1403 1403
 			default:
1404 1404
 				time.Sleep(10 * time.Millisecond)
1405 1405
 				continue
1406 1406
 			}
1407 1407
 		}
1408 1408
 
1409
-		out = strings.TrimSpace(out)
1409
+		out := strings.TrimSpace(result.Stdout())
1410 1410
 		if out == expected {
1411 1411
 			break
1412 1412
 		}
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"time"
8 8
 
9 9
 	"github.com/docker/docker/pkg/integration"
10
+	"github.com/docker/docker/pkg/integration/cmd"
10 11
 )
11 12
 
12 13
 func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
... ...
@@ -16,36 +17,33 @@ func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
16 16
 	return "", "/"
17 17
 }
18 18
 
19
-func getExitCode(err error) (int, error) {
20
-	return integration.GetExitCode(err)
19
+// TODO: update code to call cmd.RunCmd directly, and remove this function
20
+func runCommandWithOutput(execCmd *exec.Cmd) (string, int, error) {
21
+	result := cmd.RunCmd(transformCmd(execCmd))
22
+	return result.Combined(), result.ExitCode, result.Error
21 23
 }
22 24
 
23
-func processExitCode(err error) (exitCode int) {
24
-	return integration.ProcessExitCode(err)
25
+// TODO: update code to call cmd.RunCmd directly, and remove this function
26
+func runCommandWithStdoutStderr(execCmd *exec.Cmd) (string, string, int, error) {
27
+	result := cmd.RunCmd(transformCmd(execCmd))
28
+	return result.Stdout(), result.Stderr(), result.ExitCode, result.Error
25 29
 }
26 30
 
27
-func isKilled(err error) bool {
28
-	return integration.IsKilled(err)
31
+// TODO: update code to call cmd.RunCmd directly, and remove this function
32
+func runCommand(execCmd *exec.Cmd) (exitCode int, err error) {
33
+	result := cmd.RunCmd(transformCmd(execCmd))
34
+	return result.ExitCode, result.Error
29 35
 }
30 36
 
31
-func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) {
32
-	return integration.RunCommandWithOutput(cmd)
33
-}
34
-
35
-func runCommandWithStdoutStderr(cmd *exec.Cmd) (stdout string, stderr string, exitCode int, err error) {
36
-	return integration.RunCommandWithStdoutStderr(cmd)
37
-}
38
-
39
-func runCommandWithOutputForDuration(cmd *exec.Cmd, duration time.Duration) (output string, exitCode int, timedOut bool, err error) {
40
-	return integration.RunCommandWithOutputForDuration(cmd, duration)
41
-}
42
-
43
-func runCommandWithOutputAndTimeout(cmd *exec.Cmd, timeout time.Duration) (output string, exitCode int, err error) {
44
-	return integration.RunCommandWithOutputAndTimeout(cmd, timeout)
45
-}
46
-
47
-func runCommand(cmd *exec.Cmd) (exitCode int, err error) {
48
-	return integration.RunCommand(cmd)
37
+// Temporary shim for migrating commands to the new function
38
+func transformCmd(execCmd *exec.Cmd) cmd.Cmd {
39
+	return cmd.Cmd{
40
+		Command: execCmd.Args,
41
+		Env:     execCmd.Env,
42
+		Dir:     execCmd.Dir,
43
+		Stdin:   execCmd.Stdin,
44
+		Stdout:  execCmd.Stdout,
45
+	}
49 46
 }
50 47
 
51 48
 func runCommandPipelineWithOutput(cmds ...*exec.Cmd) (output string, exitCode int, err error) {
52 49
new file mode 100644
... ...
@@ -0,0 +1,266 @@
0
+package cmd
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"io"
6
+	"os/exec"
7
+	"path/filepath"
8
+	"runtime"
9
+	"strings"
10
+	"syscall"
11
+	"time"
12
+)
13
+
14
+type testingT interface {
15
+	Fatalf(string, ...interface{})
16
+}
17
+
18
+const (
19
+	// None is a token to inform Result.Assert that the output should be empty
20
+	None string = "<NOTHING>"
21
+)
22
+
23
+// GetExitCode returns the ExitStatus of the specified error if its type is
24
+// exec.ExitError, returns 0 and an error otherwise.
25
+func GetExitCode(err error) (int, error) {
26
+	exitCode := 0
27
+	if exiterr, ok := err.(*exec.ExitError); ok {
28
+		if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
29
+			return procExit.ExitStatus(), nil
30
+		}
31
+	}
32
+	return exitCode, fmt.Errorf("failed to get exit code")
33
+}
34
+
35
+// ProcessExitCode process the specified error and returns the exit status code
36
+// if the error was of type exec.ExitError, returns nothing otherwise.
37
+func ProcessExitCode(err error) (exitCode int) {
38
+	if err != nil {
39
+		var exiterr error
40
+		if exitCode, exiterr = GetExitCode(err); exiterr != nil {
41
+			// TODO: Fix this so we check the error's text.
42
+			// we've failed to retrieve exit code, so we set it to 127
43
+			exitCode = 127
44
+		}
45
+	}
46
+	return
47
+}
48
+
49
+// Result stores the result of running a command
50
+type Result struct {
51
+	Cmd      *exec.Cmd
52
+	ExitCode int
53
+	Error    error
54
+	// Timeout is true if the command was killed because it ran for too long
55
+	Timeout   bool
56
+	outBuffer *bytes.Buffer
57
+	errBuffer *bytes.Buffer
58
+}
59
+
60
+// Assert compares the Result against the Expected struct, and fails the test if
61
+// any of the expcetations are not met.
62
+func (r *Result) Assert(t testingT, exp Expected) {
63
+	fails := r.Fails(exp)
64
+	if fails == "" {
65
+		return
66
+	}
67
+
68
+	_, file, line, _ := runtime.Caller(1)
69
+	t.Fatalf("at %s:%d\n%s", filepath.Base(file), line, fails)
70
+}
71
+
72
+// Fails returns a formatted string which reports on any failed expectations
73
+func (r *Result) Fails(exp Expected) string {
74
+	errors := []string{}
75
+	add := func(format string, args ...interface{}) {
76
+		errors = append(errors, fmt.Sprintf(format, args...))
77
+	}
78
+
79
+	if exp.ExitCode != r.ExitCode {
80
+		add("ExitCode was %d expected %d", r.ExitCode, exp.ExitCode)
81
+	}
82
+	if exp.Timeout != r.Timeout {
83
+		if exp.Timeout {
84
+			add("Expected command to timeout")
85
+		} else {
86
+			add("Expected command to finish, but it hit the timeout")
87
+		}
88
+	}
89
+	if !matchOutput(exp.Out, r.Stdout()) {
90
+		add("Expected stdout to contain %q", exp.Out)
91
+	}
92
+	if !matchOutput(exp.Err, r.Stderr()) {
93
+		add("Expected stderr to contain %q", exp.Err)
94
+	}
95
+	switch {
96
+	// If a non-zero exit code is expected there is going to be an error.
97
+	// Don't require an error message as well as an exit code because the
98
+	// error message is going to be "exit status <code> which is not useful
99
+	case exp.Error == "" && exp.ExitCode != 0:
100
+	case exp.Error == "" && r.Error != nil:
101
+		add("Expected no error")
102
+	case exp.Error != "" && r.Error == nil:
103
+		add("Expected error to contain %q, but there was no error", exp.Error)
104
+	case exp.Error != "" && !strings.Contains(r.Error.Error(), exp.Error):
105
+		add("Expected error to contain %q", exp.Error)
106
+	}
107
+
108
+	if len(errors) == 0 {
109
+		return ""
110
+	}
111
+	return fmt.Sprintf("%s\nFailures:\n%s\n", r, strings.Join(errors, "\n"))
112
+}
113
+
114
+func matchOutput(expected string, actual string) bool {
115
+	switch expected {
116
+	case None:
117
+		return actual == ""
118
+	default:
119
+		return strings.Contains(actual, expected)
120
+	}
121
+}
122
+
123
+func (r *Result) String() string {
124
+	var timeout string
125
+	if r.Timeout {
126
+		timeout = " (timeout)"
127
+	}
128
+
129
+	return fmt.Sprintf(`
130
+Command: %s
131
+ExitCode: %d%s, Error: %s
132
+Stdout: %v
133
+Stderr: %v
134
+`,
135
+		strings.Join(r.Cmd.Args, " "),
136
+		r.ExitCode,
137
+		timeout,
138
+		r.Error,
139
+		r.Stdout(),
140
+		r.Stderr())
141
+}
142
+
143
+// Expected is the expected output from a Command. This struct is compared to a
144
+// Result struct by Result.Assert().
145
+type Expected struct {
146
+	ExitCode int
147
+	Timeout  bool
148
+	Error    string
149
+	Out      string
150
+	Err      string
151
+}
152
+
153
+// Stdout returns the stdout of the process as a string
154
+func (r *Result) Stdout() string {
155
+	return r.outBuffer.String()
156
+}
157
+
158
+// Stderr returns the stderr of the process as a string
159
+func (r *Result) Stderr() string {
160
+	return r.errBuffer.String()
161
+}
162
+
163
+// Combined returns the stdout and stderr combined into a single string
164
+func (r *Result) Combined() string {
165
+	return r.outBuffer.String() + r.errBuffer.String()
166
+}
167
+
168
+// SetExitError sets Error and ExitCode based on Error
169
+func (r *Result) SetExitError(err error) {
170
+	if err == nil {
171
+		return
172
+	}
173
+	r.Error = err
174
+	r.ExitCode = ProcessExitCode(err)
175
+}
176
+
177
+// Cmd is a command to run. One of Command or CommandArgs can be used to set the
178
+// comand line. Command will be paased to shlex and split into a string slice.
179
+// CommandArgs is an already split command line.
180
+type Cmd struct {
181
+	Command []string
182
+	Timeout time.Duration
183
+	Stdin   io.Reader
184
+	Stdout  io.Writer
185
+	Dir     string
186
+	Env     []string
187
+}
188
+
189
+// RunCmd runs a command and returns a Result
190
+func RunCmd(cmd Cmd) *Result {
191
+	result := StartCmd(cmd)
192
+	if result.Error != nil {
193
+		return result
194
+	}
195
+	return WaitOnCmd(cmd.Timeout, result)
196
+}
197
+
198
+// RunCommand parses a command line and runs it, returning a result
199
+func RunCommand(command string, args ...string) *Result {
200
+	return RunCmd(Cmd{Command: append([]string{command}, args...)})
201
+}
202
+
203
+// StartCmd starts a command, but doesn't wait for it to finish
204
+func StartCmd(cmd Cmd) *Result {
205
+	result := buildCmd(cmd)
206
+	if result.Error != nil {
207
+		return result
208
+	}
209
+	result.SetExitError(result.Cmd.Start())
210
+	return result
211
+}
212
+
213
+func buildCmd(cmd Cmd) *Result {
214
+	var execCmd *exec.Cmd
215
+	switch len(cmd.Command) {
216
+	case 1:
217
+		execCmd = exec.Command(cmd.Command[0])
218
+	default:
219
+		execCmd = exec.Command(cmd.Command[0], cmd.Command[1:]...)
220
+	}
221
+	outBuffer := new(bytes.Buffer)
222
+	errBuffer := new(bytes.Buffer)
223
+
224
+	execCmd.Stdin = cmd.Stdin
225
+	execCmd.Dir = cmd.Dir
226
+	execCmd.Env = cmd.Env
227
+	if cmd.Stdout != nil {
228
+		execCmd.Stdout = io.MultiWriter(outBuffer, cmd.Stdout)
229
+	} else {
230
+		execCmd.Stdout = outBuffer
231
+	}
232
+	execCmd.Stderr = errBuffer
233
+	return &Result{
234
+		Cmd:       execCmd,
235
+		outBuffer: outBuffer,
236
+		errBuffer: errBuffer,
237
+	}
238
+}
239
+
240
+// WaitOnCmd waits for a command to complete. If timeout is non-nil then
241
+// only wait until the timeout.
242
+func WaitOnCmd(timeout time.Duration, result *Result) *Result {
243
+	if timeout == time.Duration(0) {
244
+		result.SetExitError(result.Cmd.Wait())
245
+		return result
246
+	}
247
+
248
+	done := make(chan error, 1)
249
+	// Wait for command to exit in a goroutine
250
+	go func() {
251
+		done <- result.Cmd.Wait()
252
+	}()
253
+
254
+	select {
255
+	case <-time.After(timeout):
256
+		killErr := result.Cmd.Process.Kill()
257
+		if killErr != nil {
258
+			fmt.Printf("failed to kill (pid=%d): %v\n", result.Cmd.Process.Pid, killErr)
259
+		}
260
+		result.Timeout = true
261
+	case err := <-done:
262
+		result.SetExitError(err)
263
+	}
264
+	return result
265
+}
0 266
new file mode 100644
... ...
@@ -0,0 +1,104 @@
0
+package cmd
1
+
2
+import (
3
+	"runtime"
4
+	"strings"
5
+	"testing"
6
+	"time"
7
+
8
+	"github.com/docker/docker/pkg/testutil/assert"
9
+)
10
+
11
+func TestRunCommand(t *testing.T) {
12
+	// TODO Windows: Port this test
13
+	if runtime.GOOS == "windows" {
14
+		t.Skip("Needs porting to Windows")
15
+	}
16
+
17
+	result := RunCommand("ls")
18
+	result.Assert(t, Expected{})
19
+
20
+	result = RunCommand("doesnotexists")
21
+	expectedError := `exec: "doesnotexists": executable file not found`
22
+	result.Assert(t, Expected{ExitCode: 127, Error: expectedError})
23
+
24
+	result = RunCommand("ls", "-z")
25
+	result.Assert(t, Expected{
26
+		ExitCode: 2,
27
+		Error:    "exit status 2",
28
+		Err:      "invalid option",
29
+	})
30
+	assert.Contains(t, result.Combined(), "invalid option")
31
+}
32
+
33
+func TestRunCommandWithCombined(t *testing.T) {
34
+	// TODO Windows: Port this test
35
+	if runtime.GOOS == "windows" {
36
+		t.Skip("Needs porting to Windows")
37
+	}
38
+
39
+	result := RunCommand("ls", "-a")
40
+	result.Assert(t, Expected{})
41
+
42
+	assert.Contains(t, result.Combined(), "..")
43
+	assert.Contains(t, result.Stdout(), "..")
44
+}
45
+
46
+func TestRunCommandWithTimeoutFinished(t *testing.T) {
47
+	// TODO Windows: Port this test
48
+	if runtime.GOOS == "windows" {
49
+		t.Skip("Needs porting to Windows")
50
+	}
51
+
52
+	result := RunCmd(Cmd{
53
+		Command: []string{"ls", "-a"},
54
+		Timeout: 50 * time.Millisecond,
55
+	})
56
+	result.Assert(t, Expected{Out: ".."})
57
+}
58
+
59
+func TestRunCommandWithTimeoutKilled(t *testing.T) {
60
+	// TODO Windows: Port this test
61
+	if runtime.GOOS == "windows" {
62
+		t.Skip("Needs porting to Windows")
63
+	}
64
+
65
+	command := []string{"sh", "-c", "while true ; do echo 1 ; sleep .1 ; done"}
66
+	result := RunCmd(Cmd{Command: command, Timeout: 500 * time.Millisecond})
67
+	result.Assert(t, Expected{Timeout: true})
68
+
69
+	ones := strings.Split(result.Stdout(), "\n")
70
+	assert.Equal(t, len(ones), 6)
71
+}
72
+
73
+func TestRunCommandWithErrors(t *testing.T) {
74
+	result := RunCommand("/foobar")
75
+	result.Assert(t, Expected{Error: "foobar", ExitCode: 127})
76
+}
77
+
78
+func TestRunCommandWithStdoutStderr(t *testing.T) {
79
+	result := RunCommand("echo", "hello", "world")
80
+	result.Assert(t, Expected{Out: "hello world\n", Err: None})
81
+}
82
+
83
+func TestRunCommandWithStdoutStderrError(t *testing.T) {
84
+	result := RunCommand("doesnotexists")
85
+
86
+	expected := `exec: "doesnotexists": executable file not found`
87
+	result.Assert(t, Expected{Out: None, Err: None, ExitCode: 127, Error: expected})
88
+
89
+	switch runtime.GOOS {
90
+	case "windows":
91
+		expected = "ls: unknown option"
92
+	default:
93
+		expected = "ls: invalid option"
94
+	}
95
+
96
+	result = RunCommand("ls", "-z")
97
+	result.Assert(t, Expected{
98
+		Out:      None,
99
+		Err:      expected,
100
+		ExitCode: 2,
101
+		Error:    "exit status 2",
102
+	})
103
+}
0 104
deleted file mode 100644
... ...
@@ -1,78 +0,0 @@
1
-package integration
2
-
3
-import (
4
-	"fmt"
5
-	"os/exec"
6
-	"strings"
7
-	"time"
8
-
9
-	"github.com/go-check/check"
10
-)
11
-
12
-// We use the elongated quote mechanism for quoting error returns as
13
-// the use of strconv.Quote or %q in fmt.Errorf will escape characters. This
14
-// has a big downside on Windows where the args include paths, so instead
15
-// of something like c:\directory\file.txt, the output would be
16
-// c:\\directory\\file.txt. This is highly misleading.
17
-const quote = `"`
18
-
19
-var execCommand = exec.Command
20
-
21
-// DockerCmdWithError executes a docker command that is supposed to fail and returns
22
-// the output, the exit code and the error.
23
-func DockerCmdWithError(dockerBinary string, args ...string) (string, int, error) {
24
-	return RunCommandWithOutput(execCommand(dockerBinary, args...))
25
-}
26
-
27
-// DockerCmdWithStdoutStderr executes a docker command and returns the content of the
28
-// stdout, stderr and the exit code. If a check.C is passed, it will fail and stop tests
29
-// if the error is not nil.
30
-func DockerCmdWithStdoutStderr(dockerBinary string, c *check.C, args ...string) (string, string, int) {
31
-	stdout, stderr, status, err := RunCommandWithStdoutStderr(execCommand(dockerBinary, args...))
32
-	if c != nil {
33
-		c.Assert(err, check.IsNil, check.Commentf(quote+"%v"+quote+" failed with errors: %s, %v", strings.Join(args, " "), stderr, err))
34
-	}
35
-	return stdout, stderr, status
36
-}
37
-
38
-// DockerCmd executes a docker command and returns the output and the exit code. If the
39
-// command returns an error, it will fail and stop the tests.
40
-func DockerCmd(dockerBinary string, c *check.C, args ...string) (string, int) {
41
-	out, status, err := RunCommandWithOutput(execCommand(dockerBinary, args...))
42
-	c.Assert(err, check.IsNil, check.Commentf(quote+"%v"+quote+" failed with errors: %s, %v", strings.Join(args, " "), out, err))
43
-	return out, status
44
-}
45
-
46
-// DockerCmdWithTimeout executes a docker command with a timeout, and returns the output,
47
-// the exit code and the error (if any).
48
-func DockerCmdWithTimeout(dockerBinary string, timeout time.Duration, args ...string) (string, int, error) {
49
-	out, status, err := RunCommandWithOutputAndTimeout(execCommand(dockerBinary, args...), timeout)
50
-	if err != nil {
51
-		return out, status, fmt.Errorf(quote+"%v"+quote+" failed with errors: %v : %q", strings.Join(args, " "), err, out)
52
-	}
53
-	return out, status, err
54
-}
55
-
56
-// DockerCmdInDir executes a docker command in a directory and returns the output, the
57
-// exit code and the error (if any).
58
-func DockerCmdInDir(dockerBinary string, path string, args ...string) (string, int, error) {
59
-	dockerCommand := execCommand(dockerBinary, args...)
60
-	dockerCommand.Dir = path
61
-	out, status, err := RunCommandWithOutput(dockerCommand)
62
-	if err != nil {
63
-		return out, status, fmt.Errorf(quote+"%v"+quote+" failed with errors: %v : %q", strings.Join(args, " "), err, out)
64
-	}
65
-	return out, status, err
66
-}
67
-
68
-// DockerCmdInDirWithTimeout executes a docker command in a directory with a timeout and
69
-// returns the output, the exit code and the error (if any).
70
-func DockerCmdInDirWithTimeout(dockerBinary string, timeout time.Duration, path string, args ...string) (string, int, error) {
71
-	dockerCommand := execCommand(dockerBinary, args...)
72
-	dockerCommand.Dir = path
73
-	out, status, err := RunCommandWithOutputAndTimeout(dockerCommand, timeout)
74
-	if err != nil {
75
-		return out, status, fmt.Errorf(quote+"%v"+quote+" failed with errors: %v : %q", strings.Join(args, " "), err, out)
76
-	}
77
-	return out, status, err
78
-}
79 1
deleted file mode 100644
... ...
@@ -1,405 +0,0 @@
1
-package integration
2
-
3
-import (
4
-	"fmt"
5
-	"os"
6
-	"os/exec"
7
-	"testing"
8
-
9
-	"io/ioutil"
10
-	"strings"
11
-	"time"
12
-
13
-	"github.com/go-check/check"
14
-)
15
-
16
-const dockerBinary = "docker"
17
-
18
-// Setup go-check for this test
19
-func Test(t *testing.T) {
20
-	check.TestingT(t)
21
-}
22
-
23
-func init() {
24
-	check.Suite(&DockerCmdSuite{})
25
-}
26
-
27
-type DockerCmdSuite struct{}
28
-
29
-// Fake the exec.Command to use our mock.
30
-func (s *DockerCmdSuite) SetUpTest(c *check.C) {
31
-	execCommand = fakeExecCommand
32
-}
33
-
34
-// And bring it back to normal after the test.
35
-func (s *DockerCmdSuite) TearDownTest(c *check.C) {
36
-	execCommand = exec.Command
37
-}
38
-
39
-// DockerCmdWithError tests
40
-
41
-func (s *DockerCmdSuite) TestDockerCmdWithError(c *check.C) {
42
-	cmds := []struct {
43
-		binary           string
44
-		args             []string
45
-		expectedOut      string
46
-		expectedExitCode int
47
-		expectedError    error
48
-	}{
49
-		{
50
-			"doesnotexists",
51
-			[]string{},
52
-			"Command doesnotexists not found.",
53
-			1,
54
-			fmt.Errorf("exit status 1"),
55
-		},
56
-		{
57
-			dockerBinary,
58
-			[]string{"an", "error"},
59
-			"an error has occurred",
60
-			1,
61
-			fmt.Errorf("exit status 1"),
62
-		},
63
-		{
64
-			dockerBinary,
65
-			[]string{"an", "exitCode", "127"},
66
-			"an error has occurred with exitCode 127",
67
-			127,
68
-			fmt.Errorf("exit status 127"),
69
-		},
70
-		{
71
-			dockerBinary,
72
-			[]string{"run", "-ti", "ubuntu", "echo", "hello"},
73
-			"hello",
74
-			0,
75
-			nil,
76
-		},
77
-	}
78
-	for _, cmd := range cmds {
79
-		out, exitCode, error := DockerCmdWithError(cmd.binary, cmd.args...)
80
-		c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
81
-		c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
82
-		if cmd.expectedError != nil {
83
-			c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
84
-			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()))
85
-		} else {
86
-			c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
87
-		}
88
-	}
89
-}
90
-
91
-// DockerCmdWithStdoutStderr tests
92
-
93
-type dockerCmdWithStdoutStderrErrorSuite struct{}
94
-
95
-func (s *dockerCmdWithStdoutStderrErrorSuite) Test(c *check.C) {
96
-	// Should fail, the test too
97
-	DockerCmdWithStdoutStderr(dockerBinary, c, "an", "error")
98
-}
99
-
100
-type dockerCmdWithStdoutStderrSuccessSuite struct{}
101
-
102
-func (s *dockerCmdWithStdoutStderrSuccessSuite) Test(c *check.C) {
103
-	stdout, stderr, exitCode := DockerCmdWithStdoutStderr(dockerBinary, c, "run", "-ti", "ubuntu", "echo", "hello")
104
-	c.Assert(stdout, check.Equals, "hello")
105
-	c.Assert(stderr, check.Equals, "")
106
-	c.Assert(exitCode, check.Equals, 0)
107
-
108
-}
109
-
110
-func (s *DockerCmdSuite) TestDockerCmdWithStdoutStderrError(c *check.C) {
111
-	// Run error suite, should fail.
112
-	output := String{}
113
-	result := check.Run(&dockerCmdWithStdoutStderrErrorSuite{}, &check.RunConf{Output: &output})
114
-	c.Check(result.Succeeded, check.Equals, 0)
115
-	c.Check(result.Failed, check.Equals, 1)
116
-}
117
-
118
-func (s *DockerCmdSuite) TestDockerCmdWithStdoutStderrSuccess(c *check.C) {
119
-	// Run error suite, should fail.
120
-	output := String{}
121
-	result := check.Run(&dockerCmdWithStdoutStderrSuccessSuite{}, &check.RunConf{Output: &output})
122
-	c.Check(result.Succeeded, check.Equals, 1)
123
-	c.Check(result.Failed, check.Equals, 0)
124
-}
125
-
126
-// DockerCmd tests
127
-
128
-type dockerCmdErrorSuite struct{}
129
-
130
-func (s *dockerCmdErrorSuite) Test(c *check.C) {
131
-	// Should fail, the test too
132
-	DockerCmd(dockerBinary, c, "an", "error")
133
-}
134
-
135
-type dockerCmdSuccessSuite struct{}
136
-
137
-func (s *dockerCmdSuccessSuite) Test(c *check.C) {
138
-	stdout, exitCode := DockerCmd(dockerBinary, c, "run", "-ti", "ubuntu", "echo", "hello")
139
-	c.Assert(stdout, check.Equals, "hello")
140
-	c.Assert(exitCode, check.Equals, 0)
141
-
142
-}
143
-
144
-func (s *DockerCmdSuite) TestDockerCmdError(c *check.C) {
145
-	// Run error suite, should fail.
146
-	output := String{}
147
-	result := check.Run(&dockerCmdErrorSuite{}, &check.RunConf{Output: &output})
148
-	c.Check(result.Succeeded, check.Equals, 0)
149
-	c.Check(result.Failed, check.Equals, 1)
150
-}
151
-
152
-func (s *DockerCmdSuite) TestDockerCmdSuccess(c *check.C) {
153
-	// Run error suite, should fail.
154
-	output := String{}
155
-	result := check.Run(&dockerCmdSuccessSuite{}, &check.RunConf{Output: &output})
156
-	c.Check(result.Succeeded, check.Equals, 1)
157
-	c.Check(result.Failed, check.Equals, 0)
158
-}
159
-
160
-// DockerCmdWithTimeout tests
161
-
162
-func (s *DockerCmdSuite) TestDockerCmdWithTimeout(c *check.C) {
163
-	cmds := []struct {
164
-		binary           string
165
-		args             []string
166
-		timeout          time.Duration
167
-		expectedOut      string
168
-		expectedExitCode int
169
-		expectedError    error
170
-	}{
171
-		{
172
-			"doesnotexists",
173
-			[]string{},
174
-			200 * time.Millisecond,
175
-			`Command doesnotexists not found.`,
176
-			1,
177
-			fmt.Errorf(`"" failed with errors: exit status 1 : "Command doesnotexists not found."`),
178
-		},
179
-		{
180
-			dockerBinary,
181
-			[]string{"an", "error"},
182
-			200 * time.Millisecond,
183
-			`an error has occurred`,
184
-			1,
185
-			fmt.Errorf(`"an error" failed with errors: exit status 1 : "an error has occurred"`),
186
-		},
187
-		{
188
-			dockerBinary,
189
-			[]string{"a", "command", "that", "times", "out"},
190
-			5 * time.Millisecond,
191
-			"",
192
-			0,
193
-			fmt.Errorf(`"a command that times out" failed with errors: command timed out : ""`),
194
-		},
195
-		{
196
-			dockerBinary,
197
-			[]string{"run", "-ti", "ubuntu", "echo", "hello"},
198
-			200 * time.Millisecond,
199
-			"hello",
200
-			0,
201
-			nil,
202
-		},
203
-	}
204
-	for _, cmd := range cmds {
205
-		out, exitCode, error := DockerCmdWithTimeout(cmd.binary, cmd.timeout, cmd.args...)
206
-		c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
207
-		c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
208
-		if cmd.expectedError != nil {
209
-			c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
210
-			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()))
211
-		} else {
212
-			c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
213
-		}
214
-	}
215
-}
216
-
217
-// DockerCmdInDir tests
218
-
219
-func (s *DockerCmdSuite) TestDockerCmdInDir(c *check.C) {
220
-	tempFolder, err := ioutil.TempDir("", "test-docker-cmd-in-dir")
221
-	c.Assert(err, check.IsNil)
222
-
223
-	cmds := []struct {
224
-		binary           string
225
-		args             []string
226
-		expectedOut      string
227
-		expectedExitCode int
228
-		expectedError    error
229
-	}{
230
-		{
231
-			"doesnotexists",
232
-			[]string{},
233
-			`Command doesnotexists not found.`,
234
-			1,
235
-			fmt.Errorf(`"dir:%s" failed with errors: exit status 1 : "Command doesnotexists not found."`, tempFolder),
236
-		},
237
-		{
238
-			dockerBinary,
239
-			[]string{"an", "error"},
240
-			`an error has occurred`,
241
-			1,
242
-			fmt.Errorf(`"dir:%s an error" failed with errors: exit status 1 : "an error has occurred"`, tempFolder),
243
-		},
244
-		{
245
-			dockerBinary,
246
-			[]string{"run", "-ti", "ubuntu", "echo", "hello"},
247
-			"hello",
248
-			0,
249
-			nil,
250
-		},
251
-	}
252
-	for _, cmd := range cmds {
253
-		// We prepend the arguments with dir:thefolder.. the fake command will check
254
-		// that the current workdir is the same as the one we are passing.
255
-		args := append([]string{"dir:" + tempFolder}, cmd.args...)
256
-		out, exitCode, error := DockerCmdInDir(cmd.binary, tempFolder, args...)
257
-		c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
258
-		c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
259
-		if cmd.expectedError != nil {
260
-			c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
261
-			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()))
262
-		} else {
263
-			c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
264
-		}
265
-	}
266
-}
267
-
268
-// DockerCmdInDirWithTimeout tests
269
-
270
-func (s *DockerCmdSuite) TestDockerCmdInDirWithTimeout(c *check.C) {
271
-	tempFolder, err := ioutil.TempDir("", "test-docker-cmd-in-dir")
272
-	c.Assert(err, check.IsNil)
273
-
274
-	cmds := []struct {
275
-		binary           string
276
-		args             []string
277
-		timeout          time.Duration
278
-		expectedOut      string
279
-		expectedExitCode int
280
-		expectedError    error
281
-	}{
282
-		{
283
-			"doesnotexists",
284
-			[]string{},
285
-			200 * time.Millisecond,
286
-			`Command doesnotexists not found.`,
287
-			1,
288
-			fmt.Errorf(`"dir:%s" failed with errors: exit status 1 : "Command doesnotexists not found."`, tempFolder),
289
-		},
290
-		{
291
-			dockerBinary,
292
-			[]string{"an", "error"},
293
-			200 * time.Millisecond,
294
-			`an error has occurred`,
295
-			1,
296
-			fmt.Errorf(`"dir:%s an error" failed with errors: exit status 1 : "an error has occurred"`, tempFolder),
297
-		},
298
-		{
299
-			dockerBinary,
300
-			[]string{"a", "command", "that", "times", "out"},
301
-			5 * time.Millisecond,
302
-			"",
303
-			0,
304
-			fmt.Errorf(`"dir:%s a command that times out" failed with errors: command timed out : ""`, tempFolder),
305
-		},
306
-		{
307
-			dockerBinary,
308
-			[]string{"run", "-ti", "ubuntu", "echo", "hello"},
309
-			200 * time.Millisecond,
310
-			"hello",
311
-			0,
312
-			nil,
313
-		},
314
-	}
315
-	for _, cmd := range cmds {
316
-		// We prepend the arguments with dir:thefolder.. the fake command will check
317
-		// that the current workdir is the same as the one we are passing.
318
-		args := append([]string{"dir:" + tempFolder}, cmd.args...)
319
-		out, exitCode, error := DockerCmdInDirWithTimeout(cmd.binary, cmd.timeout, tempFolder, args...)
320
-		c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
321
-		c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
322
-		if cmd.expectedError != nil {
323
-			c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
324
-			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()))
325
-		} else {
326
-			c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
327
-		}
328
-	}
329
-}
330
-
331
-// Helpers :)
332
-
333
-// Type implementing the io.Writer interface for analyzing output.
334
-type String struct {
335
-	value string
336
-}
337
-
338
-// The only function required by the io.Writer interface.  Will append
339
-// written data to the String.value string.
340
-func (s *String) Write(p []byte) (n int, err error) {
341
-	s.value += string(p)
342
-	return len(p), nil
343
-}
344
-
345
-// Helper function that mock the exec.Command call (and call the test binary)
346
-func fakeExecCommand(command string, args ...string) *exec.Cmd {
347
-	cs := []string{"-test.run=TestHelperProcess", "--", command}
348
-	cs = append(cs, args...)
349
-	cmd := exec.Command(os.Args[0], cs...)
350
-	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
351
-	return cmd
352
-}
353
-
354
-func TestHelperProcess(t *testing.T) {
355
-	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
356
-		return
357
-	}
358
-	args := os.Args
359
-
360
-	// Previous arguments are tests stuff, that looks like :
361
-	// /tmp/go-build970079519/…/_test/integration.test -test.run=TestHelperProcess --
362
-	cmd, args := args[3], args[4:]
363
-	// Handle the case where args[0] is dir:...
364
-	if len(args) > 0 && strings.HasPrefix(args[0], "dir:") {
365
-		expectedCwd := args[0][4:]
366
-		if len(args) > 1 {
367
-			args = args[1:]
368
-		}
369
-		cwd, err := os.Getwd()
370
-		if err != nil {
371
-			fmt.Fprintf(os.Stderr, "Failed to get workingdir: %v", err)
372
-			os.Exit(1)
373
-		}
374
-		// This checks that the given path is the same as the currend working dire
375
-		if expectedCwd != cwd {
376
-			fmt.Fprintf(os.Stderr, "Current workdir should be %q, but is %q", expectedCwd, cwd)
377
-		}
378
-	}
379
-	switch cmd {
380
-	case dockerBinary:
381
-		argsStr := strings.Join(args, " ")
382
-		switch argsStr {
383
-		case "an exitCode 127":
384
-			fmt.Fprintf(os.Stderr, "an error has occurred with exitCode 127")
385
-			os.Exit(127)
386
-		case "an error":
387
-			fmt.Fprintf(os.Stderr, "an error has occurred")
388
-			os.Exit(1)
389
-		case "a command that times out":
390
-			time.Sleep(10 * time.Second)
391
-			fmt.Fprintf(os.Stdout, "too long, should be killed")
392
-			// A random exit code (that should never happened in tests)
393
-			os.Exit(7)
394
-		case "run -ti ubuntu echo hello":
395
-			fmt.Fprintf(os.Stdout, "hello")
396
-		default:
397
-			fmt.Fprintf(os.Stdout, "no arguments")
398
-		}
399
-	default:
400
-		fmt.Fprintf(os.Stderr, "Command %s not found.", cmd)
401
-		os.Exit(1)
402
-	}
403
-	// some code here to check arguments perhaps?
404
-	os.Exit(0)
405
-}
... ...
@@ -2,7 +2,6 @@ package integration
2 2
 
3 3
 import (
4 4
 	"archive/tar"
5
-	"bytes"
6 5
 	"errors"
7 6
 	"fmt"
8 7
 	"io"
... ...
@@ -14,35 +13,10 @@ import (
14 14
 	"syscall"
15 15
 	"time"
16 16
 
17
+	icmd "github.com/docker/docker/pkg/integration/cmd"
17 18
 	"github.com/docker/docker/pkg/stringutils"
18 19
 )
19 20
 
20
-// GetExitCode returns the ExitStatus of the specified error if its type is
21
-// exec.ExitError, returns 0 and an error otherwise.
22
-func GetExitCode(err error) (int, error) {
23
-	exitCode := 0
24
-	if exiterr, ok := err.(*exec.ExitError); ok {
25
-		if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
26
-			return procExit.ExitStatus(), nil
27
-		}
28
-	}
29
-	return exitCode, fmt.Errorf("failed to get exit code")
30
-}
31
-
32
-// ProcessExitCode process the specified error and returns the exit status code
33
-// if the error was of type exec.ExitError, returns nothing otherwise.
34
-func ProcessExitCode(err error) (exitCode int) {
35
-	if err != nil {
36
-		var exiterr error
37
-		if exitCode, exiterr = GetExitCode(err); exiterr != nil {
38
-			// TODO: Fix this so we check the error's text.
39
-			// we've failed to retrieve exit code, so we set it to 127
40
-			exitCode = 127
41
-		}
42
-	}
43
-	return
44
-}
45
-
46 21
 // IsKilled process the specified error and returns whether the process was killed or not.
47 22
 func IsKilled(err error) bool {
48 23
 	if exitErr, ok := err.(*exec.ExitError); ok {
... ...
@@ -58,110 +32,14 @@ func IsKilled(err error) bool {
58 58
 	return false
59 59
 }
60 60
 
61
-// RunCommandWithOutput runs the specified command and returns the combined output (stdout/stderr)
62
-// with the exitCode different from 0 and the error if something bad happened
63
-func RunCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) {
61
+func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) {
64 62
 	exitCode = 0
65 63
 	out, err := cmd.CombinedOutput()
66
-	exitCode = ProcessExitCode(err)
64
+	exitCode = icmd.ProcessExitCode(err)
67 65
 	output = string(out)
68 66
 	return
69 67
 }
70 68
 
71
-// RunCommandWithStdoutStderr runs the specified command and returns stdout and stderr separately
72
-// with the exitCode different from 0 and the error if something bad happened
73
-func RunCommandWithStdoutStderr(cmd *exec.Cmd) (stdout string, stderr string, exitCode int, err error) {
74
-	var (
75
-		stderrBuffer, stdoutBuffer bytes.Buffer
76
-	)
77
-	exitCode = 0
78
-	cmd.Stderr = &stderrBuffer
79
-	cmd.Stdout = &stdoutBuffer
80
-	err = cmd.Run()
81
-	exitCode = ProcessExitCode(err)
82
-
83
-	stdout = stdoutBuffer.String()
84
-	stderr = stderrBuffer.String()
85
-	return
86
-}
87
-
88
-// RunCommandWithOutputForDuration runs the specified command "timeboxed" by the specified duration.
89
-// If the process is still running when the timebox is finished, the process will be killed and .
90
-// It will returns the output with the exitCode different from 0 and the error if something bad happened
91
-// and a boolean whether it has been killed or not.
92
-func RunCommandWithOutputForDuration(cmd *exec.Cmd, duration time.Duration) (output string, exitCode int, timedOut bool, err error) {
93
-	var outputBuffer bytes.Buffer
94
-	if cmd.Stdout != nil {
95
-		err = errors.New("cmd.Stdout already set")
96
-		return
97
-	}
98
-	cmd.Stdout = &outputBuffer
99
-
100
-	if cmd.Stderr != nil {
101
-		err = errors.New("cmd.Stderr already set")
102
-		return
103
-	}
104
-	cmd.Stderr = &outputBuffer
105
-
106
-	// Start the command in the main thread..
107
-	err = cmd.Start()
108
-	if err != nil {
109
-		err = fmt.Errorf("Fail to start command %v : %v", cmd, err)
110
-	}
111
-
112
-	type exitInfo struct {
113
-		exitErr  error
114
-		exitCode int
115
-	}
116
-
117
-	done := make(chan exitInfo, 1)
118
-
119
-	go func() {
120
-		// And wait for it to exit in the goroutine :)
121
-		info := exitInfo{}
122
-		info.exitErr = cmd.Wait()
123
-		info.exitCode = ProcessExitCode(info.exitErr)
124
-		done <- info
125
-	}()
126
-
127
-	select {
128
-	case <-time.After(duration):
129
-		killErr := cmd.Process.Kill()
130
-		if killErr != nil {
131
-			fmt.Printf("failed to kill (pid=%d): %v\n", cmd.Process.Pid, killErr)
132
-		}
133
-		timedOut = true
134
-	case info := <-done:
135
-		err = info.exitErr
136
-		exitCode = info.exitCode
137
-	}
138
-	output = outputBuffer.String()
139
-	return
140
-}
141
-
142
-var errCmdTimeout = fmt.Errorf("command timed out")
143
-
144
-// RunCommandWithOutputAndTimeout runs the specified command "timeboxed" by the specified duration.
145
-// It returns the output with the exitCode different from 0 and the error if something bad happened or
146
-// if the process timed out (and has been killed).
147
-func RunCommandWithOutputAndTimeout(cmd *exec.Cmd, timeout time.Duration) (output string, exitCode int, err error) {
148
-	var timedOut bool
149
-	output, exitCode, timedOut, err = RunCommandWithOutputForDuration(cmd, timeout)
150
-	if timedOut {
151
-		err = errCmdTimeout
152
-	}
153
-	return
154
-}
155
-
156
-// RunCommand runs the specified command and returns the exitCode different from 0
157
-// and the error if something bad happened.
158
-func RunCommand(cmd *exec.Cmd) (exitCode int, err error) {
159
-	exitCode = 0
160
-	err = cmd.Run()
161
-	exitCode = ProcessExitCode(err)
162
-	return
163
-}
164
-
165 69
 // RunCommandPipelineWithOutput runs the array of commands with the output
166 70
 // of each pipelined with the following (like cmd1 | cmd2 | cmd3 would do).
167 71
 // 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
205 205
 	}
206 206
 
207 207
 	// wait on last cmd
208
-	return RunCommandWithOutput(cmds[len(cmds)-1])
208
+	return runCommandWithOutput(cmds[len(cmds)-1])
209 209
 }
210 210
 
211 211
 // ConvertSliceOfStringsToMap converts a slices of string in a map
... ...
@@ -341,11 +219,9 @@ func RunAtDifferentDate(date time.Time, block func()) {
341 341
 	const timeLayout = "010203042006"
342 342
 	// Ensure we bring time back to now
343 343
 	now := time.Now().Format(timeLayout)
344
-	dateReset := exec.Command("date", now)
345
-	defer RunCommand(dateReset)
344
+	defer icmd.RunCommand("date", now)
346 345
 
347
-	dateChange := exec.Command("date", date.Format(timeLayout))
348
-	RunCommand(dateChange)
346
+	icmd.RunCommand("date", date.Format(timeLayout))
349 347
 	block()
350 348
 	return
351 349
 }
... ...
@@ -7,7 +7,6 @@ import (
7 7
 	"os/exec"
8 8
 	"path/filepath"
9 9
 	"runtime"
10
-	"strconv"
11 10
 	"strings"
12 11
 	"testing"
13 12
 	"time"
... ...
@@ -54,203 +53,6 @@ func TestIsKilledTrueWithKilledProcess(t *testing.T) {
54 54
 	}
55 55
 }
56 56
 
57
-func TestRunCommandWithOutput(t *testing.T) {
58
-	var (
59
-		echoHelloWorldCmd *exec.Cmd
60
-		expected          string
61
-	)
62
-	if runtime.GOOS != "windows" {
63
-		echoHelloWorldCmd = exec.Command("echo", "hello", "world")
64
-		expected = "hello world\n"
65
-	} else {
66
-		echoHelloWorldCmd = exec.Command("cmd", "/s", "/c", "echo", "hello", "world")
67
-		expected = "hello world\r\n"
68
-	}
69
-
70
-	out, exitCode, err := RunCommandWithOutput(echoHelloWorldCmd)
71
-	if out != expected || exitCode != 0 || err != nil {
72
-		t.Fatalf("Expected command to output %s, got %s, %v with exitCode %v", expected, out, err, exitCode)
73
-	}
74
-}
75
-
76
-func TestRunCommandWithOutputError(t *testing.T) {
77
-	var (
78
-		p                string
79
-		wrongCmd         *exec.Cmd
80
-		expected         string
81
-		expectedExitCode int
82
-	)
83
-
84
-	if runtime.GOOS != "windows" {
85
-		p = "$PATH"
86
-		wrongCmd = exec.Command("ls", "-z")
87
-		expected = `ls: invalid option -- 'z'
88
-Try 'ls --help' for more information.
89
-`
90
-		expectedExitCode = 2
91
-	} else {
92
-		p = "%PATH%"
93
-		wrongCmd = exec.Command("cmd", "/s", "/c", "dir", "/Z")
94
-		expected = "Invalid switch - " + strconv.Quote("Z") + ".\r\n"
95
-		expectedExitCode = 1
96
-	}
97
-	cmd := exec.Command("doesnotexists")
98
-	out, exitCode, err := RunCommandWithOutput(cmd)
99
-	expectedError := `exec: "doesnotexists": executable file not found in ` + p
100
-	if out != "" || exitCode != 127 || err == nil || err.Error() != expectedError {
101
-		t.Fatalf("Expected command to output %s, got %s, %v with exitCode %v", expectedError, out, err, exitCode)
102
-	}
103
-
104
-	out, exitCode, err = RunCommandWithOutput(wrongCmd)
105
-
106
-	if out != expected || exitCode != expectedExitCode || err == nil || !strings.Contains(err.Error(), "exit status "+strconv.Itoa(expectedExitCode)) {
107
-		t.Fatalf("Expected command to output %s, got out:xxx%sxxx, err:%v with exitCode %v", expected, out, err, exitCode)
108
-	}
109
-}
110
-
111
-func TestRunCommandWithStdoutStderr(t *testing.T) {
112
-	echoHelloWorldCmd := exec.Command("echo", "hello", "world")
113
-	stdout, stderr, exitCode, err := RunCommandWithStdoutStderr(echoHelloWorldCmd)
114
-	expected := "hello world\n"
115
-	if stdout != expected || stderr != "" || exitCode != 0 || err != nil {
116
-		t.Fatalf("Expected command to output %s, got stdout:%s, stderr:%s, err:%v with exitCode %v", expected, stdout, stderr, err, exitCode)
117
-	}
118
-}
119
-
120
-func TestRunCommandWithStdoutStderrError(t *testing.T) {
121
-	p := "$PATH"
122
-	if runtime.GOOS == "windows" {
123
-		p = "%PATH%"
124
-	}
125
-	cmd := exec.Command("doesnotexists")
126
-	stdout, stderr, exitCode, err := RunCommandWithStdoutStderr(cmd)
127
-	expectedError := `exec: "doesnotexists": executable file not found in ` + p
128
-	if stdout != "" || stderr != "" || exitCode != 127 || err == nil || err.Error() != expectedError {
129
-		t.Fatalf("Expected command to output out:%s, stderr:%s, got stdout:%s, stderr:%s, err:%v with exitCode %v", "", "", stdout, stderr, err, exitCode)
130
-	}
131
-
132
-	wrongLsCmd := exec.Command("ls", "-z")
133
-	expected := `ls: invalid option -- 'z'
134
-Try 'ls --help' for more information.
135
-`
136
-
137
-	stdout, stderr, exitCode, err = RunCommandWithStdoutStderr(wrongLsCmd)
138
-	if stdout != "" && stderr != expected || exitCode != 2 || err == nil || err.Error() != "exit status 2" {
139
-		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)
140
-	}
141
-}
142
-
143
-func TestRunCommandWithOutputForDurationFinished(t *testing.T) {
144
-	// TODO Windows: Port this test
145
-	if runtime.GOOS == "windows" {
146
-		t.Skip("Needs porting to Windows")
147
-	}
148
-
149
-	cmd := exec.Command("ls")
150
-	out, exitCode, timedOut, err := RunCommandWithOutputForDuration(cmd, 50*time.Millisecond)
151
-	if out == "" || exitCode != 0 || timedOut || err != nil {
152
-		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)
153
-	}
154
-}
155
-
156
-func TestRunCommandWithOutputForDurationKilled(t *testing.T) {
157
-	// TODO Windows: Port this test
158
-	if runtime.GOOS == "windows" {
159
-		t.Skip("Needs porting to Windows")
160
-	}
161
-	cmd := exec.Command("sh", "-c", "while true ; do echo 1 ; sleep .1 ; done")
162
-	out, exitCode, timedOut, err := RunCommandWithOutputForDuration(cmd, 500*time.Millisecond)
163
-	ones := strings.Split(out, "\n")
164
-	if len(ones) != 6 || exitCode != 0 || !timedOut || err != nil {
165
-		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)
166
-	}
167
-}
168
-
169
-func TestRunCommandWithOutputForDurationErrors(t *testing.T) {
170
-	cmd := exec.Command("ls")
171
-	cmd.Stdout = os.Stdout
172
-	if _, _, _, err := RunCommandWithOutputForDuration(cmd, 1*time.Millisecond); err == nil || err.Error() != "cmd.Stdout already set" {
173
-		t.Fatalf("Expected an error as cmd.Stdout was already set, did not (err:%s).", err)
174
-	}
175
-	cmd = exec.Command("ls")
176
-	cmd.Stderr = os.Stderr
177
-	if _, _, _, err := RunCommandWithOutputForDuration(cmd, 1*time.Millisecond); err == nil || err.Error() != "cmd.Stderr already set" {
178
-		t.Fatalf("Expected an error as cmd.Stderr was already set, did not (err:%s).", err)
179
-	}
180
-}
181
-
182
-func TestRunCommandWithOutputAndTimeoutFinished(t *testing.T) {
183
-	// TODO Windows: Port this test
184
-	if runtime.GOOS == "windows" {
185
-		t.Skip("Needs porting to Windows")
186
-	}
187
-
188
-	cmd := exec.Command("ls")
189
-	out, exitCode, err := RunCommandWithOutputAndTimeout(cmd, 50*time.Millisecond)
190
-	if out == "" || exitCode != 0 || err != nil {
191
-		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)
192
-	}
193
-}
194
-
195
-func TestRunCommandWithOutputAndTimeoutKilled(t *testing.T) {
196
-	// TODO Windows: Port this test
197
-	if runtime.GOOS == "windows" {
198
-		t.Skip("Needs porting to Windows")
199
-	}
200
-
201
-	cmd := exec.Command("sh", "-c", "while true ; do echo 1 ; sleep .1 ; done")
202
-	out, exitCode, err := RunCommandWithOutputAndTimeout(cmd, 500*time.Millisecond)
203
-	ones := strings.Split(out, "\n")
204
-	if len(ones) != 6 || exitCode != 0 || err == nil || err.Error() != "command timed out" {
205
-		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)
206
-	}
207
-}
208
-
209
-func TestRunCommandWithOutputAndTimeoutErrors(t *testing.T) {
210
-	cmd := exec.Command("ls")
211
-	cmd.Stdout = os.Stdout
212
-	if _, _, err := RunCommandWithOutputAndTimeout(cmd, 1*time.Millisecond); err == nil || err.Error() != "cmd.Stdout already set" {
213
-		t.Fatalf("Expected an error as cmd.Stdout was already set, did not (err:%s).", err)
214
-	}
215
-	cmd = exec.Command("ls")
216
-	cmd.Stderr = os.Stderr
217
-	if _, _, err := RunCommandWithOutputAndTimeout(cmd, 1*time.Millisecond); err == nil || err.Error() != "cmd.Stderr already set" {
218
-		t.Fatalf("Expected an error as cmd.Stderr was already set, did not (err:%s).", err)
219
-	}
220
-}
221
-
222
-func TestRunCommand(t *testing.T) {
223
-	// TODO Windows: Port this test
224
-	if runtime.GOOS == "windows" {
225
-		t.Skip("Needs porting to Windows")
226
-	}
227
-
228
-	p := "$PATH"
229
-	if runtime.GOOS == "windows" {
230
-		p = "%PATH%"
231
-	}
232
-	lsCmd := exec.Command("ls")
233
-	exitCode, err := RunCommand(lsCmd)
234
-	if exitCode != 0 || err != nil {
235
-		t.Fatalf("Expected runCommand to run the command successfully, got: exitCode:%d, err:%v", exitCode, err)
236
-	}
237
-
238
-	var expectedError string
239
-
240
-	exitCode, err = RunCommand(exec.Command("doesnotexists"))
241
-	expectedError = `exec: "doesnotexists": executable file not found in ` + p
242
-	if exitCode != 127 || err == nil || err.Error() != expectedError {
243
-		t.Fatalf("Expected runCommand to run the command successfully, got: exitCode:%d, err:%v", exitCode, err)
244
-	}
245
-	wrongLsCmd := exec.Command("ls", "-z")
246
-	expected := 2
247
-	expectedError = `exit status 2`
248
-	exitCode, err = RunCommand(wrongLsCmd)
249
-	if exitCode != expected || err == nil || err.Error() != expectedError {
250
-		t.Fatalf("Expected runCommand to run the command successfully, got: exitCode:%d, err:%v", exitCode, err)
251
-	}
252
-}
253
-
254 57
 func TestRunCommandPipelineWithOutputWithNotEnoughCmds(t *testing.T) {
255 58
 	_, _, err := RunCommandPipelineWithOutput(exec.Command("ls"))
256 59
 	expectedError := "pipeline does not have multiple cmds"