Procházet zdrojové kódy

Merge pull request #16261 from vdemeester/integration-dockercmd

Move dockerCmd to pkg/integration and add tests to it
Brian Goff před 9 roky
rodič
revize
677a6eaf8f

+ 7 - 28
integration-cli/docker_utils.go

@@ -24,6 +24,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/httputils"
+	"github.com/docker/docker/pkg/integration"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/pkg/stringutils"
 	"github.com/go-check/check"
 	"github.com/go-check/check"
@@ -621,52 +622,30 @@ func pullImageIfNotExist(image string) (err error) {
 }
 }
 
 
 func dockerCmdWithError(args ...string) (string, int, error) {
 func dockerCmdWithError(args ...string) (string, int, error) {
-	return runCommandWithOutput(exec.Command(dockerBinary, args...))
+	return integration.DockerCmdWithError(dockerBinary, args...)
 }
 }
 
 
 func dockerCmdWithStdoutStderr(c *check.C, args ...string) (string, string, int) {
 func dockerCmdWithStdoutStderr(c *check.C, args ...string) (string, string, int) {
-	stdout, stderr, status, err := runCommandWithStdoutStderr(exec.Command(dockerBinary, args...))
-	if c != nil {
-		c.Assert(err, check.IsNil, check.Commentf("%q failed with errors: %s, %v", strings.Join(args, " "), stderr, err))
-	}
-	return stdout, stderr, status
+	return integration.DockerCmdWithStdoutStderr(dockerBinary, c, args...)
 }
 }
 
 
 func dockerCmd(c *check.C, args ...string) (string, int) {
 func dockerCmd(c *check.C, args ...string) (string, int) {
-	out, status, err := runCommandWithOutput(exec.Command(dockerBinary, args...))
-	c.Assert(err, check.IsNil, check.Commentf("%q failed with errors: %s, %v", strings.Join(args, " "), out, err))
-	return out, status
+	return integration.DockerCmd(dockerBinary, c, args...)
 }
 }
 
 
 // execute a docker command with a timeout
 // execute a docker command with a timeout
 func dockerCmdWithTimeout(timeout time.Duration, args ...string) (string, int, error) {
 func dockerCmdWithTimeout(timeout time.Duration, args ...string) (string, int, error) {
-	out, status, err := runCommandWithOutputAndTimeout(exec.Command(dockerBinary, args...), timeout)
-	if err != nil {
-		return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out)
-	}
-	return out, status, err
+	return integration.DockerCmdWithTimeout(dockerBinary, timeout, args...)
 }
 }
 
 
 // execute a docker command in a directory
 // execute a docker command in a directory
 func dockerCmdInDir(c *check.C, path string, args ...string) (string, int, error) {
 func dockerCmdInDir(c *check.C, path string, args ...string) (string, int, error) {
-	dockerCommand := exec.Command(dockerBinary, args...)
-	dockerCommand.Dir = path
-	out, status, err := runCommandWithOutput(dockerCommand)
-	if err != nil {
-		return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out)
-	}
-	return out, status, err
+	return integration.DockerCmdInDir(dockerBinary, path, args...)
 }
 }
 
 
 // execute a docker command in a directory with a timeout
 // 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) (string, int, error) {
-	dockerCommand := exec.Command(dockerBinary, args...)
-	dockerCommand.Dir = path
-	out, status, err := runCommandWithOutputAndTimeout(dockerCommand, timeout)
-	if err != nil {
-		return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out)
-	}
-	return out, status, err
+	return integration.DockerCmdInDirWithTimeout(dockerBinary, timeout, path, args...)
 }
 }
 
 
 func findContainerIP(c *check.C, id string, vargs ...string) string {
 func findContainerIP(c *check.C, id string, vargs ...string) string {

+ 71 - 0
pkg/integration/dockerCmd_utils.go

@@ -0,0 +1,71 @@
+package integration
+
+import (
+	"fmt"
+	"os/exec"
+	"strings"
+	"time"
+
+	"github.com/go-check/check"
+)
+
+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("%q 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("%q 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("%q 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("%q 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("%q failed with errors: %v : %q", strings.Join(args, " "), err, out)
+	}
+	return out, status, err
+}

+ 403 - 0
pkg/integration/dockerCmd_utils_test.go

@@ -0,0 +1,403 @@
+package integration
+
+import (
+	"fmt"
+	"os"
+	"os/exec"
+	"testing"
+
+	"github.com/go-check/check"
+	"io/ioutil"
+	"strings"
+	"time"
+)
+
+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{},
+			5 * time.Millisecond,
+			`Command doesnotexists not found.`,
+			1,
+			fmt.Errorf(`"" failed with errors: exit status 1 : "Command doesnotexists not found."`),
+		},
+		{
+			dockerBinary,
+			[]string{"an", "error"},
+			5 * 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"},
+			5 * 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{},
+			5 * 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"},
+			5 * 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"},
+			5 * 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.Millisecond)
+			fmt.Fprintf(os.Stdout, "too long, should be killed")
+			os.Exit(0)
+		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)
+}