diff --git a/api/server/server.go b/api/server/server.go index ffbef8cee9..262a5072ce 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -557,43 +557,26 @@ func getContainersLogs(eng *engine.Engine, version version.Version, w http.Respo return fmt.Errorf("Missing parameter") } - var ( - inspectJob = eng.Job("container_inspect", vars["name"]) - logsJob = eng.Job("logs", vars["name"]) - c, err = inspectJob.Stdout.AddEnv() - ) - if err != nil { - return err - } - logsJob.Setenv("follow", r.Form.Get("follow")) - logsJob.Setenv("tail", r.Form.Get("tail")) - logsJob.Setenv("stdout", r.Form.Get("stdout")) - logsJob.Setenv("stderr", r.Form.Get("stderr")) - logsJob.Setenv("timestamps", r.Form.Get("timestamps")) // Validate args here, because we can't return not StatusOK after job.Run() call - stdout, stderr := logsJob.GetenvBool("stdout"), logsJob.GetenvBool("stderr") + stdout, stderr := toBool(r.Form.Get("stdout")), toBool(r.Form.Get("stderr")) if !(stdout || stderr) { return fmt.Errorf("Bad parameters: you must choose at least one stream") } - if err = inspectJob.Run(); err != nil { - return err + + logsConfig := &daemon.ContainerLogsConfig{ + Follow: toBool(r.Form.Get("follow")), + Timestamps: toBool(r.Form.Get("timestamps")), + Tail: r.Form.Get("tail"), + UseStdout: stdout, + UseStderr: stderr, + OutStream: utils.NewWriteFlusher(w), } - var outStream, errStream io.Writer - outStream = utils.NewWriteFlusher(w) - - if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") { - errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) - outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) - } else { - errStream = outStream + d := getDaemon(eng) + if err := d.ContainerLogs(vars["name"], logsConfig); err != nil { + fmt.Fprintf(w, "Error running logs job: %s\n", err) } - logsJob.Stdout.Add(outStream) - logsJob.Stderr.Set(errStream) - if err := logsJob.Run(); err != nil { - fmt.Fprintf(outStream, "Error running logs job: %s\n", err) - } return nil } diff --git a/api/server/server_unit_test.go b/api/server/server_unit_test.go index b5dd984b0c..78b95b26bb 100644 --- a/api/server/server_unit_test.go +++ b/api/server/server_unit_test.go @@ -7,7 +7,6 @@ import ( "io" "net/http" "net/http/httptest" - "strings" "testing" "github.com/docker/docker/api" @@ -126,99 +125,6 @@ func TestGetContainersByName(t *testing.T) { } } -func TestLogs(t *testing.T) { - eng := engine.New() - var inspect bool - var logs bool - eng.Register("container_inspect", func(job *engine.Job) error { - inspect = true - if len(job.Args) == 0 { - t.Fatal("Job arguments is empty") - } - if job.Args[0] != "test" { - t.Fatalf("Container name %s, must be test", job.Args[0]) - } - return nil - }) - expected := "logs" - eng.Register("logs", func(job *engine.Job) error { - logs = true - if len(job.Args) == 0 { - t.Fatal("Job arguments is empty") - } - if job.Args[0] != "test" { - t.Fatalf("Container name %s, must be test", job.Args[0]) - } - follow := job.Getenv("follow") - if follow != "1" { - t.Fatalf("follow: %s, must be 1", follow) - } - stdout := job.Getenv("stdout") - if stdout != "1" { - t.Fatalf("stdout %s, must be 1", stdout) - } - stderr := job.Getenv("stderr") - if stderr != "" { - t.Fatalf("stderr %s, must be empty", stderr) - } - timestamps := job.Getenv("timestamps") - if timestamps != "1" { - t.Fatalf("timestamps %s, must be 1", timestamps) - } - job.Stdout.Write([]byte(expected)) - return nil - }) - r := serveRequest("GET", "/containers/test/logs?follow=1&stdout=1×tamps=1", nil, eng, t) - if r.Code != http.StatusOK { - t.Fatalf("Got status %d, expected %d", r.Code, http.StatusOK) - } - if !inspect { - t.Fatal("container_inspect job was not called") - } - if !logs { - t.Fatal("logs job was not called") - } - res := r.Body.String() - if res != expected { - t.Fatalf("Output %s, expected %s", res, expected) - } -} - -func TestLogsNoStreams(t *testing.T) { - eng := engine.New() - var inspect bool - var logs bool - eng.Register("container_inspect", func(job *engine.Job) error { - inspect = true - if len(job.Args) == 0 { - t.Fatal("Job arguments is empty") - } - if job.Args[0] != "test" { - t.Fatalf("Container name %s, must be test", job.Args[0]) - } - return nil - }) - eng.Register("logs", func(job *engine.Job) error { - logs = true - return nil - }) - r := serveRequest("GET", "/containers/test/logs", nil, eng, t) - if r.Code != http.StatusBadRequest { - t.Fatalf("Got status %d, expected %d", r.Code, http.StatusBadRequest) - } - if inspect { - t.Fatal("container_inspect job was called, but it shouldn't") - } - if logs { - t.Fatal("logs job was called, but it shouldn't") - } - res := strings.TrimSpace(r.Body.String()) - expected := "Bad parameters: you must choose at least one stream" - if !strings.Contains(res, expected) { - t.Fatalf("Output %s, expected %s in it", res, expected) - } -} - func TestGetImagesByName(t *testing.T) { eng := engine.New() name := "image_name" diff --git a/daemon/daemon.go b/daemon/daemon.go index 86ed71e231..505a09e0af 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -123,7 +123,6 @@ func (daemon *Daemon) Install(eng *engine.Engine) error { "create": daemon.ContainerCreate, "export": daemon.ContainerExport, "info": daemon.CmdInfo, - "logs": daemon.ContainerLogs, "restart": daemon.ContainerRestart, "start": daemon.ContainerStart, "stop": daemon.ContainerStop, diff --git a/daemon/logs.go b/daemon/logs.go index c991fa1978..79d4044bbe 100644 --- a/daemon/logs.go +++ b/daemon/logs.go @@ -10,40 +10,50 @@ import ( "sync" "github.com/Sirupsen/logrus" - "github.com/docker/docker/engine" "github.com/docker/docker/pkg/jsonlog" + "github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/tailfile" "github.com/docker/docker/pkg/timeutils" ) -func (daemon *Daemon) ContainerLogs(job *engine.Job) error { - if len(job.Args) != 1 { - return fmt.Errorf("Usage: %s CONTAINER\n", job.Name) - } +type ContainerLogsConfig struct { + Follow, Timestamps bool + Tail string + UseStdout, UseStderr bool + OutStream io.Writer +} +func (daemon *Daemon) ContainerLogs(name string, config *ContainerLogsConfig) error { var ( - name = job.Args[0] - stdout = job.GetenvBool("stdout") - stderr = job.GetenvBool("stderr") - tail = job.Getenv("tail") - follow = job.GetenvBool("follow") - times = job.GetenvBool("timestamps") lines = -1 format string ) - if !(stdout || stderr) { + if !(config.UseStdout || config.UseStderr) { return fmt.Errorf("You must choose at least one stream") } - if times { + if config.Timestamps { format = timeutils.RFC3339NanoFixed } - if tail == "" { - tail = "all" + if config.Tail == "" { + config.Tail = "all" } + container, err := daemon.Get(name) if err != nil { return err } + + var ( + outStream = config.OutStream + errStream io.Writer + ) + if !container.Config.Tty { + errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) + outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) + } else { + errStream = outStream + } + if container.LogDriverType() != "json-file" { return fmt.Errorf("\"logs\" endpoint is supported only for \"json-file\" logging driver") } @@ -51,30 +61,30 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) error { if err != nil && os.IsNotExist(err) { // Legacy logs logrus.Debugf("Old logs format") - if stdout { + if config.UseStdout { cLog, err := container.ReadLog("stdout") if err != nil { logrus.Errorf("Error reading logs (stdout): %s", err) - } else if _, err := io.Copy(job.Stdout, cLog); err != nil { + } else if _, err := io.Copy(outStream, cLog); err != nil { logrus.Errorf("Error streaming logs (stdout): %s", err) } } - if stderr { + if config.UseStderr { cLog, err := container.ReadLog("stderr") if err != nil { logrus.Errorf("Error reading logs (stderr): %s", err) - } else if _, err := io.Copy(job.Stderr, cLog); err != nil { + } else if _, err := io.Copy(errStream, cLog); err != nil { logrus.Errorf("Error streaming logs (stderr): %s", err) } } } else if err != nil { logrus.Errorf("Error reading logs (json): %s", err) } else { - if tail != "all" { + if config.Tail != "all" { var err error - lines, err = strconv.Atoi(tail) + lines, err = strconv.Atoi(config.Tail) if err != nil { - logrus.Errorf("Failed to parse tail %s, error: %v, show all logs", tail, err) + logrus.Errorf("Failed to parse tail %s, error: %v, show all logs", config.Tail, err) lines = -1 } } @@ -101,39 +111,39 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) error { break } logLine := l.Log - if times { + if config.Timestamps { // format can be "" or time format, so here can't be error logLine, _ = l.Format(format) } - if l.Stream == "stdout" && stdout { - io.WriteString(job.Stdout, logLine) + if l.Stream == "stdout" && config.UseStdout { + io.WriteString(outStream, logLine) } - if l.Stream == "stderr" && stderr { - io.WriteString(job.Stderr, logLine) + if l.Stream == "stderr" && config.UseStderr { + io.WriteString(errStream, logLine) } l.Reset() } } } - if follow && container.IsRunning() { + if config.Follow && container.IsRunning() { errors := make(chan error, 2) wg := sync.WaitGroup{} - if stdout { + if config.UseStdout { wg.Add(1) stdoutPipe := container.StdoutLogPipe() defer stdoutPipe.Close() go func() { - errors <- jsonlog.WriteLog(stdoutPipe, job.Stdout, format) + errors <- jsonlog.WriteLog(stdoutPipe, outStream, format) wg.Done() }() } - if stderr { + if config.UseStderr { wg.Add(1) stderrPipe := container.StderrLogPipe() defer stderrPipe.Close() go func() { - errors <- jsonlog.WriteLog(stderrPipe, job.Stderr, format) + errors <- jsonlog.WriteLog(stderrPipe, errStream, format) wg.Done() }() } diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index 07793a8fca..2771c7c02c 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -3,14 +3,15 @@ package main import ( "bytes" "encoding/json" - "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" "io" "os/exec" "strings" "testing" "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) func TestContainerApiGetAll(t *testing.T) { @@ -28,7 +29,7 @@ func TestContainerApiGetAll(t *testing.T) { t.Fatalf("Error on container creation: %v, output: %q", err, out) } - body, err := sockRequest("GET", "/containers/json?all=1", nil) + _, body, err := sockRequest("GET", "/containers/json?all=1", nil) if err != nil { t.Fatalf("GET all containers sockRequest failed: %v", err) } @@ -61,7 +62,7 @@ func TestContainerApiGetExport(t *testing.T) { t.Fatalf("Error on container creation: %v, output: %q", err, out) } - body, err := sockRequest("GET", "/containers/"+name+"/export", nil) + _, body, err := sockRequest("GET", "/containers/"+name+"/export", nil) if err != nil { t.Fatalf("GET containers/export sockRequest failed: %v", err) } @@ -98,7 +99,7 @@ func TestContainerApiGetChanges(t *testing.T) { t.Fatalf("Error on container creation: %v, output: %q", err, out) } - body, err := sockRequest("GET", "/containers/"+name+"/changes", nil) + _, body, err := sockRequest("GET", "/containers/"+name+"/changes", nil) if err != nil { t.Fatalf("GET containers/changes sockRequest failed: %v", err) } @@ -133,7 +134,7 @@ func TestContainerApiStartVolumeBinds(t *testing.T) { "Volumes": map[string]struct{}{"/tmp": {}}, } - if _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { + if _, _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { t.Fatal(err) } @@ -141,7 +142,7 @@ func TestContainerApiStartVolumeBinds(t *testing.T) { config = map[string]interface{}{ "Binds": []string{bindPath + ":/tmp"}, } - if _, err := sockRequest("POST", "/containers/"+name+"/start", config); err != nil && !strings.Contains(err.Error(), "204 No Content") { + if _, _, err := sockRequest("POST", "/containers/"+name+"/start", config); err != nil && !strings.Contains(err.Error(), "204 No Content") { t.Fatal(err) } @@ -166,7 +167,7 @@ func TestContainerApiStartDupVolumeBinds(t *testing.T) { "Volumes": map[string]struct{}{"/tmp": {}}, } - if _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { + if _, _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { t.Fatal(err) } @@ -176,7 +177,7 @@ func TestContainerApiStartDupVolumeBinds(t *testing.T) { config = map[string]interface{}{ "Binds": []string{bindPath1 + ":/tmp", bindPath2 + ":/tmp"}, } - if body, err := sockRequest("POST", "/containers/"+name+"/start", config); err == nil { + if _, body, err := sockRequest("POST", "/containers/"+name+"/start", config); err == nil { t.Fatal("expected container start to fail when duplicate volume binds to same container path") } else { if !strings.Contains(string(body), "Duplicate volume") { @@ -201,14 +202,14 @@ func TestContainerApiStartVolumesFrom(t *testing.T) { "Volumes": map[string]struct{}{volPath: {}}, } - if _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { + if _, _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { t.Fatal(err) } config = map[string]interface{}{ "VolumesFrom": []string{volName}, } - if _, err := sockRequest("POST", "/containers/"+name+"/start", config); err != nil && !strings.Contains(err.Error(), "204 No Content") { + if _, _, err := sockRequest("POST", "/containers/"+name+"/start", config); err != nil && !strings.Contains(err.Error(), "204 No Content") { t.Fatal(err) } @@ -245,7 +246,7 @@ func TestVolumesFromHasPriority(t *testing.T) { "Volumes": map[string]struct{}{volPath: {}}, } - if _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { + if _, _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { t.Fatal(err) } @@ -254,7 +255,7 @@ func TestVolumesFromHasPriority(t *testing.T) { "VolumesFrom": []string{volName}, "Binds": []string{bindPath + ":/tmp"}, } - if _, err := sockRequest("POST", "/containers/"+name+"/start", config); err != nil && !strings.Contains(err.Error(), "204 No Content") { + if _, _, err := sockRequest("POST", "/containers/"+name+"/start", config); err != nil && !strings.Contains(err.Error(), "204 No Content") { t.Fatal(err) } @@ -290,7 +291,7 @@ func TestGetContainerStats(t *testing.T) { } bc := make(chan b, 1) go func() { - body, err := sockRequest("GET", "/containers/"+name+"/stats", nil) + _, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil) bc <- b{body, err} }() @@ -334,7 +335,7 @@ func TestGetStoppedContainerStats(t *testing.T) { go func() { // We'll never get return for GET stats from sockRequest as of now, // just send request and see if panic or error would happen on daemon side. - _, err := sockRequest("GET", "/containers/"+name+"/stats", nil) + _, _, err := sockRequest("GET", "/containers/"+name+"/stats", nil) if err != nil { t.Fatal(err) } @@ -367,7 +368,7 @@ func TestBuildApiDockerfilePath(t *testing.T) { t.Fatalf("failed to close tar archive: %v", err) } - out, err := sockRequestRaw("POST", "/build?dockerfile=../Dockerfile", buffer, "application/x-tar") + _, out, err := sockRequestRaw("POST", "/build?dockerfile=../Dockerfile", buffer, "application/x-tar") if err == nil { t.Fatalf("Build was supposed to fail: %s", out) } @@ -391,7 +392,7 @@ RUN find /tmp/`, } defer server.Close() - buf, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL()+"/testD", nil, "application/json") + _, buf, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL()+"/testD", nil, "application/json") if err != nil { t.Fatalf("Build failed: %s", err) } @@ -417,7 +418,7 @@ RUN echo from dockerfile`, } defer git.Close() - buf, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json") + _, buf, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json") if err != nil { t.Fatalf("Build failed: %s\n%q", err, buf) } @@ -443,7 +444,7 @@ RUN echo from Dockerfile`, defer git.Close() // Make sure it tries to 'dockerfile' query param value - buf, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+git.RepoURL, nil, "application/json") + _, buf, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+git.RepoURL, nil, "application/json") if err != nil { t.Fatalf("Build failed: %s\n%q", err, buf) } @@ -470,7 +471,7 @@ RUN echo from dockerfile`, defer git.Close() // Make sure it tries to 'dockerfile' query param value - buf, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json") + _, buf, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json") if err != nil { t.Fatalf("Build failed: %s", err) } @@ -501,7 +502,7 @@ func TestBuildApiDockerfileSymlink(t *testing.T) { t.Fatalf("failed to close tar archive: %v", err) } - out, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar") + _, out, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar") if err == nil { t.Fatalf("Build was supposed to fail: %s", out) } @@ -537,7 +538,7 @@ func TestPostContainerBindNormalVolume(t *testing.T) { } bindSpec := map[string][]string{"Binds": {fooDir + ":/foo"}} - _, err = sockRequest("POST", "/containers/two/start", bindSpec) + _, _, err = sockRequest("POST", "/containers/two/start", bindSpec) if err != nil && !strings.Contains(err.Error(), "204 No Content") { t.Fatal(err) } @@ -565,7 +566,7 @@ func TestContainerApiPause(t *testing.T) { } ContainerID := strings.TrimSpace(out) - if _, err = sockRequest("POST", "/containers/"+ContainerID+"/pause", nil); err != nil && !strings.Contains(err.Error(), "204 No Content") { + if _, _, err = sockRequest("POST", "/containers/"+ContainerID+"/pause", nil); err != nil && !strings.Contains(err.Error(), "204 No Content") { t.Fatalf("POST a container pause: sockRequest failed: %v", err) } @@ -579,7 +580,7 @@ func TestContainerApiPause(t *testing.T) { t.Fatalf("there should be one paused container and not %d", len(pausedContainers)) } - if _, err = sockRequest("POST", "/containers/"+ContainerID+"/unpause", nil); err != nil && !strings.Contains(err.Error(), "204 No Content") { + if _, _, err = sockRequest("POST", "/containers/"+ContainerID+"/unpause", nil); err != nil && !strings.Contains(err.Error(), "204 No Content") { t.Fatalf("POST a container pause: sockRequest failed: %v", err) } diff --git a/integration-cli/docker_api_exec_test.go b/integration-cli/docker_api_exec_test.go index 1ed99a2561..f898250a11 100644 --- a/integration-cli/docker_api_exec_test.go +++ b/integration-cli/docker_api_exec_test.go @@ -18,7 +18,7 @@ func TestExecApiCreateNoCmd(t *testing.T) { t.Fatal(out, err) } - body, err := sockRequest("POST", fmt.Sprintf("/containers/%s/exec", name), map[string]interface{}{"Cmd": nil}) + _, body, err := sockRequest("POST", fmt.Sprintf("/containers/%s/exec", name), map[string]interface{}{"Cmd": nil}) if err == nil || !bytes.Contains(body, []byte("No exec command specified")) { t.Fatalf("Expected error when creating exec command with no Cmd specified: %q", err) } diff --git a/integration-cli/docker_api_images_test.go b/integration-cli/docker_api_images_test.go index 38d891fd5a..49cfb36da8 100644 --- a/integration-cli/docker_api_images_test.go +++ b/integration-cli/docker_api_images_test.go @@ -8,7 +8,7 @@ import ( ) func TestLegacyImages(t *testing.T) { - body, err := sockRequest("GET", "/v1.6/images/json", nil) + _, body, err := sockRequest("GET", "/v1.6/images/json", nil) if err != nil { t.Fatalf("Error on GET: %s", err) } diff --git a/integration-cli/docker_api_inspect_test.go b/integration-cli/docker_api_inspect_test.go index e43f10fd6e..43144f9165 100644 --- a/integration-cli/docker_api_inspect_test.go +++ b/integration-cli/docker_api_inspect_test.go @@ -27,7 +27,7 @@ func TestInspectApiContainerResponse(t *testing.T) { if testVersion != "latest" { endpoint = "/" + testVersion + endpoint } - body, err := sockRequest("GET", endpoint, nil) + _, body, err := sockRequest("GET", endpoint, nil) if err != nil { t.Fatalf("sockRequest failed for %s version: %v", testVersion, err) } diff --git a/integration-cli/docker_api_logs_test.go b/integration-cli/docker_api_logs_test.go new file mode 100644 index 0000000000..27eb31c339 --- /dev/null +++ b/integration-cli/docker_api_logs_test.go @@ -0,0 +1,53 @@ +package main + +import ( + "bytes" + "fmt" + "net/http" + "os/exec" + "testing" +) + +func TestLogsApiWithStdout(t *testing.T) { + defer deleteAllContainers() + name := "logs_test" + + runCmd := exec.Command(dockerBinary, "run", "-d", "-t", "--name", name, "busybox", "bin/sh", "-c", "sleep 10 && echo "+name) + if out, _, err := runCommandWithOutput(runCmd); err != nil { + t.Fatal(out, err) + } + + statusCode, body, err := sockRequest("GET", fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1×tamps=1", name), nil) + + if err != nil || statusCode != http.StatusOK { + t.Fatalf("Expected %d from logs request, got %d", http.StatusOK, statusCode) + } + + if !bytes.Contains(body, []byte(name)) { + t.Fatalf("Expected %s, got %s", name, string(body[:])) + } + + logDone("logs API - with stdout ok") +} + +func TestLogsApiNoStdoutNorStderr(t *testing.T) { + defer deleteAllContainers() + name := "logs_test" + runCmd := exec.Command(dockerBinary, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh") + if out, _, err := runCommandWithOutput(runCmd); err != nil { + t.Fatal(out, err) + } + + statusCode, body, err := sockRequest("GET", fmt.Sprintf("/containers/%s/logs", name), nil) + + if err == nil || statusCode != http.StatusBadRequest { + t.Fatalf("Expected %d from logs request, got %d", http.StatusBadRequest, statusCode) + } + + expected := "Bad parameters: you must choose at least one stream" + if !bytes.Contains(body, []byte(expected)) { + t.Fatalf("Expected %s, got %s", expected, string(body[:])) + } + + logDone("logs API - returns error when no stdout nor stderr specified") +} diff --git a/integration-cli/docker_api_resize_test.go b/integration-cli/docker_api_resize_test.go index be36be8c1d..27d7d10a0a 100644 --- a/integration-cli/docker_api_resize_test.go +++ b/integration-cli/docker_api_resize_test.go @@ -16,7 +16,7 @@ func TestResizeApiResponse(t *testing.T) { cleanedContainerID := strings.TrimSpace(out) endpoint := "/containers/" + cleanedContainerID + "/resize?h=40&w=40" - _, err = sockRequest("POST", endpoint, nil) + _, _, err = sockRequest("POST", endpoint, nil) if err != nil { t.Fatalf("resize Request failed %v", err) } @@ -41,7 +41,7 @@ func TestResizeApiResponseWhenContainerNotStarted(t *testing.T) { } endpoint := "/containers/" + cleanedContainerID + "/resize?h=40&w=40" - body, err := sockRequest("POST", endpoint, nil) + _, body, err := sockRequest("POST", endpoint, nil) if err == nil { t.Fatalf("resize should fail when container is not started") } diff --git a/integration-cli/docker_cli_rm_test.go b/integration-cli/docker_cli_rm_test.go index d01b36d45d..8f8ea7b664 100644 --- a/integration-cli/docker_cli_rm_test.go +++ b/integration-cli/docker_cli_rm_test.go @@ -64,7 +64,7 @@ func TestRmRunningContainerCheckError409(t *testing.T) { createRunningContainer(t, "foo") endpoint := "/containers/foo" - _, err := sockRequest("DELETE", endpoint, nil) + _, _, err := sockRequest("DELETE", endpoint, nil) if err == nil { t.Fatalf("Expected error, can't rm a running container") diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index 943c1e02a4..4984b578bc 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -298,19 +298,19 @@ func sockConn(timeout time.Duration) (net.Conn, error) { } } -func sockRequest(method, endpoint string, data interface{}) ([]byte, error) { +func sockRequest(method, endpoint string, data interface{}) (int, []byte, error) { jsonData := bytes.NewBuffer(nil) if err := json.NewEncoder(jsonData).Encode(data); err != nil { - return nil, err + return -1, nil, err } return sockRequestRaw(method, endpoint, jsonData, "application/json") } -func sockRequestRaw(method, endpoint string, data io.Reader, ct string) ([]byte, error) { +func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (int, []byte, error) { c, err := sockConn(time.Duration(10 * time.Second)) if err != nil { - return nil, fmt.Errorf("could not dial docker daemon: %v", err) + return -1, nil, fmt.Errorf("could not dial docker daemon: %v", err) } client := httputil.NewClientConn(c, nil) @@ -318,7 +318,7 @@ func sockRequestRaw(method, endpoint string, data io.Reader, ct string) ([]byte, req, err := http.NewRequest(method, endpoint, data) if err != nil { - return nil, fmt.Errorf("could not create new request: %v", err) + return -1, nil, fmt.Errorf("could not create new request: %v", err) } if ct == "" { @@ -328,15 +328,17 @@ func sockRequestRaw(method, endpoint string, data io.Reader, ct string) ([]byte, resp, err := client.Do(req) if err != nil { - return nil, fmt.Errorf("could not perform request: %v", err) + return -1, nil, fmt.Errorf("could not perform request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := ioutil.ReadAll(resp.Body) - return body, fmt.Errorf("received status != 200 OK: %s", resp.Status) + return resp.StatusCode, body, fmt.Errorf("received status != 200 OK: %s", resp.Status) } - return ioutil.ReadAll(resp.Body) + b, err := ioutil.ReadAll(resp.Body) + + return resp.StatusCode, b, err } func deleteContainer(container string) error { @@ -1041,7 +1043,7 @@ func daemonTime(t *testing.T) time.Time { return time.Now() } - body, err := sockRequest("GET", "/info", nil) + _, body, err := sockRequest("GET", "/info", nil) if err != nil { t.Fatalf("daemonTime: failed to get /info: %v", err) } diff --git a/integration-cli/requirements.go b/integration-cli/requirements.go index cdd9991873..9769e2d3a5 100644 --- a/integration-cli/requirements.go +++ b/integration-cli/requirements.go @@ -57,7 +57,7 @@ var ( func() bool { if daemonExecDriver == "" { // get daemon info - body, err := sockRequest("GET", "/info", nil) + _, body, err := sockRequest("GET", "/info", nil) if err != nil { log.Fatalf("sockRequest failed for /info: %v", err) }