mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
cleanup attach api calls
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
parent
3434860d54
commit
a77b7dd227
6 changed files with 91 additions and 77 deletions
|
@ -59,8 +59,7 @@ type monitorBackend interface {
|
|||
|
||||
// attachBackend includes function to implement to provide container attaching functionality.
|
||||
type attachBackend interface {
|
||||
ContainerAttachWithLogs(name string, c *backend.ContainerAttachWithLogsConfig) error
|
||||
ContainerWsAttachWithLogs(name string, c *backend.ContainerWsAttachWithLogsConfig) error
|
||||
ContainerAttach(name string, c *backend.ContainerAttachConfig) error
|
||||
}
|
||||
|
||||
// Backend is all the methods that need to be implemented to provide container specific functionality.
|
||||
|
|
|
@ -3,6 +3,7 @@ package container
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -14,6 +15,7 @@ import (
|
|||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/runconfig"
|
||||
|
@ -425,18 +427,45 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
|||
}
|
||||
}
|
||||
|
||||
attachWithLogsConfig := &backend.ContainerAttachWithLogsConfig{
|
||||
Hijacker: w.(http.Hijacker),
|
||||
Upgrade: upgrade,
|
||||
hijacker, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return derr.ErrorCodeNoHijackConnection.WithArgs(containerName)
|
||||
}
|
||||
|
||||
setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||
conn, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// set raw mode
|
||||
conn.Write([]byte{})
|
||||
|
||||
if upgrade {
|
||||
fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
||||
} else {
|
||||
fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
}
|
||||
|
||||
closer := func() error {
|
||||
httputils.CloseStreams(conn)
|
||||
return nil
|
||||
}
|
||||
return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil
|
||||
}
|
||||
|
||||
attachConfig := &backend.ContainerAttachConfig{
|
||||
GetStreams: setupStreams,
|
||||
UseStdin: httputils.BoolValue(r, "stdin"),
|
||||
UseStdout: httputils.BoolValue(r, "stdout"),
|
||||
UseStderr: httputils.BoolValue(r, "stderr"),
|
||||
Logs: httputils.BoolValue(r, "logs"),
|
||||
Stream: httputils.BoolValue(r, "stream"),
|
||||
DetachKeys: keys,
|
||||
MuxStreams: true,
|
||||
}
|
||||
|
||||
return s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig)
|
||||
return s.backend.ContainerAttach(containerName, attachConfig)
|
||||
}
|
||||
|
||||
func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
|
@ -455,24 +484,44 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
|
|||
}
|
||||
}
|
||||
|
||||
h := websocket.Handler(func(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
done := make(chan struct{})
|
||||
started := make(chan struct{})
|
||||
|
||||
wsAttachWithLogsConfig := &backend.ContainerWsAttachWithLogsConfig{
|
||||
InStream: ws,
|
||||
OutStream: ws,
|
||||
ErrStream: ws,
|
||||
Logs: httputils.BoolValue(r, "logs"),
|
||||
Stream: httputils.BoolValue(r, "stream"),
|
||||
DetachKeys: keys,
|
||||
setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||
wsChan := make(chan *websocket.Conn)
|
||||
h := func(conn *websocket.Conn) {
|
||||
wsChan <- conn
|
||||
<-done
|
||||
}
|
||||
|
||||
if err := s.backend.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {
|
||||
logrus.Errorf("Error attaching websocket: %s", utils.GetErrorMessage(err))
|
||||
}
|
||||
})
|
||||
ws := websocket.Server{Handler: h, Handshake: nil}
|
||||
ws.ServeHTTP(w, r)
|
||||
srv := websocket.Server{Handler: h, Handshake: nil}
|
||||
go func() {
|
||||
close(started)
|
||||
srv.ServeHTTP(w, r)
|
||||
}()
|
||||
|
||||
return nil
|
||||
conn := <-wsChan
|
||||
return conn, conn, conn, nil
|
||||
}
|
||||
|
||||
attachConfig := &backend.ContainerAttachConfig{
|
||||
GetStreams: setupStreams,
|
||||
Logs: httputils.BoolValue(r, "logs"),
|
||||
Stream: httputils.BoolValue(r, "stream"),
|
||||
DetachKeys: keys,
|
||||
UseStdin: true,
|
||||
UseStdout: true,
|
||||
UseStderr: true,
|
||||
MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr
|
||||
}
|
||||
|
||||
err = s.backend.ContainerAttach(containerName, attachConfig)
|
||||
close(done)
|
||||
select {
|
||||
case <-started:
|
||||
logrus.Errorf("Error attaching websocket: %s", err)
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -5,32 +5,25 @@ package backend
|
|||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// ContainerAttachWithLogsConfig holds the streams to use when connecting to a container to view logs.
|
||||
type ContainerAttachWithLogsConfig struct {
|
||||
Hijacker http.Hijacker
|
||||
Upgrade bool
|
||||
// ContainerAttachConfig holds the streams to use when connecting to a container to view logs.
|
||||
type ContainerAttachConfig struct {
|
||||
GetStreams func() (io.ReadCloser, io.Writer, io.Writer, error)
|
||||
UseStdin bool
|
||||
UseStdout bool
|
||||
UseStderr bool
|
||||
Logs bool
|
||||
Stream bool
|
||||
DetachKeys []byte
|
||||
}
|
||||
|
||||
// ContainerWsAttachWithLogsConfig attach with websockets, since all
|
||||
// stream data is delegated to the websocket to handle there.
|
||||
type ContainerWsAttachWithLogsConfig struct {
|
||||
InStream io.ReadCloser // Reader to attach to stdin of container
|
||||
OutStream io.Writer // Writer to attach to stdout of container
|
||||
ErrStream io.Writer // Writer to attach to stderr of container
|
||||
Logs bool // If true return log output
|
||||
Stream bool // If true return stream output
|
||||
DetachKeys []byte
|
||||
// Used to signify that streams are multiplexed and therefore need a StdWriter to encode stdout/sderr messages accordingly.
|
||||
// TODO @cpuguy83: This shouldn't be needed. It was only added so that http and websocket endpoints can use the same function, and the websocket function was not using a stdwriter prior to this change...
|
||||
// HOWEVER, the websocket endpoint is using a single stream and SHOULD be encoded with stdout/stderr as is done for HTTP since it is still just a single stream.
|
||||
// Since such a change is an API change unrelated to the current changeset we'll keep it as is here and change separately.
|
||||
MuxStreams bool
|
||||
}
|
||||
|
||||
// ContainerLogsConfig holds configs for logging operations. Exists
|
||||
|
|
|
@ -106,7 +106,7 @@ type Backend interface {
|
|||
// Pull tells Docker to pull image referenced by `name`.
|
||||
PullOnBuild(name string, authConfigs map[string]types.AuthConfig, output io.Writer) (Image, error)
|
||||
// ContainerAttach attaches to container.
|
||||
ContainerAttachOnBuild(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error
|
||||
ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error
|
||||
// ContainerCreate creates a new Docker container and returns potential warnings
|
||||
ContainerCreate(types.ContainerCreateConfig) (types.ContainerCreateResponse, error)
|
||||
// ContainerRm removes a container specified by `id`.
|
||||
|
|
|
@ -541,7 +541,7 @@ func (b *Builder) create() (string, error) {
|
|||
func (b *Builder) run(cID string) (err error) {
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
errCh <- b.docker.ContainerAttachOnBuild(cID, nil, b.Stdout, b.Stderr, true)
|
||||
errCh <- b.docker.ContainerAttachRaw(cID, nil, b.Stdout, b.Stderr, true)
|
||||
}()
|
||||
|
||||
finished := make(chan struct{})
|
||||
|
|
|
@ -13,11 +13,8 @@ import (
|
|||
"github.com/docker/docker/pkg/stdcopy"
|
||||
)
|
||||
|
||||
// ContainerAttachWithLogs attaches to logs according to the config passed in. See ContainerAttachWithLogsConfig.
|
||||
func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *backend.ContainerAttachWithLogsConfig) error {
|
||||
if c.Hijacker == nil {
|
||||
return derr.ErrorCodeNoHijackConnection.WithArgs(prefixOrName)
|
||||
}
|
||||
// ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig.
|
||||
func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error {
|
||||
container, err := daemon.GetContainer(prefixOrName)
|
||||
if err != nil {
|
||||
return derr.ErrorCodeNoSuchContainer.WithArgs(prefixOrName)
|
||||
|
@ -26,29 +23,15 @@ func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *backend.Co
|
|||
return derr.ErrorCodePausedContainer.WithArgs(prefixOrName)
|
||||
}
|
||||
|
||||
conn, _, err := c.Hijacker.Hijack()
|
||||
inStream, outStream, errStream, err := c.GetStreams()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
// Flush the options to make sure the client sets the raw mode
|
||||
conn.Write([]byte{})
|
||||
inStream := conn.(io.ReadCloser)
|
||||
outStream := conn.(io.Writer)
|
||||
defer inStream.Close()
|
||||
|
||||
if c.Upgrade {
|
||||
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")
|
||||
} else {
|
||||
fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
}
|
||||
|
||||
var errStream io.Writer
|
||||
|
||||
if !container.Config.Tty {
|
||||
errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
|
||||
if !container.Config.Tty && c.MuxStreams {
|
||||
errStream = stdcopy.NewStdWriter(errStream, stdcopy.Stderr)
|
||||
outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
|
||||
} else {
|
||||
errStream = outStream
|
||||
}
|
||||
|
||||
var stdin io.ReadCloser
|
||||
|
@ -64,32 +47,22 @@ func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *backend.Co
|
|||
stderr = errStream
|
||||
}
|
||||
|
||||
if err := daemon.attachWithLogs(container, stdin, stdout, stderr, c.Logs, c.Stream, c.DetachKeys); err != nil {
|
||||
if err := daemon.containerAttach(container, stdin, stdout, stderr, c.Logs, c.Stream, c.DetachKeys); err != nil {
|
||||
fmt.Fprintf(outStream, "Error attaching: %s\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContainerWsAttachWithLogs websocket connection
|
||||
func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *backend.ContainerWsAttachWithLogsConfig) error {
|
||||
// ContainerAttachRaw attaches the provided streams to the container's stdio
|
||||
func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
|
||||
container, err := daemon.GetContainer(prefixOrName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return daemon.attachWithLogs(container, c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream, c.DetachKeys)
|
||||
return daemon.containerAttach(container, stdin, stdout, stderr, false, stream, nil)
|
||||
}
|
||||
|
||||
// ContainerAttachOnBuild attaches streams to the container cID. If stream is true, it streams the output.
|
||||
func (daemon *Daemon) ContainerAttachOnBuild(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
|
||||
return daemon.ContainerWsAttachWithLogs(cID, &backend.ContainerWsAttachWithLogsConfig{
|
||||
InStream: stdin,
|
||||
OutStream: stdout,
|
||||
ErrStream: stderr,
|
||||
Stream: stream,
|
||||
})
|
||||
}
|
||||
|
||||
func (daemon *Daemon) attachWithLogs(container *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error {
|
||||
func (daemon *Daemon) containerAttach(container *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error {
|
||||
if logs {
|
||||
logDriver, err := daemon.getLogger(container)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in a new issue