Browse code

Move stream flushes to backend

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2015/12/19 23:43:10
Showing 5 changed files
... ...
@@ -44,14 +44,13 @@ type stateBackend interface {
44 44
 	ContainerUnpause(name string) error
45 45
 	ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error)
46 46
 	ContainerWait(name string, timeout time.Duration) (int, error)
47
-	Exists(id string) bool
48 47
 }
49 48
 
50 49
 // monitorBackend includes functions to implement to provide containers monitoring functionality.
51 50
 type monitorBackend interface {
52 51
 	ContainerChanges(name string) ([]archive.Change, error)
53 52
 	ContainerInspect(name string, size bool, version version.Version) (interface{}, error)
54
-	ContainerLogs(name string, config *backend.ContainerLogsConfig) error
53
+	ContainerLogs(name string, config *backend.ContainerLogsConfig, started chan struct{}) error
55 54
 	ContainerStats(name string, config *backend.ContainerStatsConfig) error
56 55
 	ContainerTop(name string, psArgs string) (*types.ContainerProcessList, error)
57 56
 
... ...
@@ -3,7 +3,6 @@ package container
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6
-	"io"
7 6
 	"net/http"
8 7
 	"strconv"
9 8
 	"strings"
... ...
@@ -15,7 +14,6 @@ import (
15 15
 	"github.com/docker/docker/api/server/httputils"
16 16
 	"github.com/docker/docker/api/types/backend"
17 17
 	derr "github.com/docker/docker/errors"
18
-	"github.com/docker/docker/pkg/ioutils"
19 18
 	"github.com/docker/docker/pkg/signal"
20 19
 	"github.com/docker/docker/pkg/term"
21 20
 	"github.com/docker/docker/runconfig"
... ...
@@ -66,14 +64,8 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
66 66
 	}
67 67
 
68 68
 	stream := httputils.BoolValueOrDefault(r, "stream", true)
69
-	var out io.Writer
70 69
 	if !stream {
71 70
 		w.Header().Set("Content-Type", "application/json")
72
-		out = w
73
-	} else {
74
-		wf := ioutils.NewWriteFlusher(w)
75
-		out = wf
76
-		defer wf.Close()
77 71
 	}
78 72
 
79 73
 	var closeNotifier <-chan bool
... ...
@@ -83,7 +75,7 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
83 83
 
84 84
 	config := &backend.ContainerStatsConfig{
85 85
 		Stream:    stream,
86
-		OutStream: out,
86
+		OutStream: w,
87 87
 		Stop:      closeNotifier,
88 88
 		Version:   string(httputils.VersionFromContext(ctx)),
89 89
 	}
... ...
@@ -112,22 +104,6 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
112 112
 	}
113 113
 
114 114
 	containerName := vars["name"]
115
-
116
-	if !s.backend.Exists(containerName) {
117
-		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
118
-	}
119
-
120
-	// write an empty chunk of data (this is to ensure that the
121
-	// HTTP Response is sent immediately, even if the container has
122
-	// not yet produced any data)
123
-	w.WriteHeader(http.StatusOK)
124
-	if flusher, ok := w.(http.Flusher); ok {
125
-		flusher.Flush()
126
-	}
127
-
128
-	output := ioutils.NewWriteFlusher(w)
129
-	defer output.Close()
130
-
131 115
 	logsConfig := &backend.ContainerLogsConfig{
132 116
 		ContainerLogsOptions: types.ContainerLogsOptions{
133 117
 			Follow:     httputils.BoolValue(r, "follow"),
... ...
@@ -137,15 +113,21 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
137 137
 			ShowStdout: stdout,
138 138
 			ShowStderr: stderr,
139 139
 		},
140
-		OutStream: output,
140
+		OutStream: w,
141 141
 		Stop:      closeNotifier,
142 142
 	}
143 143
 
144
-	if err := s.backend.ContainerLogs(containerName, logsConfig); err != nil {
145
-		// The client may be expecting all of the data we're sending to
146
-		// be multiplexed, so send it through OutStream, which will
147
-		// have been set up to handle that if needed.
148
-		fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err))
144
+	chStarted := make(chan struct{})
145
+	if err := s.backend.ContainerLogs(containerName, logsConfig, chStarted); err != nil {
146
+		select {
147
+		case <-chStarted:
148
+			// The client may be expecting all of the data we're sending to
149
+			// be multiplexed, so send it through OutStream, which will
150
+			// have been set up to handle that if needed.
151
+			fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err))
152
+		default:
153
+			return err
154
+		}
149 155
 	}
150 156
 
151 157
 	return nil
... ...
@@ -463,10 +445,6 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
463 463
 	}
