Signed-off-by: Jim Minter <jminter@redhat.com>
| ... | ... |
@@ -365,19 +365,6 @@ func (container *Container) GetExecIDs() []string {
|
| 365 | 365 |
return container.ExecCommands.List() |
| 366 | 366 |
} |
| 367 | 367 |
|
| 368 |
-// Attach connects to the container's stdio to the client streams |
|
| 369 |
-func (container *Container) Attach(cfg *stream.AttachConfig) chan error {
|
|
| 370 |
- ctx := container.InitAttachContext() |
|
| 371 |
- |
|
| 372 |
- cfg.TTY = container.Config.Tty |
|
| 373 |
- if !container.Config.OpenStdin {
|
|
| 374 |
- cfg.Stdin = nil |
|
| 375 |
- } |
|
| 376 |
- cfg.CloseStdin = cfg.Stdin != nil && container.Config.StdinOnce |
|
| 377 |
- |
|
| 378 |
- return container.StreamConfig.Attach(ctx, cfg) |
|
| 379 |
-} |
|
| 380 |
- |
|
| 381 | 368 |
// ShouldRestart decides whether the daemon should restart the container or not. |
| 382 | 369 |
// This is based on the container's restart policy. |
| 383 | 370 |
func (container *Container) ShouldRestart() bool {
|
| ... | ... |
@@ -33,33 +33,51 @@ type AttachConfig struct {
|
| 33 | 33 |
// For example, this would close the attached container's stdin. |
| 34 | 34 |
CloseStdin bool |
| 35 | 35 |
|
| 36 |
+ // UseStd* indicate whether the client has requested to be connected to the |
|
| 37 |
+ // given stream or not. These flags are used instead of checking Std* != nil |
|
| 38 |
+ // at points before the client streams Std* are wired up. |
|
| 39 |
+ UseStdin, UseStdout, UseStderr bool |
|
| 40 |
+ |
|
| 41 |
+ // CStd* are the streams directly connected to the container |
|
| 42 |
+ CStdin io.WriteCloser |
|
| 43 |
+ CStdout, CStderr io.ReadCloser |
|
| 44 |
+ |
|
| 36 | 45 |
// Provide client streams to wire up to |
| 37 | 46 |
Stdin io.ReadCloser |
| 38 | 47 |
Stdout, Stderr io.Writer |
| 39 | 48 |
} |
| 40 | 49 |
|
| 41 |
-// Attach attaches the stream config to the streams specified in |
|
| 42 |
-// the AttachOptions |
|
| 43 |
-func (c *Config) Attach(ctx context.Context, cfg *AttachConfig) chan error {
|
|
| 50 |
+// AttachStreams attaches the container's streams to the AttachConfig |
|
| 51 |
+func (c *Config) AttachStreams(cfg *AttachConfig) {
|
|
| 52 |
+ if cfg.UseStdin {
|
|
| 53 |
+ cfg.CStdin = c.StdinPipe() |
|
| 54 |
+ } |
|
| 55 |
+ |
|
| 56 |
+ if cfg.UseStdout {
|
|
| 57 |
+ cfg.CStdout = c.StdoutPipe() |
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ if cfg.UseStderr {
|
|
| 61 |
+ cfg.CStderr = c.StderrPipe() |
|
| 62 |
+ } |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+// CopyStreams starts goroutines to copy data in and out to/from the container |
|
| 66 |
+func (c *Config) CopyStreams(ctx context.Context, cfg *AttachConfig) chan error {
|
|
| 44 | 67 |
var ( |
| 45 |
- cStdout, cStderr io.ReadCloser |
|
| 46 |
- cStdin io.WriteCloser |
|
| 47 |
- wg sync.WaitGroup |
|
| 48 |
- errors = make(chan error, 3) |
|
| 68 |
+ wg sync.WaitGroup |
|
| 69 |
+ errors = make(chan error, 3) |
|
| 49 | 70 |
) |
| 50 | 71 |
|
| 51 | 72 |
if cfg.Stdin != nil {
|
| 52 |
- cStdin = c.StdinPipe() |
|
| 53 | 73 |
wg.Add(1) |
| 54 | 74 |
} |
| 55 | 75 |
|
| 56 | 76 |
if cfg.Stdout != nil {
|
| 57 |
- cStdout = c.StdoutPipe() |
|
| 58 | 77 |
wg.Add(1) |
| 59 | 78 |
} |
| 60 | 79 |
|
| 61 | 80 |
if cfg.Stderr != nil {
|
| 62 |
- cStderr = c.StderrPipe() |
|
| 63 | 81 |
wg.Add(1) |
| 64 | 82 |
} |
| 65 | 83 |
|
| ... | ... |
@@ -72,9 +90,9 @@ func (c *Config) Attach(ctx context.Context, cfg *AttachConfig) chan error {
|
| 72 | 72 |
|
| 73 | 73 |
var err error |
| 74 | 74 |
if cfg.TTY {
|
| 75 |
- _, err = copyEscapable(cStdin, cfg.Stdin, cfg.DetachKeys) |
|
| 75 |
+ _, err = copyEscapable(cfg.CStdin, cfg.Stdin, cfg.DetachKeys) |
|
| 76 | 76 |
} else {
|
| 77 |
- _, err = io.Copy(cStdin, cfg.Stdin) |
|
| 77 |
+ _, err = io.Copy(cfg.CStdin, cfg.Stdin) |
|
| 78 | 78 |
} |
| 79 | 79 |
if err == io.ErrClosedPipe {
|
| 80 | 80 |
err = nil |
| ... | ... |
@@ -84,14 +102,14 @@ func (c *Config) Attach(ctx context.Context, cfg *AttachConfig) chan error {
|
| 84 | 84 |
errors <- err |
| 85 | 85 |
} |
| 86 | 86 |
if cfg.CloseStdin && !cfg.TTY {
|
| 87 |
- cStdin.Close() |
|
| 87 |
+ cfg.CStdin.Close() |
|
| 88 | 88 |
} else {
|
| 89 | 89 |
// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr |
| 90 |
- if cStdout != nil {
|
|
| 91 |
- cStdout.Close() |
|
| 90 |
+ if cfg.CStdout != nil {
|
|
| 91 |
+ cfg.CStdout.Close() |
|
| 92 | 92 |
} |
| 93 |
- if cStderr != nil {
|
|
| 94 |
- cStderr.Close() |
|
| 93 |
+ if cfg.CStderr != nil {
|
|
| 94 |
+ cfg.CStderr.Close() |
|
| 95 | 95 |
} |
| 96 | 96 |
} |
| 97 | 97 |
logrus.Debug("attach: stdin: end")
|
| ... | ... |
@@ -121,8 +139,8 @@ func (c *Config) Attach(ctx context.Context, cfg *AttachConfig) chan error {
|
| 121 | 121 |
wg.Done() |
| 122 | 122 |
} |
| 123 | 123 |
|
| 124 |
- go attachStream("stdout", cfg.Stdout, cStdout)
|
|
| 125 |
- go attachStream("stderr", cfg.Stderr, cStderr)
|
|
| 124 |
+ go attachStream("stdout", cfg.Stdout, cfg.CStdout)
|
|
| 125 |
+ go attachStream("stderr", cfg.Stderr, cfg.CStderr)
|
|
| 126 | 126 |
|
| 127 | 127 |
return promise.Go(func() error {
|
| 128 | 128 |
done := make(chan struct{})
|
| ... | ... |
@@ -134,14 +152,14 @@ func (c *Config) Attach(ctx context.Context, cfg *AttachConfig) chan error {
|
| 134 | 134 |
case <-done: |
| 135 | 135 |
case <-ctx.Done(): |
| 136 | 136 |
// close all pipes |
| 137 |
- if cStdin != nil {
|
|
| 138 |
- cStdin.Close() |
|
| 137 |
+ if cfg.CStdin != nil {
|
|
| 138 |
+ cfg.CStdin.Close() |
|
| 139 | 139 |
} |
| 140 |
- if cStdout != nil {
|
|
| 141 |
- cStdout.Close() |
|
| 140 |
+ if cfg.CStdout != nil {
|
|
| 141 |
+ cfg.CStdout.Close() |
|
| 142 | 142 |
} |
| 143 |
- if cStderr != nil {
|
|
| 144 |
- cStderr.Close() |
|
| 143 |
+ if cfg.CStderr != nil {
|
|
| 144 |
+ cfg.CStderr.Close() |
|
| 145 | 145 |
} |
| 146 | 146 |
<-done |
| 147 | 147 |
} |
| ... | ... |
@@ -15,14 +15,6 @@ import ( |
| 15 | 15 |
"github.com/docker/docker/pkg/term" |
| 16 | 16 |
) |
| 17 | 17 |
|
| 18 |
-type containerAttachConfig struct {
|
|
| 19 |
- detachKeys []byte |
|
| 20 |
- stdin io.ReadCloser |
|
| 21 |
- stdout, stderr io.Writer |
|
| 22 |
- showHistory bool |
|
| 23 |
- stream bool |
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 | 18 |
// ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig. |
| 27 | 19 |
func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error {
|
| 28 | 20 |
keys := []byte{}
|
| ... | ... |
@@ -43,6 +35,16 @@ func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerA |
| 43 | 43 |
return errors.NewRequestConflictError(err) |
| 44 | 44 |
} |
| 45 | 45 |
|
| 46 |
+ cfg := stream.AttachConfig{
|
|
| 47 |
+ UseStdin: c.UseStdin && container.Config.OpenStdin, |
|
| 48 |
+ UseStdout: c.UseStdout, |
|
| 49 |
+ UseStderr: c.UseStderr, |
|
| 50 |
+ TTY: container.Config.Tty, |
|
| 51 |
+ CloseStdin: container.Config.StdinOnce, |
|
| 52 |
+ DetachKeys: keys, |
|
| 53 |
+ } |
|
| 54 |
+ container.StreamConfig.AttachStreams(&cfg) |
|
| 55 |
+ |
|
| 46 | 56 |
inStream, outStream, errStream, err := c.GetStreams() |
| 47 | 57 |
if err != nil {
|
| 48 | 58 |
return err |
| ... | ... |
@@ -54,48 +56,51 @@ func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerA |
| 54 | 54 |
outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) |
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 |
- var cfg containerAttachConfig |
|
| 58 |
- |
|
| 59 |
- if c.UseStdin {
|
|
| 60 |
- cfg.stdin = inStream |
|
| 57 |
+ if cfg.UseStdin {
|
|
| 58 |
+ cfg.Stdin = inStream |
|
| 61 | 59 |
} |
| 62 |
- if c.UseStdout {
|
|
| 63 |
- cfg.stdout = outStream |
|
| 60 |
+ if cfg.UseStdout {
|
|
| 61 |
+ cfg.Stdout = outStream |
|
| 64 | 62 |
} |
| 65 |
- if c.UseStderr {
|
|
| 66 |
- cfg.stderr = errStream |
|
| 63 |
+ if cfg.UseStderr {
|
|
| 64 |
+ cfg.Stderr = errStream |
|
| 67 | 65 |
} |
| 68 | 66 |
|
| 69 |
- cfg.showHistory = c.Logs |
|
| 70 |
- cfg.stream = c.Stream |
|
| 71 |
- cfg.detachKeys = keys |
|
| 72 |
- |
|
| 73 |
- if err := daemon.containerAttach(container, &cfg); err != nil {
|
|
| 67 |
+ if err := daemon.containerAttach(container, &cfg, c.Logs, c.Stream); err != nil {
|
|
| 74 | 68 |
fmt.Fprintf(outStream, "Error attaching: %s\n", err) |
| 75 | 69 |
} |
| 76 | 70 |
return nil |
| 77 | 71 |
} |
| 78 | 72 |
|
| 79 | 73 |
// ContainerAttachRaw attaches the provided streams to the container's stdio |
| 80 |
-func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
|
|
| 74 |
+func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadCloser, stdout, stderr io.Writer, doStream bool) error {
|
|
| 81 | 75 |
container, err := daemon.GetContainer(prefixOrName) |
| 82 | 76 |
if err != nil {
|
| 83 | 77 |
return err |
| 84 | 78 |
} |
| 85 |
- cfg := &containerAttachConfig{
|
|
| 86 |
- stdin: stdin, |
|
| 87 |
- stdout: stdout, |
|
| 88 |
- stderr: stderr, |
|
| 89 |
- stream: stream, |
|
| 79 |
+ cfg := stream.AttachConfig{
|
|
| 80 |
+ UseStdin: stdin != nil && container.Config.OpenStdin, |
|
| 81 |
+ UseStdout: stdout != nil, |
|
| 82 |
+ UseStderr: stderr != nil, |
|
| 83 |
+ TTY: container.Config.Tty, |
|
| 84 |
+ CloseStdin: container.Config.StdinOnce, |
|
| 85 |
+ } |
|
| 86 |
+ container.StreamConfig.AttachStreams(&cfg) |
|
| 87 |
+ if cfg.UseStdin {
|
|
| 88 |
+ cfg.Stdin = stdin |
|
| 90 | 89 |
} |
| 91 |
- return daemon.containerAttach(container, cfg) |
|
| 90 |
+ if cfg.UseStdout {
|
|
| 91 |
+ cfg.Stdout = stdout |
|
| 92 |
+ } |
|
| 93 |
+ if cfg.UseStderr {
|
|
| 94 |
+ cfg.Stderr = stderr |
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 97 |
+ return daemon.containerAttach(container, &cfg, false, doStream) |
|
| 92 | 98 |
} |
| 93 | 99 |
|
| 94 |
-func (daemon *Daemon) containerAttach(c *container.Container, cfg *containerAttachConfig) error {
|
|
| 95 |
- stdin := cfg.stdin |
|
| 96 |
- stdout := cfg.stdout |
|
| 97 |
- stderr := cfg.stderr |
|
| 98 |
- if cfg.showHistory {
|
|
| 100 |
+func (daemon *Daemon) containerAttach(c *container.Container, cfg *stream.AttachConfig, logs, doStream bool) error {
|
|
| 101 |
+ if logs {
|
|
| 99 | 102 |
logDriver, err := daemon.getLogger(c) |
| 100 | 103 |
if err != nil {
|
| 101 | 104 |
return err |
| ... | ... |
@@ -113,11 +118,11 @@ func (daemon *Daemon) containerAttach(c *container.Container, cfg *containerAtta |
| 113 | 113 |
if !ok {
|
| 114 | 114 |
break LogLoop |
| 115 | 115 |
} |
| 116 |
- if msg.Source == "stdout" && stdout != nil {
|
|
| 117 |
- stdout.Write(msg.Line) |
|
| 116 |
+ if msg.Source == "stdout" && cfg.Stdout != nil {
|
|
| 117 |
+ cfg.Stdout.Write(msg.Line) |
|
| 118 | 118 |
} |
| 119 |
- if msg.Source == "stderr" && stderr != nil {
|
|
| 120 |
- stderr.Write(msg.Line) |
|
| 119 |
+ if msg.Source == "stderr" && cfg.Stderr != nil {
|
|
| 120 |
+ cfg.Stderr.Write(msg.Line) |
|
| 121 | 121 |
} |
| 122 | 122 |
case err := <-logs.Err: |
| 123 | 123 |
logrus.Errorf("Error streaming logs: %v", err)
|
| ... | ... |
@@ -128,19 +133,18 @@ func (daemon *Daemon) containerAttach(c *container.Container, cfg *containerAtta |
| 128 | 128 |
|
| 129 | 129 |
daemon.LogContainerEvent(c, "attach") |
| 130 | 130 |
|
| 131 |
- if !cfg.stream {
|
|
| 131 |
+ if !doStream {
|
|
| 132 | 132 |
return nil |
| 133 | 133 |
} |
| 134 | 134 |
|
| 135 |
- var stdinPipe io.ReadCloser |
|
| 136 |
- if stdin != nil {
|
|
| 135 |
+ if cfg.Stdin != nil {
|
|
| 137 | 136 |
r, w := io.Pipe() |
| 138 |
- go func() {
|
|
| 137 |
+ go func(stdin io.ReadCloser) {
|
|
| 139 | 138 |
defer w.Close() |
| 140 | 139 |
defer logrus.Debug("Closing buffered stdin pipe")
|
| 141 | 140 |
io.Copy(w, stdin) |
| 142 |
- }() |
|
| 143 |
- stdinPipe = r |
|
| 141 |
+ }(cfg.Stdin) |
|
| 142 |
+ cfg.Stdin = r |
|
| 144 | 143 |
} |
| 145 | 144 |
|
| 146 | 145 |
waitChan := make(chan struct{})
|
| ... | ... |
@@ -154,14 +158,8 @@ func (daemon *Daemon) containerAttach(c *container.Container, cfg *containerAtta |
| 154 | 154 |
}() |
| 155 | 155 |
} |
| 156 | 156 |
|
| 157 |
- aCfg := &stream.AttachConfig{
|
|
| 158 |
- Stdin: stdinPipe, |
|
| 159 |
- Stdout: stdout, |
|
| 160 |
- Stderr: stderr, |
|
| 161 |
- DetachKeys: cfg.detachKeys, |
|
| 162 |
- } |
|
| 163 |
- |
|
| 164 |
- err := <-c.Attach(aCfg) |
|
| 157 |
+ ctx := c.InitAttachContext() |
|
| 158 |
+ err := <-c.StreamConfig.CopyStreams(ctx, cfg) |
|
| 165 | 159 |
if err != nil {
|
| 166 | 160 |
if _, ok := err.(stream.DetachError); ok {
|
| 167 | 161 |
daemon.LogContainerEvent(c, "detach") |
| ... | ... |
@@ -210,15 +210,19 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R |
| 210 | 210 |
return err |
| 211 | 211 |
} |
| 212 | 212 |
|
| 213 |
- attachConfig := &stream.AttachConfig{
|
|
| 213 |
+ attachConfig := stream.AttachConfig{
|
|
| 214 | 214 |
TTY: ec.Tty, |
| 215 |
+ UseStdin: cStdin != nil, |
|
| 216 |
+ UseStdout: cStdout != nil, |
|
| 217 |
+ UseStderr: cStderr != nil, |
|
| 215 | 218 |
Stdin: cStdin, |
| 216 | 219 |
Stdout: cStdout, |
| 217 | 220 |
Stderr: cStderr, |
| 218 | 221 |
DetachKeys: ec.DetachKeys, |
| 219 | 222 |
CloseStdin: true, |
| 220 | 223 |
} |
| 221 |
- attachErr := ec.StreamConfig.Attach(ctx, attachConfig) |
|
| 224 |
+ ec.StreamConfig.AttachStreams(&attachConfig) |
|
| 225 |
+ attachErr := ec.StreamConfig.CopyStreams(ctx, &attachConfig) |
|
| 222 | 226 |
|
| 223 | 227 |
systemPid, err := d.containerd.AddProcess(ctx, c.ID, name, p, ec.InitializeStdio) |
| 224 | 228 |
if err != nil {
|