Signed-off-by: Daniel Nephin <dnephin@docker.com>
Daniel Nephin authored on 2016/08/30 00:11:29... | ... |
@@ -31,27 +31,24 @@ type DockerCli struct { |
31 | 31 |
// configFile has the client configuration file |
32 | 32 |
configFile *configfile.ConfigFile |
33 | 33 |
// in holds the input stream and closer (io.ReadCloser) for the client. |
34 |
+ // TODO: remove |
|
34 | 35 |
in io.ReadCloser |
35 |
- // out holds the output stream (io.Writer) for the client. |
|
36 |
- out io.Writer |
|
37 | 36 |
// err holds the error stream (io.Writer) for the client. |
38 | 37 |
err io.Writer |
39 | 38 |
// keyFile holds the key file as a string. |
40 | 39 |
keyFile string |
41 | 40 |
// inFd holds the file descriptor of the client's STDIN (if valid). |
41 |
+ // TODO: remove |
|
42 | 42 |
inFd uintptr |
43 |
- // outFd holds file descriptor of the client's STDOUT (if valid). |
|
44 |
- outFd uintptr |
|
45 | 43 |
// isTerminalIn indicates whether the client's STDIN is a TTY |
44 |
+ // TODO: remove |
|
46 | 45 |
isTerminalIn bool |
47 |
- // isTerminalOut indicates whether the client's STDOUT is a TTY |
|
48 |
- isTerminalOut bool |
|
49 | 46 |
// client is the http client that performs all API operations |
50 | 47 |
client client.APIClient |
51 | 48 |
// inState holds the terminal input state |
49 |
+ // TODO: remove |
|
52 | 50 |
inState *term.State |
53 |
- // outState holds the terminal output state |
|
54 |
- outState *term.State |
|
51 |
+ out *OutStream |
|
55 | 52 |
} |
56 | 53 |
|
57 | 54 |
// Client returns the APIClient |
... | ... |
@@ -60,7 +57,7 @@ func (cli *DockerCli) Client() client.APIClient { |
60 | 60 |
} |
61 | 61 |
|
62 | 62 |
// Out returns the writer used for stdout |
63 |
-func (cli *DockerCli) Out() io.Writer { |
|
63 |
+func (cli *DockerCli) Out() *OutStream { |
|
64 | 64 |
return cli.out |
65 | 65 |
} |
66 | 66 |
|
... | ... |
@@ -80,20 +77,11 @@ func (cli *DockerCli) ConfigFile() *configfile.ConfigFile { |
80 | 80 |
} |
81 | 81 |
|
82 | 82 |
// IsTerminalIn returns true if the clients stdin is a TTY |
83 |
+// TODO: remove |
|
83 | 84 |
func (cli *DockerCli) IsTerminalIn() bool { |
84 | 85 |
return cli.isTerminalIn |
85 | 86 |
} |
86 | 87 |
|
87 |
-// IsTerminalOut returns true if the clients stdout is a TTY |
|
88 |
-func (cli *DockerCli) IsTerminalOut() bool { |
|
89 |
- return cli.isTerminalOut |
|
90 |
-} |
|
91 |
- |
|
92 |
-// OutFd returns the fd for the stdout stream |
|
93 |
-func (cli *DockerCli) OutFd() uintptr { |
|
94 |
- return cli.outFd |
|
95 |
-} |
|
96 |
- |
|
97 | 88 |
// CheckTtyInput checks if we are trying to attach to a container tty |
98 | 89 |
// from a non-tty client input stream, and if so, returns an error. |
99 | 90 |
func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error { |
... | ... |
@@ -119,12 +107,8 @@ func (cli *DockerCli) setRawTerminal() error { |
119 | 119 |
} |
120 | 120 |
cli.inState = state |
121 | 121 |
} |
122 |
- if cli.isTerminalOut { |
|
123 |
- state, err := term.SetRawTerminalOutput(cli.outFd) |
|
124 |
- if err != nil { |
|
125 |
- return err |
|
126 |
- } |
|
127 |
- cli.outState = state |
|
122 |
+ if err := cli.out.setRawTerminal(); err != nil { |
|
123 |
+ return err |
|
128 | 124 |
} |
129 | 125 |
} |
130 | 126 |
return nil |
... | ... |
@@ -134,9 +118,7 @@ func (cli *DockerCli) restoreTerminal(in io.Closer) error { |
134 | 134 |
if cli.inState != nil { |
135 | 135 |
term.RestoreTerminal(cli.inFd, cli.inState) |
136 | 136 |
} |
137 |
- if cli.outState != nil { |
|
138 |
- term.RestoreTerminal(cli.outFd, cli.outState) |
|
139 |
- } |
|
137 |
+ cli.out.restoreTerminal() |
|
140 | 138 |
// WARNING: DO NOT REMOVE THE OS CHECK !!! |
141 | 139 |
// For some reason this Close call blocks on darwin.. |
142 | 140 |
// As the client exists right after, simply discard the close |
... | ... |
@@ -149,22 +131,17 @@ func (cli *DockerCli) restoreTerminal(in io.Closer) error { |
149 | 149 |
|
150 | 150 |
// Initialize the dockerCli runs initialization that must happen after command |
151 | 151 |
// line flags are parsed. |
152 |
-func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { |
|
152 |
+func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) (err error) { |
|
153 | 153 |
cli.configFile = LoadDefaultConfigFile(cli.err) |
154 | 154 |
|
155 |
- client, err := NewAPIClientFromFlags(opts.Common, cli.configFile) |
|
155 |
+ cli.client, err = NewAPIClientFromFlags(opts.Common, cli.configFile) |
|
156 | 156 |
if err != nil { |
157 | 157 |
return err |
158 | 158 |
} |
159 | 159 |
|
160 |
- cli.client = client |
|
161 |
- |
|
162 | 160 |
if cli.in != nil { |
163 | 161 |
cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in) |
164 | 162 |
} |
165 |
- if cli.out != nil { |
|
166 |
- cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out) |
|
167 |
- } |
|
168 | 163 |
|
169 | 164 |
if opts.Common.TrustKey == "" { |
170 | 165 |
cli.keyFile = filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile) |
... | ... |
@@ -177,11 +154,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { |
177 | 177 |
|
178 | 178 |
// NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. |
179 | 179 |
func NewDockerCli(in io.ReadCloser, out, err io.Writer) *DockerCli { |
180 |
- return &DockerCli{ |
|
181 |
- in: in, |
|
182 |
- out: out, |
|
183 |
- err: err, |
|
184 |
- } |
|
180 |
+ return &DockerCli{in: in, out: NewOutStream(out), err: err} |
|
185 | 181 |
} |
186 | 182 |
|
187 | 183 |
// LoadDefaultConfigFile attempts to load the default config file and returns |
... | ... |
@@ -95,8 +95,8 @@ func runAttach(dockerCli *client.DockerCli, opts *attachOptions) error { |
95 | 95 |
} |
96 | 96 |
defer resp.Close() |
97 | 97 |
|
98 |
- if c.Config.Tty && dockerCli.IsTerminalOut() { |
|
99 |
- height, width := dockerCli.GetTtySize() |
|
98 |
+ if c.Config.Tty && dockerCli.Out().IsTerminal() { |
|
99 |
+ height, width := dockerCli.Out().GetTtySize() |
|
100 | 100 |
// To handle the case where a user repeatedly attaches/detaches without resizing their |
101 | 101 |
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially |
102 | 102 |
// resize it, then go back to normal. Without this, every attach after the first will |
... | ... |
@@ -103,8 +103,8 @@ func pullImage(ctx context.Context, dockerCli *client.DockerCli, image string, o |
103 | 103 |
return jsonmessage.DisplayJSONMessagesStream( |
104 | 104 |
responseBody, |
105 | 105 |
out, |
106 |
- dockerCli.OutFd(), |
|
107 |
- dockerCli.IsTerminalOut(), |
|
106 |
+ dockerCli.Out().FD(), |
|
107 |
+ dockerCli.Out().IsTerminal(), |
|
108 | 108 |
nil) |
109 | 109 |
} |
110 | 110 |
|
... | ... |
@@ -38,7 +38,7 @@ func NewExportCommand(dockerCli *client.DockerCli) *cobra.Command { |
38 | 38 |
} |
39 | 39 |
|
40 | 40 |
func runExport(dockerCli *client.DockerCli, opts exportOptions) error { |
41 |
- if opts.output == "" && dockerCli.IsTerminalOut() { |
|
41 |
+ if opts.output == "" && dockerCli.Out().IsTerminal() { |
|
42 | 42 |
return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.") |
43 | 43 |
} |
44 | 44 |
|
... | ... |
@@ -135,7 +135,7 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, |
135 | 135 |
// a far better user experience rather than relying on subsequent resizes |
136 | 136 |
// to cause things to catch up. |
137 | 137 |
if runtime.GOOS == "windows" { |
138 |
- hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.GetTtySize() |
|
138 |
+ hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.Out().GetTtySize() |
|
139 | 139 |
} |
140 | 140 |
|
141 | 141 |
ctx, cancelFun := context.WithCancel(context.Background()) |
... | ... |
@@ -234,7 +234,7 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, |
234 | 234 |
return runStartContainerErr(err) |
235 | 235 |
} |
236 | 236 |
|
237 |
- if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && dockerCli.IsTerminalOut() { |
|
237 |
+ if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && dockerCli.Out().IsTerminal() { |
|
238 | 238 |
if err := dockerCli.MonitorTtySize(ctx, createResponse.ID, false); err != nil { |
239 | 239 |
fmt.Fprintf(stderr, "Error monitoring TTY size: %s\n", err) |
240 | 240 |
} |
... | ... |
@@ -118,7 +118,7 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error { |
118 | 118 |
} |
119 | 119 |
|
120 | 120 |
// 5. Wait for attachment to break. |
121 |
- if c.Config.Tty && dockerCli.IsTerminalOut() { |
|
121 |
+ if c.Config.Tty && dockerCli.Out().IsTerminal() { |
|
122 | 122 |
if err := dockerCli.MonitorTtySize(ctx, c.ID, false); err != nil { |
123 | 123 |
fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err) |
124 | 124 |
} |
... | ... |
@@ -227,7 +227,7 @@ func runBuild(dockerCli *client.DockerCli, options buildOptions) error { |
227 | 227 |
|
228 | 228 |
// Setup an upload progress bar |
229 | 229 |
progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true) |
230 |
- if !dockerCli.IsTerminalOut() { |
|
230 |
+ if !dockerCli.Out().IsTerminal() { |
|
231 | 231 |
progressOutput = &lastProgressOutput{output: progressOutput} |
232 | 232 |
} |
233 | 233 |
|
... | ... |
@@ -293,7 +293,7 @@ func runBuild(dockerCli *client.DockerCli, options buildOptions) error { |
293 | 293 |
} |
294 | 294 |
defer response.Body.Close() |
295 | 295 |
|
296 |
- err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, dockerCli.OutFd(), dockerCli.IsTerminalOut(), nil) |
|
296 |
+ err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, dockerCli.Out().FD(), dockerCli.Out().IsTerminal(), nil) |
|
297 | 297 |
if err != nil { |
298 | 298 |
if jerr, ok := err.(*jsonmessage.JSONError); ok { |
299 | 299 |
// If no error code is set, default to 1 |
... | ... |
@@ -84,5 +84,5 @@ func runImport(dockerCli *client.DockerCli, opts importOptions) error { |
84 | 84 |
} |
85 | 85 |
defer responseBody.Close() |
86 | 86 |
|
87 |
- return jsonmessage.DisplayJSONMessagesStream(responseBody, dockerCli.Out(), dockerCli.OutFd(), dockerCli.IsTerminalOut(), nil) |
|
87 |
+ return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil) |
|
88 | 88 |
} |
... | ... |
@@ -49,7 +49,7 @@ func runLoad(dockerCli *client.DockerCli, opts loadOptions) error { |
49 | 49 |
defer file.Close() |
50 | 50 |
input = file |
51 | 51 |
} |
52 |
- if !dockerCli.IsTerminalOut() { |
|
52 |
+ if !dockerCli.Out().IsTerminal() { |
|
53 | 53 |
opts.quiet = true |
54 | 54 |
} |
55 | 55 |
response, err := dockerCli.Client().ImageLoad(context.Background(), input, opts.quiet) |
... | ... |
@@ -59,7 +59,7 @@ func runLoad(dockerCli *client.DockerCli, opts loadOptions) error { |
59 | 59 |
defer response.Body.Close() |
60 | 60 |
|
61 | 61 |
if response.Body != nil && response.JSON { |
62 |
- return jsonmessage.DisplayJSONMessagesStream(response.Body, dockerCli.Out(), dockerCli.OutFd(), dockerCli.IsTerminalOut(), nil) |
|
62 |
+ return jsonmessage.DisplayJSONMessagesToStream(response.Body, dockerCli.Out(), nil) |
|
63 | 63 |
} |
64 | 64 |
|
65 | 65 |
_, err = io.Copy(dockerCli.Out(), response.Body) |
... | ... |
@@ -57,6 +57,5 @@ func runPush(dockerCli *client.DockerCli, remote string) error { |
57 | 57 |
} |
58 | 58 |
|
59 | 59 |
defer responseBody.Close() |
60 |
- |
|
61 |
- return jsonmessage.DisplayJSONMessagesStream(responseBody, dockerCli.Out(), dockerCli.OutFd(), dockerCli.IsTerminalOut(), nil) |
|
60 |
+ return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil) |
|
62 | 61 |
} |
... | ... |
@@ -38,7 +38,7 @@ func NewSaveCommand(dockerCli *client.DockerCli) *cobra.Command { |
38 | 38 |
} |
39 | 39 |
|
40 | 40 |
func runSave(dockerCli *client.DockerCli, opts saveOptions) error { |
41 |
- if opts.output == "" && dockerCli.IsTerminalOut() { |
|
41 |
+ if opts.output == "" && dockerCli.Out().IsTerminal() { |
|
42 | 42 |
return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.") |
43 | 43 |
} |
44 | 44 |
|
45 | 45 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,67 @@ |
0 |
+package client |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "io" |
|
4 |
+ "os" |
|
5 |
+ |
|
6 |
+ "github.com/Sirupsen/logrus" |
|
7 |
+ "github.com/docker/docker/pkg/term" |
|
8 |
+) |
|
9 |
+ |
|
10 |
+// OutStream is an output stream used by the DockerCli to write normal program |
|
11 |
+// output. |
|
12 |
+type OutStream struct { |
|
13 |
+ out io.Writer |
|
14 |
+ fd uintptr |
|
15 |
+ isTerminal bool |
|
16 |
+ state *term.State |
|
17 |
+} |
|
18 |
+ |
|
19 |
+func (o *OutStream) Write(p []byte) (int, error) { |
|
20 |
+ return o.out.Write(p) |
|
21 |
+} |
|
22 |
+ |
|
23 |
+// FD returns the file descriptor number for this stream |
|
24 |
+func (o *OutStream) FD() uintptr { |
|
25 |
+ return o.fd |
|
26 |
+} |
|
27 |
+ |
|
28 |
+// IsTerminal returns true if this stream is connected to a terminal |
|
29 |
+func (o *OutStream) IsTerminal() bool { |
|
30 |
+ return o.isTerminal |
|
31 |
+} |
|
32 |
+ |
|
33 |
+func (o *OutStream) setRawTerminal() (err error) { |
|
34 |
+ if os.Getenv("NORAW") != "" || !o.isTerminal { |
|
35 |
+ return nil |
|
36 |
+ } |
|
37 |
+ o.state, err = term.SetRawTerminalOutput(o.fd) |
|
38 |
+ return err |
|
39 |
+} |
|
40 |
+ |
|
41 |
+func (o *OutStream) restoreTerminal() { |
|
42 |
+ if o.state != nil { |
|
43 |
+ term.RestoreTerminal(o.fd, o.state) |
|
44 |
+ } |
|
45 |
+} |
|
46 |
+ |
|
47 |
+// GetTtySize returns the height and width in characters of the tty |
|
48 |
+func (o *OutStream) GetTtySize() (int, int) { |
|
49 |
+ if !o.isTerminal { |
|
50 |
+ return 0, 0 |
|
51 |
+ } |
|
52 |
+ ws, err := term.GetWinsize(o.fd) |
|
53 |
+ if err != nil { |
|
54 |
+ logrus.Debugf("Error getting size: %s", err) |
|
55 |
+ if ws == nil { |
|
56 |
+ return 0, 0 |
|
57 |
+ } |
|
58 |
+ } |
|
59 |
+ return int(ws.Height), int(ws.Width) |
|
60 |
+} |
|
61 |
+ |
|
62 |
+// NewOutStream returns a new OutStream object from a Writer |
|
63 |
+func NewOutStream(out io.Writer) *OutStream { |
|
64 |
+ fd, isTerminal := term.GetFdInfo(out) |
|
65 |
+ return &OutStream{out: out, fd: fd, isTerminal: isTerminal} |
|
66 |
+} |
... | ... |
@@ -440,14 +440,14 @@ func (cli *DockerCli) TrustedPush(ctx context.Context, repoInfo *registry.Reposi |
440 | 440 |
// We want trust signatures to always take an explicit tag, |
441 | 441 |
// otherwise it will act as an untrusted push. |
442 | 442 |
if tag == "" { |
443 |
- if err = jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, nil); err != nil { |
|
443 |
+ if err = jsonmessage.DisplayJSONMessagesToStream(responseBody, cli.Out(), nil); err != nil { |
|
444 | 444 |
return err |
445 | 445 |
} |
446 | 446 |
fmt.Fprintln(cli.out, "No tag specified, skipping trust metadata push") |
447 | 447 |
return nil |
448 | 448 |
} |
449 | 449 |
|
450 |
- if err = jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, handleTarget); err != nil { |
|
450 |
+ if err = jsonmessage.DisplayJSONMessagesToStream(responseBody, cli.Out(), handleTarget); err != nil { |
|
451 | 451 |
return err |
452 | 452 |
} |
453 | 453 |
|
... | ... |
@@ -580,7 +580,7 @@ func (cli *DockerCli) ImagePullPrivileged(ctx context.Context, authConfig types. |
580 | 580 |
} |
581 | 581 |
defer responseBody.Close() |
582 | 582 |
|
583 |
- return jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, nil) |
|
583 |
+ return jsonmessage.DisplayJSONMessagesToStream(responseBody, cli.Out(), nil) |
|
584 | 584 |
} |
585 | 585 |
|
586 | 586 |
// ImagePushPrivileged push the image |
... | ... |
@@ -17,11 +17,11 @@ import ( |
17 | 17 |
"github.com/docker/docker/api/types" |
18 | 18 |
"github.com/docker/docker/client" |
19 | 19 |
"github.com/docker/docker/pkg/signal" |
20 |
- "github.com/docker/docker/pkg/term" |
|
20 |
+ "github.com/docker/engine-api/types" |
|
21 | 21 |
) |
22 | 22 |
|
23 | 23 |
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) { |
24 |
- height, width := cli.GetTtySize() |
|
24 |
+ height, width := cli.Out().GetTtySize() |
|
25 | 25 |
cli.ResizeTtyTo(ctx, id, height, width, isExec) |
26 | 26 |
} |
27 | 27 |
|
... | ... |
@@ -70,10 +70,10 @@ func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool |
70 | 70 |
|
71 | 71 |
if runtime.GOOS == "windows" { |
72 | 72 |
go func() { |
73 |
- prevH, prevW := cli.GetTtySize() |
|
73 |
+ prevH, prevW := cli.Out().GetTtySize() |
|
74 | 74 |
for { |
75 | 75 |
time.Sleep(time.Millisecond * 250) |
76 |
- h, w := cli.GetTtySize() |
|
76 |
+ h, w := cli.Out().GetTtySize() |
|
77 | 77 |
|
78 | 78 |
if prevW != w || prevH != h { |
79 | 79 |
cli.resizeTty(ctx, id, isExec) |
... | ... |
@@ -94,21 +94,6 @@ func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool |
94 | 94 |
return nil |
95 | 95 |
} |
96 | 96 |
|
97 |
-// GetTtySize returns the height and width in characters of the tty |
|
98 |
-func (cli *DockerCli) GetTtySize() (int, int) { |
|
99 |
- if !cli.isTerminalOut { |
|
100 |
- return 0, 0 |
|
101 |
- } |
|
102 |
- ws, err := term.GetWinsize(cli.outFd) |
|
103 |
- if err != nil { |
|
104 |
- logrus.Debugf("Error getting size: %s", err) |
|
105 |
- if ws == nil { |
|
106 |
- return 0, 0 |
|
107 |
- } |
|
108 |
- } |
|
109 |
- return int(ws.Height), int(ws.Width) |
|
110 |
-} |
|
111 |
- |
|
112 | 97 |
// CopyToFile writes the content of the reader to the specified file |
113 | 98 |
func CopyToFile(outfile string, r io.Reader) error { |
114 | 99 |
tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_") |
... | ... |
@@ -219,3 +219,14 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, |
219 | 219 |
} |
220 | 220 |
return nil |
221 | 221 |
} |
222 |
+ |
|
223 |
+type stream interface { |
|
224 |
+ io.Writer |
|
225 |
+ FD() uintptr |
|
226 |
+ IsTerminal() bool |
|
227 |
+} |
|
228 |
+ |
|
229 |
+// DisplayJSONMessagesToStream prints json messages to the output stream |
|
230 |
+func DisplayJSONMessagesToStream(in io.Reader, stream stream, auxCallback func(*json.RawMessage)) error { |
|
231 |
+ return DisplayJSONMessagesStream(in, stream, stream.FD(), stream.IsTerminal(), auxCallback) |
|
232 |
+} |