464 464
 	containerName := vars["name"]
465 465
 
466
-	if !s.backend.Exists(containerName) {
467
-		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
468
-	}
469
-
470 466
 	var keys []byte
471 467
 	var err error
472 468
 	detachKeys := r.FormValue("detachKeys")
... ...
@@ -68,16 +68,9 @@ func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *
68 68
 	}
69 69
 
70 70
 	w.Header().Set("Content-Type", "application/json")
71
-
72
-	// This is to ensure that the HTTP status code is sent immediately,
73
-	// so that it will not block the receiver.
74
-	w.WriteHeader(http.StatusOK)
75
-	if flusher, ok := w.(http.Flusher); ok {
76
-		flusher.Flush()
77
-	}
78
-
79 71
 	output := ioutils.NewWriteFlusher(w)
80 72
 	defer output.Close()
73
+	output.Flush()
81 74
 
82 75
 	enc := json.NewEncoder(output)
83 76
 
... ...
@@ -11,13 +11,14 @@ import (
11 11
 	"github.com/docker/docker/daemon/logger"
12 12
 	"github.com/docker/docker/daemon/logger/jsonfilelog"
13 13
 	derr "github.com/docker/docker/errors"
14
+	"github.com/docker/docker/pkg/ioutils"
14 15
 	"github.com/docker/docker/pkg/stdcopy"
15 16
 	timetypes "github.com/docker/engine-api/types/time"
16 17
 )
17 18
 
18 19
 // ContainerLogs hooks up a container's stdout and stderr streams
19 20
 // configured with the given struct.
20
-func (daemon *Daemon) ContainerLogs(containerName string, config *backend.ContainerLogsConfig) error {
21
+func (daemon *Daemon) ContainerLogs(containerName string, config *backend.ContainerLogsConfig, started chan struct{}) error {
21 22
 	container, err := daemon.GetContainer(containerName)
22 23
 	if err != nil {
23 24
 		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
... ...
@@ -27,14 +28,6 @@ func (daemon *Daemon) ContainerLogs(containerName string, config *backend.Contai
27 27
 		return derr.ErrorCodeNeedStream
28 28
 	}
29 29
 
30
-	outStream := config.OutStream
31
-	errStream := outStream
32
-	if !container.Config.Tty {
33
-		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
34
-		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
35
-	}
36
-	config.OutStream = outStream
37
-
38 30
 	cLog, err := daemon.getLogger(container)
39 31
 	if err != nil {
40 32
 		return err
... ...
@@ -67,6 +60,18 @@ func (daemon *Daemon) ContainerLogs(containerName string, config *backend.Contai
67 67
 	}
68 68
 	logs := logReader.ReadLogs(readConfig)
69 69
 
70
+	wf := ioutils.NewWriteFlusher(config.OutStream)
71
+	defer wf.Close()
72
+	close(started)
73
+	wf.Flush()
74
+
75
+	var outStream io.Writer = wf
76
+	errStream := outStream
77
+	if !container.Config.Tty {
78
+		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
79
+		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
80
+	}
81
+
70 82
 	for {
71 83
 		select {
72 84
 		case err := <-logs.Err:
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/docker/docker/api/types/backend"
9 9
 	"github.com/docker/docker/daemon/execdriver"
10
+	"github.com/docker/docker/pkg/ioutils"
10 11
 	"github.com/docker/docker/pkg/version"
11 12
 	"github.com/docker/engine-api/types"
12 13
 	"github.com/docker/engine-api/types/versions/v1p20"
... ...
@@ -31,11 +32,12 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *backend.Contai
31 31
 		return json.NewEncoder(config.OutStream).Encode(&types.Stats{})
32 32
 	}
33 33
 
34
+	outStream := config.OutStream
34 35
 	if config.Stream {
35
-		// Write an empty chunk of data.
36
-		// This is to ensure that the HTTP status code is sent immediately,
37
-		// even if the container has not yet produced any data.
38
-		config.OutStream.Write(nil)
36
+		wf := ioutils.NewWriteFlusher(outStream)
37
+		defer wf.Close()
38
+		wf.Flush()
39
+		outStream = wf
39 40
 	}
40 41
 
41 42
 	var preCPUStats types.CPUStats
... ...
@@ -50,7 +52,7 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *backend.Contai
50 50
 		return ss
51 51
 	}
52 52
 
53
-	enc := json.NewEncoder(config.OutStream)
53
+	enc := json.NewEncoder(outStream)
54 54
 
55 55
 	updates := daemon.subscribeToContainerStats(container)
56 56
 	defer daemon.unsubscribeToContainerStats(container, updates)