mirror of https://github.com/docker/docs.git
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:
parent
417e48e4a0
commit
c2cf97a074
|
@ -7,6 +7,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/engine"
|
"github.com/docker/docker/engine"
|
||||||
|
@ -112,24 +113,36 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status {
|
||||||
}
|
}
|
||||||
if follow && container.IsRunning() {
|
if follow && container.IsRunning() {
|
||||||
errors := make(chan error, 2)
|
errors := make(chan error, 2)
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
if stdout {
|
if stdout {
|
||||||
|
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, job.Stdout, format)
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if stderr {
|
if stderr {
|
||||||
|
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, job.Stderr, format)
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
err := <-errors
|
|
||||||
if err != nil {
|
wg.Wait()
|
||||||
log.Errorf("%s", err)
|
close(errors)
|
||||||
|
|
||||||
|
for err := range errors {
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("%s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return engine.StatusOK
|
return engine.StatusOK
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,3 +284,54 @@ func TestLogsFollowStopped(t *testing.T) {
|
||||||
deleteContainer(cleanedContainerID)
|
deleteContainer(cleanedContainerID)
|
||||||
logDone("logs - logs follow stopped container")
|
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")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue