Remove `IsPaused` from backend interface.
| ... | ... |
@@ -45,7 +45,6 @@ type stateBackend interface {
|
| 45 | 45 |
ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error) |
| 46 | 46 |
ContainerWait(name string, timeout time.Duration) (int, error) |
| 47 | 47 |
Exists(id string) bool |
| 48 |
- IsPaused(id string) bool |
|
| 49 | 48 |
} |
| 50 | 49 |
|
| 51 | 50 |
// monitorBackend includes functions to implement to provide containers monitoring functionality. |
| ... | ... |
@@ -425,29 +425,11 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo |
| 425 | 425 |
} |
| 426 | 426 |
containerName := vars["name"] |
| 427 | 427 |
|
| 428 |
- if !s.backend.Exists(containerName) {
|
|
| 429 |
- return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) |
|
| 430 |
- } |
|
| 431 |
- |
|
| 432 |
- if s.backend.IsPaused(containerName) {
|
|
| 433 |
- return derr.ErrorCodePausedContainer.WithArgs(containerName) |
|
| 434 |
- } |
|
| 435 |
- |
|
| 436 |
- inStream, outStream, err := httputils.HijackConnection(w) |
|
| 437 |
- if err != nil {
|
|
| 438 |
- return err |
|
| 439 |
- } |
|
| 440 |
- defer httputils.CloseStreams(inStream, outStream) |
|
| 441 |
- |
|
| 442 |
- if _, ok := r.Header["Upgrade"]; ok {
|
|
| 443 |
- 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") |
|
| 444 |
- } else {
|
|
| 445 |
- fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") |
|
| 446 |
- } |
|
| 428 |
+ _, upgrade := r.Header["Upgrade"] |
|
| 447 | 429 |
|
| 448 | 430 |
attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
|
| 449 |
- InStream: inStream, |
|
| 450 |
- OutStream: outStream, |
|
| 431 |
+ Hijacker: w.(http.Hijacker), |
|
| 432 |
+ Upgrade: upgrade, |
|
| 451 | 433 |
UseStdin: httputils.BoolValue(r, "stdin"), |
| 452 | 434 |
UseStdout: httputils.BoolValue(r, "stdout"), |
| 453 | 435 |
UseStderr: httputils.BoolValue(r, "stderr"), |
| ... | ... |
@@ -455,11 +437,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo |
| 455 | 455 |
Stream: httputils.BoolValue(r, "stream"), |
| 456 | 456 |
} |
| 457 | 457 |
|
| 458 |
- if err := s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil {
|
|
| 459 |
- fmt.Fprintf(outStream, "Error attaching: %s\n", err) |
|
| 460 |
- } |
|
| 461 |
- |
|
| 462 |
- return nil |
|
| 458 |
+ return s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig) |
|
| 463 | 459 |
} |
| 464 | 460 |
|
| 465 | 461 |
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 |
) |