4f0d95fa |
package daemon // import "github.com/docker/docker/daemon" |
41cfaa73 |
import ( |
cfdf84d5 |
"context" |
af94f941 |
"fmt" |
41cfaa73 |
"io"
|
06d8f504 |
"github.com/docker/docker/api/types/backend" |
6bb0d181 |
"github.com/docker/docker/container" |
2ddec975 |
"github.com/docker/docker/container/stream" |
ca5ede2d |
"github.com/docker/docker/daemon/logger" |
d453fe35 |
"github.com/docker/docker/errdefs" |
e2acca67 |
"github.com/docker/docker/pkg/stdcopy" |
91e5bb95 |
"github.com/docker/docker/pkg/term" |
ebcb7d6b |
"github.com/pkg/errors" |
1009e6a4 |
"github.com/sirupsen/logrus" |
41cfaa73 |
)
|
a77b7dd2 |
// ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig.
func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error { |
91e5bb95 |
keys := []byte{}
var err error
if c.DetachKeys != "" {
keys, err = term.ToBytes(c.DetachKeys)
if err != nil { |
87a12421 |
return errdefs.InvalidParameter(errors.Errorf("Invalid detach keys (%s) provided", c.DetachKeys)) |
91e5bb95 |
}
}
|
d7d512bb |
container, err := daemon.GetContainer(prefixOrName) |
8aef1a33 |
if err != nil { |
a793564b |
return err |
af94f941 |
}
if container.IsPaused() { |
9b47b7b1 |
err := fmt.Errorf("container %s is paused, unpause the container before attach", prefixOrName) |
87a12421 |
return errdefs.Conflict(err) |
5010e095 |
}
if container.IsRestarting() { |
9b47b7b1 |
err := fmt.Errorf("container %s is restarting, wait until the container is running", prefixOrName) |
87a12421 |
return errdefs.Conflict(err) |
af94f941 |
}
|
84d6240c |
cfg := stream.AttachConfig{ |
dc0ee988 |
UseStdin: c.UseStdin, |
84d6240c |
UseStdout: c.UseStdout,
UseStderr: c.UseStderr,
TTY: container.Config.Tty,
CloseStdin: container.Config.StdinOnce,
DetachKeys: keys,
}
container.StreamConfig.AttachStreams(&cfg)
|
a77b7dd2 |
inStream, outStream, errStream, err := c.GetStreams() |
af94f941 |
if err != nil { |
8aef1a33 |
return err
} |
a77b7dd2 |
defer inStream.Close() |
8aef1a33 |
|
a77b7dd2 |
if !container.Config.Tty && c.MuxStreams {
errStream = stdcopy.NewStdWriter(errStream, stdcopy.Stderr) |
af94f941 |
outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) |
21e44d7a |
}
|
84d6240c |
if cfg.UseStdin {
cfg.Stdin = inStream |
e2acca67 |
} |
84d6240c |
if cfg.UseStdout {
cfg.Stdout = outStream |
e2acca67 |
} |
84d6240c |
if cfg.UseStderr {
cfg.Stderr = errStream |
21e44d7a |
}
|
84d6240c |
if err := daemon.containerAttach(container, &cfg, c.Logs, c.Stream); err != nil { |
af94f941 |
fmt.Fprintf(outStream, "Error attaching: %s\n", err)
}
return nil |
e2acca67 |
} |
21e44d7a |
|
a77b7dd2 |
// ContainerAttachRaw attaches the provided streams to the container's stdio |
32ca1214 |
func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadCloser, stdout, stderr io.Writer, doStream bool, attached chan struct{}) error { |
d7d512bb |
container, err := daemon.GetContainer(prefixOrName) |
8aef1a33 |
if err != nil {
return err
} |
84d6240c |
cfg := stream.AttachConfig{ |
dc0ee988 |
UseStdin: stdin != nil, |
84d6240c |
UseStdout: stdout != nil,
UseStderr: stderr != nil,
TTY: container.Config.Tty,
CloseStdin: container.Config.StdinOnce,
}
container.StreamConfig.AttachStreams(&cfg) |
32ca1214 |
close(attached) |
84d6240c |
if cfg.UseStdin {
cfg.Stdin = stdin |
2ddec975 |
} |
84d6240c |
if cfg.UseStdout {
cfg.Stdout = stdout
}
if cfg.UseStderr {
cfg.Stderr = stderr
}
return daemon.containerAttach(container, &cfg, false, doStream) |
9c332b16 |
}
|
84d6240c |
func (daemon *Daemon) containerAttach(c *container.Container, cfg *stream.AttachConfig, logs, doStream bool) error {
if logs { |
68e71aa3 |
logDriver, logCreated, err := daemon.getLogger(c) |
ca5ede2d |
if err != nil {
return err
} |
68e71aa3 |
if logCreated {
defer func() {
if err = logDriver.Close(); err != nil {
logrus.Errorf("Error closing logger: %v", err)
}
}()
} |
ca5ede2d |
cLog, ok := logDriver.(logger.LogReader)
if !ok { |
ebcb7d6b |
return logger.ErrReadLogsNotSupported{} |
ca5ede2d |
}
logs := cLog.ReadLogs(logger.ReadConfig{Tail: -1}) |
916eabd4 |
defer logs.ConsumerGone() |
ca5ede2d |
LogLoop:
for {
select {
case msg, ok := <-logs.Msg:
if !ok {
break LogLoop
} |
84d6240c |
if msg.Source == "stdout" && cfg.Stdout != nil {
cfg.Stdout.Write(msg.Line) |
ca5ede2d |
} |
84d6240c |
if msg.Source == "stderr" && cfg.Stderr != nil {
cfg.Stderr.Write(msg.Line) |
ca5ede2d |
}
case err := <-logs.Err:
logrus.Errorf("Error streaming logs: %v", err)
break LogLoop
}
}
}
|
83ad006d |
daemon.LogContainerEvent(c, "attach") |
ca5ede2d |
|
84d6240c |
if !doStream { |
2ddec975 |
return nil
} |
c498d470 |
|
84d6240c |
if cfg.Stdin != nil { |
2ddec975 |
r, w := io.Pipe() |
84d6240c |
go func(stdin io.ReadCloser) { |
2ddec975 |
defer w.Close()
defer logrus.Debug("Closing buffered stdin pipe")
io.Copy(w, stdin) |
84d6240c |
}(cfg.Stdin)
cfg.Stdin = r |
2ddec975 |
} |
83ad006d |
|
dc0ee988 |
if !c.Config.OpenStdin {
cfg.Stdin = nil
}
|
2ddec975 |
if c.Config.StdinOnce && !c.Config.Tty { |
cfdf84d5 |
// Wait for the container to stop before returning. |
49211715 |
waitChan := c.Wait(context.Background(), container.WaitConditionNotRunning) |
2ddec975 |
defer func() { |
94cefa21 |
<-waitChan // Ignore returned exit code. |
2ddec975 |
}()
}
|
84d6240c |
ctx := c.InitAttachContext()
err := <-c.StreamConfig.CopyStreams(ctx, cfg) |
2ddec975 |
if err != nil { |
0f514770 |
if _, ok := errors.Cause(err).(term.EscapeError); ok || err == context.Canceled { |
2ddec975 |
daemon.LogContainerEvent(c, "detach")
} else {
logrus.Errorf("attach failed with error: %v", err) |
ca5ede2d |
}
} |
2ddec975 |
|
ca5ede2d |
return nil |
c30a55f1 |
} |