Browse code

Remove `IsPaused` from backend interface.

Move connection hijacking logic to the daemon.

Signed-off-by: David Calavera <david.calavera@gmail.com>

David Calavera authored on 2015/12/16 15:41:46
Showing 4 changed files
... ...
@@ -44,7 +44,6 @@ type stateBackend interface {
44 44
 	ContainerUnpause(name string) error
45 45
 	ContainerWait(name string, timeout time.Duration) (int, error)
46 46
 	Exists(id string) bool
47
-	IsPaused(id string) bool
48 47
 }
49 48
 
50 49
 // monitorBackend includes functions to implement to provide containers monitoring functionality.
... ...
@@ -400,29 +400,11 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
400 400
 	}
401 401
 	containerName := vars["name"]
402 402
 
403
-	if !s.backend.Exists(containerName) {
404
-		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
405
-	}
406
-
407
-	if s.backend.IsPaused(containerName) {
408
-		return derr.ErrorCodePausedContainer.WithArgs(containerName)
409
-	}
410
-
411
-	inStream, outStream, err := httputils.HijackConnection(w)
412
-	if err != nil {
413
-		return err
414
-	}
415
-	defer httputils.CloseStreams(inStream, outStream)
416
-
417
-	if _, ok := r.Header["Upgrade"]; ok {
418
-		fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
419
-	} else {
420
-		fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
421
-	}
403
+	_, upgrade := r.Header["Upgrade"]
422 404
 
423 405
 	attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
424
-		InStream:  inStream,
425
-		OutStream: outStream,
406
+		Hijacker:  w.(http.Hijacker),
407
+		Upgrade:   upgrade,
426 408
 		UseStdin:  httputils.BoolValue(r, "stdin"),
427 409
 		UseStdout: httputils.BoolValue(r, "stdout"),
428 410
 		UseStderr: httputils.BoolValue(r, "stderr"),
... ...
@@ -430,11 +412,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
430 430
 		Stream:    httputils.BoolValue(r, "stream"),
431 431
 	}
432 432
 
433
-	if err := s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil {
434
-		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
435
-	}
436
-
437
-	return nil
433
+	return s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig)
438 434
 }
439 435
 
440 436
 func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -1,53 +1,84 @@
1 1
 package daemon
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"io"
6
+	"net/http"
5 7
 	"time"
6 8
 
7 9
 	"github.com/Sirupsen/logrus"
8 10
 	"github.com/docker/docker/container"
9 11
 	"github.com/docker/docker/daemon/logger"
12
+	derr "github.com/docker/docker/errors"
10 13
 	"github.com/docker/docker/pkg/stdcopy"
11 14
 )
12 15
 
13 16
 // ContainerAttachWithLogsConfig holds the streams to use when connecting to a container to view logs.
14 17
 type ContainerAttachWithLogsConfig struct {
15
-	InStream                       io.ReadCloser
16
-	OutStream                      io.Writer
17
-	UseStdin, UseStdout, UseStderr bool
18
-	Logs, Stream                   bool
18
+	Hijacker  http.Hijacker
19
+	Upgrade   bool
20
+	UseStdin  bool
21
+	UseStdout bool
22
+	UseStderr bool
23
+	Logs      bool
24
+	Stream    bool
19 25
 }
20 26
 
21 27
 // ContainerAttachWithLogs attaches to logs according to the config passed in. See ContainerAttachWithLogsConfig.
22 28
 func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *ContainerAttachWithLogsConfig) error {
29
+	if c.Hijacker == nil {
30
+		return derr.ErrorCodeNoHijackConnection.WithArgs(prefixOrName)
31
+	}
23 32
 	container, err := daemon.GetContainer(prefixOrName)
24 33
 	if err != nil {
34
+		return derr.ErrorCodeNoSuchContainer.WithArgs(prefixOrName)
35
+	}
36
+	if container.IsPaused() {
37
+		return derr.ErrorCodePausedContainer.WithArgs(prefixOrName)
38
+	}
39
+
40
+	conn, _, err := c.Hijacker.Hijack()
41
+	if err != nil {
25 42
 		return err
26 43
 	}
44
+	defer conn.Close()
45
+	// Flush the options to make sure the client sets the raw mode
46
+	conn.Write([]byte{})
47
+	inStream := conn.(io.ReadCloser)
48
+	outStream := conn.(io.Writer)
49
+
50
+	if c.Upgrade {
51
+		fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
52
+	} else {
53
+		fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
54
+	}
27 55
 
28 56
 	var errStream io.Writer
29 57
 
30 58
 	if !container.Config.Tty {
31
-		errStream = stdcopy.NewStdWriter(c.OutStream, stdcopy.Stderr)
32
-		c.OutStream = stdcopy.NewStdWriter(c.OutStream, stdcopy.Stdout)
59
+		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
60
+		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
33 61
 	} else {
34
-		errStream = c.OutStream
62
+		errStream = outStream
35 63
 	}
36 64
 
37 65
 	var stdin io.ReadCloser
38 66
 	var stdout, stderr io.Writer
39 67
 
40 68
 	if c.UseStdin {
41
-		stdin = c.InStream
69
+		stdin = inStream
42 70
 	}
43 71
 	if c.UseStdout {
44
-		stdout = c.OutStream
72
+		stdout = outStream
45 73
 	}
46 74
 	if c.UseStderr {
47 75
 		stderr = errStream
48 76
 	}
49 77
 
50
-	return daemon.attachWithLogs(container, stdin, stdout, stderr, c.Logs, c.Stream)
78
+	if err := daemon.attachWithLogs(container, stdin, stdout, stderr, c.Logs, c.Stream); err != nil {
79
+		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
80
+	}
81
+	return nil
51 82
 }
52 83
 
53 84
 // ContainerWsAttachWithLogsConfig attach with websockets, since all
... ...
@@ -33,4 +33,13 @@ var (
33 33
 		Description:    "Docker's networking stack is disabled for this platform",
34 34
 		HTTPStatusCode: http.StatusNotFound,
35 35
 	})
36
+
37
+	// ErrorCodeNoHijackConnection is generated when a request tries to attach to a container
38
+	// but the connection to hijack is not provided.
39
+	ErrorCodeNoHijackConnection = errcode.Register(errGroup, errcode.ErrorDescriptor{
40
+		Value:          "HIJACK_CONNECTION_MISSING",
41
+		Message:        "error attaching to container %s, hijack connection missing",
42
+		Description:    "The caller didn't provide a connection to hijack",
43
+		HTTPStatusCode: http.StatusBadRequest,
44
+	})
36 45
 )