Fix panic on slow log consumer.

Fixes #8832

All stdio streams need to finish writing before the
connection can be closed.

Signed-off-by: Tõnis Tiigi <tonistiigi@gmail.com> (github: tonistiigi)
This commit is contained in:
Tonis Tiigi 2014-10-30 21:33:26 +02:00
parent 417e48e4a0
commit c2cf97a074
2 changed files with 67 additions and 3 deletions

View File

@ -7,6 +7,7 @@ import (
"io"
"os"
"strconv"
"sync"
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/engine"
@ -112,24 +113,36 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status {
}
if follow && container.IsRunning() {
errors := make(chan error, 2)
wg := sync.WaitGroup{}
if stdout {
wg.Add(1)
stdoutPipe := container.StdoutLogPipe()
defer stdoutPipe.Close()
go func() {
errors <- jsonlog.WriteLog(stdoutPipe, job.Stdout, format)
wg.Done()
}()
}
if stderr {
wg.Add(1)
stderrPipe := container.StderrLogPipe()
defer stderrPipe.Close()
go func() {
errors <- jsonlog.WriteLog(stderrPipe, job.Stderr, format)
wg.Done()
}()
}
err := <-errors
if err != nil {
log.Errorf("%s", err)
wg.Wait()
close(errors)
for err := range errors {
if err != nil {
log.Errorf("%s", err)
}
}
}
return engine.StatusOK
}

View File

@ -284,3 +284,54 @@ func TestLogsFollowStopped(t *testing.T) {
deleteContainer(cleanedContainerID)
logDone("logs - logs follow stopped container")
}
// Regression test for #8832
func TestLogsFollowSlowStdoutConsumer(t *testing.T) {
runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "/bin/sh", "-c", `usleep 200000;yes X | head -c 200000`)
out, _, _, err := runCommandWithStdoutStderr(runCmd)
if err != nil {
t.Fatalf("run failed with errors: %s, %v", out, err)
}
cleanedContainerID := stripTrailingCharacters(out)
defer deleteContainer(cleanedContainerID)
stopSlowRead := make(chan bool)
go func() {
exec.Command(dockerBinary, "wait", cleanedContainerID).Run()
stopSlowRead <- true
}()
logCmd := exec.Command(dockerBinary, "logs", "-f", cleanedContainerID)
stdout, err := logCmd.StdoutPipe()
if err != nil {
t.Fatal(err)
}
if err := logCmd.Start(); err != nil {
t.Fatal(err)
}
// First read slowly
bytes1, err := consumeWithSpeed(stdout, 10, 50*time.Millisecond, stopSlowRead)
if err != nil {
t.Fatal(err)
}
// After the container has finished we can continue reading fast
bytes2, err := consumeWithSpeed(stdout, 32*1024, 0, nil)
if err != nil {
t.Fatal(err)
}
actual := bytes1 + bytes2
expected := 200000
if actual != expected {
t.Fatalf("Invalid bytes read: %d, expected %d", actual, expected)
}
logDone("logs - follow slow consumer")
}