diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 5102781a8c..95424b69cc 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -106,27 +106,9 @@ func TestBuildAddSingleFileToWorkdir(t *testing.T) { t.Fatal(err) } f.Close() - buildCmd := exec.Command(dockerBinary, "build", "-t", "testaddimg", ".") - buildCmd.Dir = buildDirectory - done := make(chan error) - go func() { - out, exitCode, err := runCommandWithOutput(buildCmd) - if err != nil || exitCode != 0 { - done <- fmt.Errorf("build failed to complete: %s %v", out, err) - return - } - done <- nil - }() - select { - case <-time.After(5 * time.Second): - if err := buildCmd.Process.Kill(); err != nil { - fmt.Printf("could not kill build (pid=%d): %v\n", buildCmd.Process.Pid, err) - } - t.Fatal("build timed out") - case err := <-done: - if err != nil { - t.Fatal(err) - } + _, exitCode, err := dockerCmdInDirWithTimeout(5*time.Second, buildDirectory, "build", "-t", "testaddimg", ".") + if err != nil || exitCode != 0 { + t.Fatalf("build failed: %s", err) } deleteImages("testaddimg") @@ -343,27 +325,9 @@ func TestBuildCopySingleFileToWorkdir(t *testing.T) { t.Fatal(err) } f.Close() - buildCmd := exec.Command(dockerBinary, "build", "-t", "testcopyimg", ".") - buildCmd.Dir = buildDirectory - done := make(chan error) - go func() { - out, exitCode, err := runCommandWithOutput(buildCmd) - if err != nil || exitCode != 0 { - done <- fmt.Errorf("build failed to complete: %s %v", out, err) - return - } - done <- nil - }() - select { - case <-time.After(5 * time.Second): - if err := buildCmd.Process.Kill(); err != nil { - fmt.Printf("could not kill build (pid=%d): %v\n", buildCmd.Process.Pid, err) - } - t.Fatal("build timed out") - case err := <-done: - if err != nil { - t.Fatal(err) - } + _, exitCode, err := dockerCmdInDirWithTimeout(5*time.Second, buildDirectory, "build", "-t", "testcopyimg", ".") + if err != nil || exitCode != 0 { + t.Fatalf("build failed: %s", err) } deleteImages("testcopyimg") diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index 8fb5074e93..91418ea0a4 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -345,12 +345,34 @@ func dockerCmd(t *testing.T, args ...string) (string, int, error) { return out, status, err } +// execute a docker ocmmand with a timeout +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("'%s' failed with errors: %v : %q)", strings.Join(args, " "), err, out) + } + return out, status, err +} + // execute a docker command in a directory func dockerCmdInDir(t *testing.T, path string, args ...string) (string, int, error) { dockerCommand := exec.Command(dockerBinary, args...) dockerCommand.Dir = path out, status, err := runCommandWithOutput(dockerCommand) - errorOut(err, t, fmt.Sprintf("'%s' failed with errors: %v (%v)", strings.Join(args, " "), err, out)) + if err != nil { + return out, status, fmt.Errorf("'%s' failed with errors: %v : %q)", strings.Join(args, " "), err, out) + } + return out, status, err +} + +// execute a docker command in a directory with a timeout +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("'%s' failed with errors: %v : %q)", strings.Join(args, " "), err, out) + } return out, status, err } @@ -486,6 +508,36 @@ func getIDByName(name string) (string, error) { return inspectField(name, "Id") } +// getContainerState returns the exit code of the container +// and true if it's running +// the exit code should be ignored if it's running +func getContainerState(t *testing.T, id string) (int, bool, error) { + var ( + exitStatus int + running bool + ) + out, exitCode, err := dockerCmd(t, "inspect", "--format={{.State.Running}} {{.State.ExitCode}}", id) + if err != nil || exitCode != 0 { + return 0, false, fmt.Errorf("'%s' doesn't exist: %s", id, err) + } + + out = strings.Trim(out, "\n") + splitOutput := strings.Split(out, " ") + if len(splitOutput) != 2 { + return 0, false, fmt.Errorf("failed to get container state: output is broken") + } + if splitOutput[0] == "true" { + running = true + } + if n, err := strconv.Atoi(splitOutput[1]); err == nil { + exitStatus = n + } else { + return 0, false, fmt.Errorf("failed to get container state: couldn't parse integer") + } + + return exitStatus, running, nil +} + func buildImageWithOut(name, dockerfile string, useCache bool) (string, string, error) { args := []string{"build", "-t", name} if !useCache { diff --git a/integration-cli/utils.go b/integration-cli/utils.go index c686a0b085..ae4cd61a39 100644 --- a/integration-cli/utils.go +++ b/integration-cli/utils.go @@ -28,9 +28,7 @@ func getExitCode(err error) (int, error) { return exitCode, fmt.Errorf("failed to get exit code") } -func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) { - exitCode = 0 - out, err := cmd.CombinedOutput() +func processExitCode(err error) (exitCode int) { if err != nil { var exiterr error if exitCode, exiterr = getExitCode(err); exiterr != nil { @@ -39,6 +37,13 @@ func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error exitCode = 127 } } + return +} + +func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) { + exitCode = 0 + out, err := cmd.CombinedOutput() + exitCode = processExitCode(err) output = string(out) return } @@ -51,45 +56,49 @@ func runCommandWithStdoutStderr(cmd *exec.Cmd) (stdout string, stderr string, ex cmd.Stderr = &stderrBuffer cmd.Stdout = &stdoutBuffer err = cmd.Run() + exitCode = processExitCode(err) - if err != nil { - var exiterr error - if exitCode, exiterr = getExitCode(err); exiterr != nil { - // TODO: Fix this so we check the error's text. - // we've failed to retrieve exit code, so we set it to 127 - exitCode = 127 - } - } stdout = stdoutBuffer.String() stderr = stderrBuffer.String() return } +var ErrCmdTimeout = fmt.Errorf("command timed out") + +func runCommandWithOutputAndTimeout(cmd *exec.Cmd, timeout time.Duration) (output string, exitCode int, err error) { + done := make(chan error) + go func() { + output, exitCode, err = runCommandWithOutput(cmd) + if err != nil || exitCode != 0 { + done <- fmt.Errorf("failed to run command: %s", err) + return + } + done <- nil + }() + select { + case <-time.After(timeout): + killFailed := cmd.Process.Kill() + if killFailed == nil { + fmt.Printf("failed to kill (pid=%d): %v\n", cmd.Process.Pid, err) + } + err = ErrCmdTimeout + case <-done: + break + } + return +} + func runCommand(cmd *exec.Cmd) (exitCode int, err error) { exitCode = 0 err = cmd.Run() - if err != nil { - var exiterr error - if exitCode, exiterr = getExitCode(err); exiterr != nil { - // TODO: Fix this so we check the error's text. - // we've failed to retrieve exit code, so we set it to 127 - exitCode = 127 - } - } + exitCode = processExitCode(err) return } func startCommand(cmd *exec.Cmd) (exitCode int, err error) { exitCode = 0 err = cmd.Start() - if err != nil { - var exiterr error - if exitCode, exiterr = getExitCode(err); exiterr != nil { - // TODO: Fix this so we check the error's text. - // we've failed to retrieve exit code, so we set it to 127 - exitCode = 127 - } - } + exitCode = processExitCode(err) return }