mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Remove job from logs
Signed-off-by: Antonio Murdaca <me@runcom.ninja>
This commit is contained in:
parent
e3dd323336
commit
91bfed6049
13 changed files with 151 additions and 197 deletions
|
@ -557,43 +557,26 @@ func getContainersLogs(eng *engine.Engine, version version.Version, w http.Respo
|
||||||
return fmt.Errorf("Missing parameter")
|
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
|
// 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) {
|
if !(stdout || stderr) {
|
||||||
return fmt.Errorf("Bad parameters: you must choose at least one stream")
|
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
|
d := getDaemon(eng)
|
||||||
outStream = utils.NewWriteFlusher(w)
|
if err := d.ContainerLogs(vars["name"], logsConfig); err != nil {
|
||||||
|
fmt.Fprintf(w, "Error running logs job: %s\n", err)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/api"
|
"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) {
|
func TestGetImagesByName(t *testing.T) {
|
||||||
eng := engine.New()
|
eng := engine.New()
|
||||||
name := "image_name"
|
name := "image_name"
|
||||||
|
|
|
@ -123,7 +123,6 @@ func (daemon *Daemon) Install(eng *engine.Engine) error {
|
||||||
"create": daemon.ContainerCreate,
|
"create": daemon.ContainerCreate,
|
||||||
"export": daemon.ContainerExport,
|
"export": daemon.ContainerExport,
|
||||||
"info": daemon.CmdInfo,
|
"info": daemon.CmdInfo,
|
||||||
"logs": daemon.ContainerLogs,
|
|
||||||
"restart": daemon.ContainerRestart,
|
"restart": daemon.ContainerRestart,
|
||||||
"start": daemon.ContainerStart,
|
"start": daemon.ContainerStart,
|
||||||
"stop": daemon.ContainerStop,
|
"stop": daemon.ContainerStop,
|
||||||
|
|
|
@ -10,40 +10,50 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/engine"
|
|
||||||
"github.com/docker/docker/pkg/jsonlog"
|
"github.com/docker/docker/pkg/jsonlog"
|
||||||
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
"github.com/docker/docker/pkg/tailfile"
|
"github.com/docker/docker/pkg/tailfile"
|
||||||
"github.com/docker/docker/pkg/timeutils"
|
"github.com/docker/docker/pkg/timeutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (daemon *Daemon) ContainerLogs(job *engine.Job) error {
|
type ContainerLogsConfig struct {
|
||||||
if len(job.Args) != 1 {
|
Follow, Timestamps bool
|
||||||
return fmt.Errorf("Usage: %s CONTAINER\n", job.Name)
|
Tail string
|
||||||
|
UseStdout, UseStderr bool
|
||||||
|
OutStream io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (daemon *Daemon) ContainerLogs(name string, config *ContainerLogsConfig) error {
|
||||||
var (
|
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
|
lines = -1
|
||||||
format string
|
format string
|
||||||
)
|
)
|
||||||
if !(stdout || stderr) {
|
if !(config.UseStdout || config.UseStderr) {
|
||||||
return fmt.Errorf("You must choose at least one stream")
|
return fmt.Errorf("You must choose at least one stream")
|
||||||
}
|
}
|
||||||
if times {
|
if config.Timestamps {
|
||||||
format = timeutils.RFC3339NanoFixed
|
format = timeutils.RFC3339NanoFixed
|
||||||
}
|
}
|
||||||
if tail == "" {
|
if config.Tail == "" {
|
||||||
tail = "all"
|
config.Tail = "all"
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := daemon.Get(name)
|
container, err := daemon.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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" {
|
if container.LogDriverType() != "json-file" {
|
||||||
return fmt.Errorf("\"logs\" endpoint is supported only for \"json-file\" logging driver")
|
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) {
|
if err != nil && os.IsNotExist(err) {
|
||||||
// Legacy logs
|
// Legacy logs
|
||||||
logrus.Debugf("Old logs format")
|
logrus.Debugf("Old logs format")
|
||||||
if stdout {
|
if config.UseStdout {
|
||||||
cLog, err := container.ReadLog("stdout")
|
cLog, err := container.ReadLog("stdout")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("Error reading logs (stdout): %s", err)
|
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)
|
logrus.Errorf("Error streaming logs (stdout): %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if stderr {
|
if config.UseStderr {
|
||||||
cLog, err := container.ReadLog("stderr")
|
cLog, err := container.ReadLog("stderr")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("Error reading logs (stderr): %s", err)
|
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)
|
logrus.Errorf("Error streaming logs (stderr): %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
logrus.Errorf("Error reading logs (json): %s", err)
|
logrus.Errorf("Error reading logs (json): %s", err)
|
||||||
} else {
|
} else {
|
||||||
if tail != "all" {
|
if config.Tail != "all" {
|
||||||
var err error
|
var err error
|
||||||
lines, err = strconv.Atoi(tail)
|
lines, err = strconv.Atoi(config.Tail)
|
||||||
if err != nil {
|
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
|
lines = -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,39 +111,39 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) error {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
logLine := l.Log
|
logLine := l.Log
|
||||||
if times {
|
if config.Timestamps {
|
||||||
// format can be "" or time format, so here can't be error
|
// format can be "" or time format, so here can't be error
|
||||||
logLine, _ = l.Format(format)
|
logLine, _ = l.Format(format)
|
||||||
}
|
}
|
||||||
if l.Stream == "stdout" && stdout {
|
if l.Stream == "stdout" && config.UseStdout {
|
||||||
io.WriteString(job.Stdout, logLine)
|
io.WriteString(outStream, logLine)
|
||||||
}
|
}
|
||||||
if l.Stream == "stderr" && stderr {
|
if l.Stream == "stderr" && config.UseStderr {
|
||||||
io.WriteString(job.Stderr, logLine)
|
io.WriteString(errStream, logLine)
|
||||||
}
|
}
|
||||||
l.Reset()
|
l.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if follow && container.IsRunning() {
|
if config.Follow && container.IsRunning() {
|
||||||
errors := make(chan error, 2)
|
errors := make(chan error, 2)
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
if stdout {
|
if config.UseStdout {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
stdoutPipe := container.StdoutLogPipe()
|
stdoutPipe := container.StdoutLogPipe()
|
||||||
defer stdoutPipe.Close()
|
defer stdoutPipe.Close()
|
||||||
go func() {
|
go func() {
|
||||||
errors <- jsonlog.WriteLog(stdoutPipe, job.Stdout, format)
|
errors <- jsonlog.WriteLog(stdoutPipe, outStream, format)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if stderr {
|
if config.UseStderr {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
stderrPipe := container.StderrLogPipe()
|
stderrPipe := container.StderrLogPipe()
|
||||||
defer stderrPipe.Close()
|
defer stderrPipe.Close()
|
||||||
go func() {
|
go func() {
|
||||||
errors <- jsonlog.WriteLog(stderrPipe, job.Stderr, format)
|
errors <- jsonlog.WriteLog(stderrPipe, errStream, format)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,15 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"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"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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) {
|
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)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("GET all containers sockRequest failed: %v", err)
|
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)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("GET containers/export sockRequest failed: %v", err)
|
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)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("GET containers/changes sockRequest failed: %v", err)
|
t.Fatalf("GET containers/changes sockRequest failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -133,7 +134,7 @@ func TestContainerApiStartVolumeBinds(t *testing.T) {
|
||||||
"Volumes": map[string]struct{}{"/tmp": {}},
|
"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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +142,7 @@ func TestContainerApiStartVolumeBinds(t *testing.T) {
|
||||||
config = map[string]interface{}{
|
config = map[string]interface{}{
|
||||||
"Binds": []string{bindPath + ":/tmp"},
|
"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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +167,7 @@ func TestContainerApiStartDupVolumeBinds(t *testing.T) {
|
||||||
"Volumes": map[string]struct{}{"/tmp": {}},
|
"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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +177,7 @@ func TestContainerApiStartDupVolumeBinds(t *testing.T) {
|
||||||
config = map[string]interface{}{
|
config = map[string]interface{}{
|
||||||
"Binds": []string{bindPath1 + ":/tmp", bindPath2 + ":/tmp"},
|
"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")
|
t.Fatal("expected container start to fail when duplicate volume binds to same container path")
|
||||||
} else {
|
} else {
|
||||||
if !strings.Contains(string(body), "Duplicate volume") {
|
if !strings.Contains(string(body), "Duplicate volume") {
|
||||||
|
@ -201,14 +202,14 @@ func TestContainerApiStartVolumesFrom(t *testing.T) {
|
||||||
"Volumes": map[string]struct{}{volPath: {}},
|
"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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config = map[string]interface{}{
|
config = map[string]interface{}{
|
||||||
"VolumesFrom": []string{volName},
|
"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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +246,7 @@ func TestVolumesFromHasPriority(t *testing.T) {
|
||||||
"Volumes": map[string]struct{}{volPath: {}},
|
"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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +255,7 @@ func TestVolumesFromHasPriority(t *testing.T) {
|
||||||
"VolumesFrom": []string{volName},
|
"VolumesFrom": []string{volName},
|
||||||
"Binds": []string{bindPath + ":/tmp"},
|
"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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +291,7 @@ func TestGetContainerStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
bc := make(chan b, 1)
|
bc := make(chan b, 1)
|
||||||
go func() {
|
go func() {
|
||||||
body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
|
_, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
|
||||||
bc <- b{body, err}
|
bc <- b{body, err}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -334,7 +335,7 @@ func TestGetStoppedContainerStats(t *testing.T) {
|
||||||
go func() {
|
go func() {
|
||||||
// We'll never get return for GET stats from sockRequest as of now,
|
// 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.
|
// 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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -367,7 +368,7 @@ func TestBuildApiDockerfilePath(t *testing.T) {
|
||||||
t.Fatalf("failed to close tar archive: %v", err)
|
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 {
|
if err == nil {
|
||||||
t.Fatalf("Build was supposed to fail: %s", out)
|
t.Fatalf("Build was supposed to fail: %s", out)
|
||||||
}
|
}
|
||||||
|
@ -391,7 +392,7 @@ RUN find /tmp/`,
|
||||||
}
|
}
|
||||||
defer server.Close()
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("Build failed: %s", err)
|
t.Fatalf("Build failed: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -417,7 +418,7 @@ RUN echo from dockerfile`,
|
||||||
}
|
}
|
||||||
defer git.Close()
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("Build failed: %s\n%q", err, buf)
|
t.Fatalf("Build failed: %s\n%q", err, buf)
|
||||||
}
|
}
|
||||||
|
@ -443,7 +444,7 @@ RUN echo from Dockerfile`,
|
||||||
defer git.Close()
|
defer git.Close()
|
||||||
|
|
||||||
// Make sure it tries to 'dockerfile' query param value
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("Build failed: %s\n%q", err, buf)
|
t.Fatalf("Build failed: %s\n%q", err, buf)
|
||||||
}
|
}
|
||||||
|
@ -470,7 +471,7 @@ RUN echo from dockerfile`,
|
||||||
defer git.Close()
|
defer git.Close()
|
||||||
|
|
||||||
// Make sure it tries to 'dockerfile' query param value
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("Build failed: %s", err)
|
t.Fatalf("Build failed: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -501,7 +502,7 @@ func TestBuildApiDockerfileSymlink(t *testing.T) {
|
||||||
t.Fatalf("failed to close tar archive: %v", err)
|
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 {
|
if err == nil {
|
||||||
t.Fatalf("Build was supposed to fail: %s", out)
|
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"}}
|
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") {
|
if err != nil && !strings.Contains(err.Error(), "204 No Content") {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -565,7 +566,7 @@ func TestContainerApiPause(t *testing.T) {
|
||||||
}
|
}
|
||||||
ContainerID := strings.TrimSpace(out)
|
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)
|
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))
|
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)
|
t.Fatalf("POST a container pause: sockRequest failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ func TestExecApiCreateNoCmd(t *testing.T) {
|
||||||
t.Fatal(out, err)
|
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")) {
|
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)
|
t.Fatalf("Expected error when creating exec command with no Cmd specified: %q", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLegacyImages(t *testing.T) {
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("Error on GET: %s", err)
|
t.Fatalf("Error on GET: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ func TestInspectApiContainerResponse(t *testing.T) {
|
||||||
if testVersion != "latest" {
|
if testVersion != "latest" {
|
||||||
endpoint = "/" + testVersion + endpoint
|
endpoint = "/" + testVersion + endpoint
|
||||||
}
|
}
|
||||||
body, err := sockRequest("GET", endpoint, nil)
|
_, body, err := sockRequest("GET", endpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("sockRequest failed for %s version: %v", testVersion, err)
|
t.Fatalf("sockRequest failed for %s version: %v", testVersion, err)
|
||||||
}
|
}
|
||||||
|
|
53
integration-cli/docker_api_logs_test.go
Normal file
53
integration-cli/docker_api_logs_test.go
Normal file
|
@ -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")
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ func TestResizeApiResponse(t *testing.T) {
|
||||||
cleanedContainerID := strings.TrimSpace(out)
|
cleanedContainerID := strings.TrimSpace(out)
|
||||||
|
|
||||||
endpoint := "/containers/" + cleanedContainerID + "/resize?h=40&w=40"
|
endpoint := "/containers/" + cleanedContainerID + "/resize?h=40&w=40"
|
||||||
_, err = sockRequest("POST", endpoint, nil)
|
_, _, err = sockRequest("POST", endpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("resize Request failed %v", err)
|
t.Fatalf("resize Request failed %v", err)
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ func TestResizeApiResponseWhenContainerNotStarted(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoint := "/containers/" + cleanedContainerID + "/resize?h=40&w=40"
|
endpoint := "/containers/" + cleanedContainerID + "/resize?h=40&w=40"
|
||||||
body, err := sockRequest("POST", endpoint, nil)
|
_, body, err := sockRequest("POST", endpoint, nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("resize should fail when container is not started")
|
t.Fatalf("resize should fail when container is not started")
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ func TestRmRunningContainerCheckError409(t *testing.T) {
|
||||||
createRunningContainer(t, "foo")
|
createRunningContainer(t, "foo")
|
||||||
|
|
||||||
endpoint := "/containers/foo"
|
endpoint := "/containers/foo"
|
||||||
_, err := sockRequest("DELETE", endpoint, nil)
|
_, _, err := sockRequest("DELETE", endpoint, nil)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expected error, can't rm a running container")
|
t.Fatalf("Expected error, can't rm a running container")
|
||||||
|
|
|
@ -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)
|
jsonData := bytes.NewBuffer(nil)
|
||||||
if err := json.NewEncoder(jsonData).Encode(data); err != nil {
|
if err := json.NewEncoder(jsonData).Encode(data); err != nil {
|
||||||
return nil, err
|
return -1, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return sockRequestRaw(method, endpoint, jsonData, "application/json")
|
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))
|
c, err := sockConn(time.Duration(10 * time.Second))
|
||||||
if err != nil {
|
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)
|
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)
|
req, err := http.NewRequest(method, endpoint, data)
|
||||||
if err != nil {
|
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 == "" {
|
if ct == "" {
|
||||||
|
@ -328,15 +328,17 @@ func sockRequestRaw(method, endpoint string, data io.Reader, ct string) ([]byte,
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
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()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
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 {
|
func deleteContainer(container string) error {
|
||||||
|
@ -1041,7 +1043,7 @@ func daemonTime(t *testing.T) time.Time {
|
||||||
return time.Now()
|
return time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := sockRequest("GET", "/info", nil)
|
_, body, err := sockRequest("GET", "/info", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("daemonTime: failed to get /info: %v", err)
|
t.Fatalf("daemonTime: failed to get /info: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ var (
|
||||||
func() bool {
|
func() bool {
|
||||||
if daemonExecDriver == "" {
|
if daemonExecDriver == "" {
|
||||||
// get daemon info
|
// get daemon info
|
||||||
body, err := sockRequest("GET", "/info", nil)
|
_, body, err := sockRequest("GET", "/info", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("sockRequest failed for /info: %v", err)
|
log.Fatalf("sockRequest failed for /info: %v", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue