`StreamConfig` carries with it a dep on libcontainerd, which is used by
other projects, but libcontainerd doesn't compile on all platforms, so
move it to `github.com/docker/docker/container/stream`
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
... | ... |
@@ -19,6 +19,7 @@ import ( |
19 | 19 |
containertypes "github.com/docker/docker/api/types/container" |
20 | 20 |
mounttypes "github.com/docker/docker/api/types/mount" |
21 | 21 |
networktypes "github.com/docker/docker/api/types/network" |
22 |
+ "github.com/docker/docker/container/stream" |
|
22 | 23 |
"github.com/docker/docker/daemon/exec" |
23 | 24 |
"github.com/docker/docker/daemon/logger" |
24 | 25 |
"github.com/docker/docker/daemon/logger/jsonfilelog" |
... | ... |
@@ -65,7 +66,7 @@ func (DetachError) Error() string { |
65 | 65 |
// CommonContainer holds the fields for a container which are |
66 | 66 |
// applicable across all platforms supported by the daemon. |
67 | 67 |
type CommonContainer struct { |
68 |
- *runconfig.StreamConfig |
|
68 |
+ StreamConfig *stream.Config |
|
69 | 69 |
// embed for Container to support states directly. |
70 | 70 |
*State `json:"State"` // Needed for remote api version <= 1.11 |
71 | 71 |
Root string `json:"-"` // Path to the "home" of the container, including metadata. |
... | ... |
@@ -109,7 +110,7 @@ func NewBaseContainer(id, root string) *Container { |
109 | 109 |
ExecCommands: exec.NewStore(), |
110 | 110 |
Root: root, |
111 | 111 |
MountPoints: make(map[string]*volume.MountPoint), |
112 |
- StreamConfig: runconfig.NewStreamConfig(), |
|
112 |
+ StreamConfig: stream.NewConfig(), |
|
113 | 113 |
attachContext: &attachContext{}, |
114 | 114 |
}, |
115 | 115 |
} |
... | ... |
@@ -377,7 +378,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr |
377 | 377 |
|
378 | 378 |
// AttachStreams connects streams to a TTY. |
379 | 379 |
// Used by exec too. Should this move somewhere else? |
380 |
-func AttachStreams(ctx context.Context, streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error { |
|
380 |
+func AttachStreams(ctx context.Context, streamConfig *stream.Config, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error { |
|
381 | 381 |
var ( |
382 | 382 |
cStdout, cStderr io.ReadCloser |
383 | 383 |
cStdin io.WriteCloser |
... | ... |
@@ -1064,6 +1065,26 @@ func (container *Container) startLogging() error { |
1064 | 1064 |
return nil |
1065 | 1065 |
} |
1066 | 1066 |
|
1067 |
+// StdinPipe gets the stdin stream of the container |
|
1068 |
+func (container *Container) StdinPipe() io.WriteCloser { |
|
1069 |
+ return container.StreamConfig.StdinPipe() |
|
1070 |
+} |
|
1071 |
+ |
|
1072 |
+// StdoutPipe gets the stdout stream of the container |
|
1073 |
+func (container *Container) StdoutPipe() io.ReadCloser { |
|
1074 |
+ return container.StreamConfig.StdoutPipe() |
|
1075 |
+} |
|
1076 |
+ |
|
1077 |
+// StderrPipe gets the stderr stream of the container |
|
1078 |
+func (container *Container) StderrPipe() io.ReadCloser { |
|
1079 |
+ return container.StreamConfig.StderrPipe() |
|
1080 |
+} |
|
1081 |
+ |
|
1082 |
+// CloseStreams closes the container's stdio streams |
|
1083 |
+func (container *Container) CloseStreams() error { |
|
1084 |
+ return container.StreamConfig.CloseStreams() |
|
1085 |
+} |
|
1086 |
+ |
|
1067 | 1087 |
// InitializeStdio is called by libcontainerd to connect the stdio. |
1068 | 1088 |
func (container *Container) InitializeStdio(iop libcontainerd.IOPipe) error { |
1069 | 1089 |
if err := container.startLogging(); err != nil { |
... | ... |
@@ -1073,7 +1094,7 @@ func (container *Container) InitializeStdio(iop libcontainerd.IOPipe) error { |
1073 | 1073 |
|
1074 | 1074 |
container.StreamConfig.CopyToPipe(iop) |
1075 | 1075 |
|
1076 |
- if container.Stdin() == nil && !container.Config.Tty { |
|
1076 |
+ if container.StreamConfig.Stdin() == nil && !container.Config.Tty { |
|
1077 | 1077 |
if iop.Stdin != nil { |
1078 | 1078 |
if err := iop.Stdin.Close(); err != nil { |
1079 | 1079 |
logrus.Warnf("error closing stdin: %+v", err) |
... | ... |
@@ -23,7 +23,7 @@ func (container *Container) Reset(lock bool) { |
23 | 23 |
|
24 | 24 |
// Re-create a brand new stdin pipe once the container exited |
25 | 25 |
if container.Config.OpenStdin { |
26 |
- container.NewInputPipes() |
|
26 |
+ container.StreamConfig.NewInputPipes() |
|
27 | 27 |
} |
28 | 28 |
|
29 | 29 |
if container.LogDriver != nil { |
30 | 30 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,143 @@ |
0 |
+package stream |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "fmt" |
|
4 |
+ "io" |
|
5 |
+ "io/ioutil" |
|
6 |
+ "strings" |
|
7 |
+ "sync" |
|
8 |
+ |
|
9 |
+ "github.com/Sirupsen/logrus" |
|
10 |
+ "github.com/docker/docker/libcontainerd" |
|
11 |
+ "github.com/docker/docker/pkg/broadcaster" |
|
12 |
+ "github.com/docker/docker/pkg/ioutils" |
|
13 |
+ "github.com/docker/docker/pkg/pools" |
|
14 |
+) |
|
15 |
+ |
|
16 |
+// Config holds information about I/O streams managed together. |
|
17 |
+// |
|
18 |
+// config.StdinPipe returns a WriteCloser which can be used to feed data |
|
19 |
+// to the standard input of the streamConfig's active process. |
|
20 |
+// config.StdoutPipe and streamConfig.StderrPipe each return a ReadCloser |
|
21 |
+// which can be used to retrieve the standard output (and error) generated |
|
22 |
+// by the container's active process. The output (and error) are actually |
|
23 |
+// copied and delivered to all StdoutPipe and StderrPipe consumers, using |
|
24 |
+// a kind of "broadcaster". |
|
25 |
+type Config struct { |
|
26 |
+ sync.WaitGroup |
|
27 |
+ stdout *broadcaster.Unbuffered |
|
28 |
+ stderr *broadcaster.Unbuffered |
|
29 |
+ stdin io.ReadCloser |
|
30 |
+ stdinPipe io.WriteCloser |
|
31 |
+} |
|
32 |
+ |
|
33 |
+// NewConfig creates a stream config and initializes |
|
34 |
+// the standard err and standard out to new unbuffered broadcasters. |
|
35 |
+func NewConfig() *Config { |
|
36 |
+ return &Config{ |
|
37 |
+ stderr: new(broadcaster.Unbuffered), |
|
38 |
+ stdout: new(broadcaster.Unbuffered), |
|
39 |
+ } |
|
40 |
+} |
|
41 |
+ |
|
42 |
+// Stdout returns the standard output in the configuration. |
|
43 |
+func (c *Config) Stdout() *broadcaster.Unbuffered { |
|
44 |
+ return c.stdout |
|
45 |
+} |
|
46 |
+ |
|
47 |
+// Stderr returns the standard error in the configuration. |
|
48 |
+func (c *Config) Stderr() *broadcaster.Unbuffered { |
|
49 |
+ return c.stderr |
|
50 |
+} |
|
51 |
+ |
|
52 |
+// Stdin returns the standard input in the configuration. |
|
53 |
+func (c *Config) Stdin() io.ReadCloser { |
|
54 |
+ return c.stdin |
|
55 |
+} |
|
56 |
+ |
|
57 |
+// StdinPipe returns an input writer pipe as an io.WriteCloser. |
|
58 |
+func (c *Config) StdinPipe() io.WriteCloser { |
|
59 |
+ return c.stdinPipe |
|
60 |
+} |
|
61 |
+ |
|
62 |
+// StdoutPipe creates a new io.ReadCloser with an empty bytes pipe. |
|
63 |
+// It adds this new out pipe to the Stdout broadcaster. |
|
64 |
+func (c *Config) StdoutPipe() io.ReadCloser { |
|
65 |
+ bytesPipe := ioutils.NewBytesPipe() |
|
66 |
+ c.stdout.Add(bytesPipe) |
|
67 |
+ return bytesPipe |
|
68 |
+} |
|
69 |
+ |
|
70 |
+// StderrPipe creates a new io.ReadCloser with an empty bytes pipe. |
|
71 |
+// It adds this new err pipe to the Stderr broadcaster. |
|
72 |
+func (c *Config) StderrPipe() io.ReadCloser { |
|
73 |
+ bytesPipe := ioutils.NewBytesPipe() |
|
74 |
+ c.stderr.Add(bytesPipe) |
|
75 |
+ return bytesPipe |
|
76 |
+} |
|
77 |
+ |
|
78 |
+// NewInputPipes creates new pipes for both standard inputs, Stdin and StdinPipe. |
|
79 |
+func (c *Config) NewInputPipes() { |
|
80 |
+ c.stdin, c.stdinPipe = io.Pipe() |
|
81 |
+} |
|
82 |
+ |
|
83 |
+// NewNopInputPipe creates a new input pipe that will silently drop all messages in the input. |
|
84 |
+func (c *Config) NewNopInputPipe() { |
|
85 |
+ c.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) |
|
86 |
+} |
|
87 |
+ |
|
88 |
+// CloseStreams ensures that the configured streams are properly closed. |
|
89 |
+func (c *Config) CloseStreams() error { |
|
90 |
+ var errors []string |
|
91 |
+ |
|
92 |
+ if c.stdin != nil { |
|
93 |
+ if err := c.stdin.Close(); err != nil { |
|
94 |
+ errors = append(errors, fmt.Sprintf("error close stdin: %s", err)) |
|
95 |
+ } |
|
96 |
+ } |
|
97 |
+ |
|
98 |
+ if err := c.stdout.Clean(); err != nil { |
|
99 |
+ errors = append(errors, fmt.Sprintf("error close stdout: %s", err)) |
|
100 |
+ } |
|
101 |
+ |
|
102 |
+ if err := c.stderr.Clean(); err != nil { |
|
103 |
+ errors = append(errors, fmt.Sprintf("error close stderr: %s", err)) |
|
104 |
+ } |
|
105 |
+ |
|
106 |
+ if len(errors) > 0 { |
|
107 |
+ return fmt.Errorf(strings.Join(errors, "\n")) |
|
108 |
+ } |
|
109 |
+ |
|
110 |
+ return nil |
|
111 |
+} |
|
112 |
+ |
|
113 |
+// CopyToPipe connects streamconfig with a libcontainerd.IOPipe |
|
114 |
+func (c *Config) CopyToPipe(iop libcontainerd.IOPipe) { |
|
115 |
+ copyFunc := func(w io.Writer, r io.Reader) { |
|
116 |
+ c.Add(1) |
|
117 |
+ go func() { |
|
118 |
+ if _, err := pools.Copy(w, r); err != nil { |
|
119 |
+ logrus.Errorf("stream copy error: %+v", err) |
|
120 |
+ } |
|
121 |
+ c.Done() |
|
122 |
+ }() |
|
123 |
+ } |
|
124 |
+ |
|
125 |
+ if iop.Stdout != nil { |
|
126 |
+ copyFunc(c.Stdout(), iop.Stdout) |
|
127 |
+ } |
|
128 |
+ if iop.Stderr != nil { |
|
129 |
+ copyFunc(c.Stderr(), iop.Stderr) |
|
130 |
+ } |
|
131 |
+ |
|
132 |
+ if stdin := c.Stdin(); stdin != nil { |
|
133 |
+ if iop.Stdin != nil { |
|
134 |
+ go func() { |
|
135 |
+ pools.Copy(iop.Stdin, stdin) |
|
136 |
+ if err := iop.Stdin.Close(); err != nil { |
|
137 |
+ logrus.Errorf("failed to close stdin: %+v", err) |
|
138 |
+ } |
|
139 |
+ }() |
|
140 |
+ } |
|
141 |
+ } |
|
142 |
+} |
... | ... |
@@ -91,9 +91,9 @@ func (daemon *Daemon) load(id string) (*container.Container, error) { |
91 | 91 |
func (daemon *Daemon) Register(c *container.Container) error { |
92 | 92 |
// Attach to stdout and stderr |
93 | 93 |
if c.Config.OpenStdin { |
94 |
- c.NewInputPipes() |
|
94 |
+ c.StreamConfig.NewInputPipes() |
|
95 | 95 |
} else { |
96 |
- c.NewNopInputPipe() |
|
96 |
+ c.StreamConfig.NewNopInputPipe() |
|
97 | 97 |
} |
98 | 98 |
|
99 | 99 |
daemon.containers.Add(c.ID, c) |
... | ... |
@@ -195,9 +195,9 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R |
195 | 195 |
} |
196 | 196 |
|
197 | 197 |
if ec.OpenStdin { |
198 |
- ec.NewInputPipes() |
|
198 |
+ ec.StreamConfig.NewInputPipes() |
|
199 | 199 |
} else { |
200 |
- ec.NewNopInputPipe() |
|
200 |
+ ec.StreamConfig.NewNopInputPipe() |
|
201 | 201 |
} |
202 | 202 |
|
203 | 203 |
p := libcontainerd.Process{ |
... | ... |
@@ -5,9 +5,9 @@ import ( |
5 | 5 |
"sync" |
6 | 6 |
|
7 | 7 |
"github.com/Sirupsen/logrus" |
8 |
+ "github.com/docker/docker/container/stream" |
|
8 | 9 |
"github.com/docker/docker/libcontainerd" |
9 | 10 |
"github.com/docker/docker/pkg/stringid" |
10 |
- "github.com/docker/docker/runconfig" |
|
11 | 11 |
) |
12 | 12 |
|
13 | 13 |
// Config holds the configurations for execs. The Daemon keeps |
... | ... |
@@ -15,30 +15,30 @@ import ( |
15 | 15 |
// examined both during and after completion. |
16 | 16 |
type Config struct { |
17 | 17 |
sync.Mutex |
18 |
- *runconfig.StreamConfig |
|
19 |
- ID string |
|
20 |
- Running bool |
|
21 |
- ExitCode *int |
|
22 |
- OpenStdin bool |
|
23 |
- OpenStderr bool |
|
24 |
- OpenStdout bool |
|
25 |
- CanRemove bool |
|
26 |
- ContainerID string |
|
27 |
- DetachKeys []byte |
|
28 |
- Entrypoint string |
|
29 |
- Args []string |
|
30 |
- Tty bool |
|
31 |
- Privileged bool |
|
32 |
- User string |
|
33 |
- Env []string |
|
34 |
- Pid int |
|
18 |
+ StreamConfig *stream.Config |
|
19 |
+ ID string |
|
20 |
+ Running bool |
|
21 |
+ ExitCode *int |
|
22 |
+ OpenStdin bool |
|
23 |
+ OpenStderr bool |
|
24 |
+ OpenStdout bool |
|
25 |
+ CanRemove bool |
|
26 |
+ ContainerID string |
|
27 |
+ DetachKeys []byte |
|
28 |
+ Entrypoint string |
|
29 |
+ Args []string |
|
30 |
+ Tty bool |
|
31 |
+ Privileged bool |
|
32 |
+ User string |
|
33 |
+ Env []string |
|
34 |
+ Pid int |
|
35 | 35 |
} |
36 | 36 |
|
37 | 37 |
// NewConfig initializes the a new exec configuration |
38 | 38 |
func NewConfig() *Config { |
39 | 39 |
return &Config{ |
40 | 40 |
ID: stringid.GenerateNonCryptoID(), |
41 |
- StreamConfig: runconfig.NewStreamConfig(), |
|
41 |
+ StreamConfig: stream.NewConfig(), |
|
42 | 42 |
} |
43 | 43 |
} |
44 | 44 |
|
... | ... |
@@ -46,7 +46,7 @@ func NewConfig() *Config { |
46 | 46 |
func (c *Config) InitializeStdio(iop libcontainerd.IOPipe) error { |
47 | 47 |
c.StreamConfig.CopyToPipe(iop) |
48 | 48 |
|
49 |
- if c.Stdin() == nil && !c.Tty && runtime.GOOS == "windows" { |
|
49 |
+ if c.StreamConfig.Stdin() == nil && !c.Tty && runtime.GOOS == "windows" { |
|
50 | 50 |
if iop.Stdin != nil { |
51 | 51 |
if err := iop.Stdin.Close(); err != nil { |
52 | 52 |
logrus.Errorf("error closing exec stdin: %+v", err) |
... | ... |
@@ -57,6 +57,11 @@ func (c *Config) InitializeStdio(iop libcontainerd.IOPipe) error { |
57 | 57 |
return nil |
58 | 58 |
} |
59 | 59 |
|
60 |
+// CloseStreams closes the stdio streams for the exec |
|
61 |
+func (c *Config) CloseStreams() error { |
|
62 |
+ return c.StreamConfig.CloseStreams() |
|
63 |
+} |
|
64 |
+ |
|
60 | 65 |
// Store keeps track of the exec configurations. |
61 | 66 |
type Store struct { |
62 | 67 |
commands map[string]*Config |
... | ... |
@@ -39,7 +39,7 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error { |
39 | 39 |
} |
40 | 40 |
|
41 | 41 |
c.Lock() |
42 |
- c.Wait() |
|
42 |
+ c.StreamConfig.Wait() |
|
43 | 43 |
c.Reset(false) |
44 | 44 |
|
45 | 45 |
restart, wait, err := c.RestartManager().ShouldRestart(e.ExitCode, false, time.Since(c.StartedAt)) |
... | ... |
@@ -88,7 +88,7 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error { |
88 | 88 |
defer execConfig.Unlock() |
89 | 89 |
execConfig.ExitCode = &ec |
90 | 90 |
execConfig.Running = false |
91 |
- execConfig.Wait() |
|
91 |
+ execConfig.StreamConfig.Wait() |
|
92 | 92 |
if err := execConfig.CloseStreams(); err != nil { |
93 | 93 |
logrus.Errorf("%s: %s", c.ID, err) |
94 | 94 |
} |
95 | 95 |
deleted file mode 100644 |
... | ... |
@@ -1,143 +0,0 @@ |
1 |
-package runconfig |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "fmt" |
|
5 |
- "io" |
|
6 |
- "io/ioutil" |
|
7 |
- "strings" |
|
8 |
- "sync" |
|
9 |
- |
|
10 |
- "github.com/Sirupsen/logrus" |
|
11 |
- "github.com/docker/docker/libcontainerd" |
|
12 |
- "github.com/docker/docker/pkg/broadcaster" |
|
13 |
- "github.com/docker/docker/pkg/ioutils" |
|
14 |
- "github.com/docker/docker/pkg/pools" |
|
15 |
-) |
|
16 |
- |
|
17 |
-// StreamConfig holds information about I/O streams managed together. |
|
18 |
-// |
|
19 |
-// streamConfig.StdinPipe returns a WriteCloser which can be used to feed data |
|
20 |
-// to the standard input of the streamConfig's active process. |
|
21 |
-// streamConfig.StdoutPipe and streamConfig.StderrPipe each return a ReadCloser |
|
22 |
-// which can be used to retrieve the standard output (and error) generated |
|
23 |
-// by the container's active process. The output (and error) are actually |
|
24 |
-// copied and delivered to all StdoutPipe and StderrPipe consumers, using |
|
25 |
-// a kind of "broadcaster". |
|
26 |
-type StreamConfig struct { |
|
27 |
- sync.WaitGroup |
|
28 |
- stdout *broadcaster.Unbuffered |
|
29 |
- stderr *broadcaster.Unbuffered |
|
30 |
- stdin io.ReadCloser |
|
31 |
- stdinPipe io.WriteCloser |
|
32 |
-} |
|
33 |
- |
|
34 |
-// NewStreamConfig creates a stream config and initializes |
|
35 |
-// the standard err and standard out to new unbuffered broadcasters. |
|
36 |
-func NewStreamConfig() *StreamConfig { |
|
37 |
- return &StreamConfig{ |
|
38 |
- stderr: new(broadcaster.Unbuffered), |
|
39 |
- stdout: new(broadcaster.Unbuffered), |
|
40 |
- } |
|
41 |
-} |
|
42 |
- |
|
43 |
-// Stdout returns the standard output in the configuration. |
|
44 |
-func (streamConfig *StreamConfig) Stdout() *broadcaster.Unbuffered { |
|
45 |
- return streamConfig.stdout |
|
46 |
-} |
|
47 |
- |
|
48 |
-// Stderr returns the standard error in the configuration. |
|
49 |
-func (streamConfig *StreamConfig) Stderr() *broadcaster.Unbuffered { |
|
50 |
- return streamConfig.stderr |
|
51 |
-} |
|
52 |
- |
|
53 |
-// Stdin returns the standard input in the configuration. |
|
54 |
-func (streamConfig *StreamConfig) Stdin() io.ReadCloser { |
|
55 |
- return streamConfig.stdin |
|
56 |
-} |
|
57 |
- |
|
58 |
-// StdinPipe returns an input writer pipe as an io.WriteCloser. |
|
59 |
-func (streamConfig *StreamConfig) StdinPipe() io.WriteCloser { |
|
60 |
- return streamConfig.stdinPipe |
|
61 |
-} |
|
62 |
- |
|
63 |
-// StdoutPipe creates a new io.ReadCloser with an empty bytes pipe. |
|
64 |
-// It adds this new out pipe to the Stdout broadcaster. |
|
65 |
-func (streamConfig *StreamConfig) StdoutPipe() io.ReadCloser { |
|
66 |
- bytesPipe := ioutils.NewBytesPipe() |
|
67 |
- streamConfig.stdout.Add(bytesPipe) |
|
68 |
- return bytesPipe |
|
69 |
-} |
|
70 |
- |
|
71 |
-// StderrPipe creates a new io.ReadCloser with an empty bytes pipe. |
|
72 |
-// It adds this new err pipe to the Stderr broadcaster. |
|
73 |
-func (streamConfig *StreamConfig) StderrPipe() io.ReadCloser { |
|
74 |
- bytesPipe := ioutils.NewBytesPipe() |
|
75 |
- streamConfig.stderr.Add(bytesPipe) |
|
76 |
- return bytesPipe |
|
77 |
-} |
|
78 |
- |
|
79 |
-// NewInputPipes creates new pipes for both standard inputs, Stdin and StdinPipe. |
|
80 |
-func (streamConfig *StreamConfig) NewInputPipes() { |
|
81 |
- streamConfig.stdin, streamConfig.stdinPipe = io.Pipe() |
|
82 |
-} |
|
83 |
- |
|
84 |
-// NewNopInputPipe creates a new input pipe that will silently drop all messages in the input. |
|
85 |
-func (streamConfig *StreamConfig) NewNopInputPipe() { |
|
86 |
- streamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) |
|
87 |
-} |
|
88 |
- |
|
89 |
-// CloseStreams ensures that the configured streams are properly closed. |
|
90 |
-func (streamConfig *StreamConfig) CloseStreams() error { |
|
91 |
- var errors []string |
|
92 |
- |
|
93 |
- if streamConfig.stdin != nil { |
|
94 |
- if err := streamConfig.stdin.Close(); err != nil { |
|
95 |
- errors = append(errors, fmt.Sprintf("error close stdin: %s", err)) |
|
96 |
- } |
|
97 |
- } |
|
98 |
- |
|
99 |
- if err := streamConfig.stdout.Clean(); err != nil { |
|
100 |
- errors = append(errors, fmt.Sprintf("error close stdout: %s", err)) |
|
101 |
- } |
|
102 |
- |
|
103 |
- if err := streamConfig.stderr.Clean(); err != nil { |
|
104 |
- errors = append(errors, fmt.Sprintf("error close stderr: %s", err)) |
|
105 |
- } |
|
106 |
- |
|
107 |
- if len(errors) > 0 { |
|
108 |
- return fmt.Errorf(strings.Join(errors, "\n")) |
|
109 |
- } |
|
110 |
- |
|
111 |
- return nil |
|
112 |
-} |
|
113 |
- |
|
114 |
-// CopyToPipe connects streamconfig with a libcontainerd.IOPipe |
|
115 |
-func (streamConfig *StreamConfig) CopyToPipe(iop libcontainerd.IOPipe) { |
|
116 |
- copyFunc := func(w io.Writer, r io.Reader) { |
|
117 |
- streamConfig.Add(1) |
|
118 |
- go func() { |
|
119 |
- if _, err := pools.Copy(w, r); err != nil { |
|
120 |
- logrus.Errorf("stream copy error: %+v", err) |
|
121 |
- } |
|
122 |
- streamConfig.Done() |
|
123 |
- }() |
|
124 |
- } |
|
125 |
- |
|
126 |
- if iop.Stdout != nil { |
|
127 |
- copyFunc(streamConfig.Stdout(), iop.Stdout) |
|
128 |
- } |
|
129 |
- if iop.Stderr != nil { |
|
130 |
- copyFunc(streamConfig.Stderr(), iop.Stderr) |
|
131 |
- } |
|
132 |
- |
|
133 |
- if stdin := streamConfig.Stdin(); stdin != nil { |
|
134 |
- if iop.Stdin != nil { |
|
135 |
- go func() { |
|
136 |
- pools.Copy(iop.Stdin, stdin) |
|
137 |
- if err := iop.Stdin.Close(); err != nil { |
|
138 |
- logrus.Errorf("failed to close stdin: %+v", err) |
|
139 |
- } |
|
140 |
- }() |
|
141 |
- } |
|
142 |
- } |
|
143 |
-} |