StreamFormatter suffered was two distinct structs mixed into a single struct
without any overlap.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
| ... | ... |
@@ -138,7 +138,6 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r * |
| 138 | 138 |
|
| 139 | 139 |
output := ioutils.NewWriteFlusher(w) |
| 140 | 140 |
defer output.Close() |
| 141 |
- sf := streamformatter.NewJSONStreamFormatter() |
|
| 142 | 141 |
errf := func(err error) error {
|
| 143 | 142 |
if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 {
|
| 144 | 143 |
output.Write(notVerboseBuffer.Bytes()) |
| ... | ... |
@@ -148,7 +147,7 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r * |
| 148 | 148 |
if !output.Flushed() {
|
| 149 | 149 |
return err |
| 150 | 150 |
} |
| 151 |
- _, err = w.Write(sf.FormatError(err)) |
|
| 151 |
+ _, err = w.Write(streamformatter.FormatError(err)) |
|
| 152 | 152 |
if err != nil {
|
| 153 | 153 |
logrus.Warnf("could not write error response: %v", err)
|
| 154 | 154 |
} |
| ... | ... |
@@ -166,25 +165,22 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r * |
| 166 | 166 |
errors.New("squash is only supported with experimental mode"))
|
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 |
+ out := io.Writer(output) |
|
| 170 |
+ if buildOptions.SuppressOutput {
|
|
| 171 |
+ out = notVerboseBuffer |
|
| 172 |
+ } |
|
| 173 |
+ |
|
| 169 | 174 |
// Currently, only used if context is from a remote url. |
| 170 | 175 |
// Look at code in DetectContextFromRemoteURL for more information. |
| 171 | 176 |
createProgressReader := func(in io.ReadCloser) io.ReadCloser {
|
| 172 |
- progressOutput := sf.NewProgressOutput(output, true) |
|
| 173 |
- if buildOptions.SuppressOutput {
|
|
| 174 |
- progressOutput = sf.NewProgressOutput(notVerboseBuffer, true) |
|
| 175 |
- } |
|
| 177 |
+ progressOutput := streamformatter.NewJSONProgressOutput(out, true) |
|
| 176 | 178 |
return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", buildOptions.RemoteContext) |
| 177 | 179 |
} |
| 178 | 180 |
|
| 179 |
- out := io.Writer(output) |
|
| 180 |
- if buildOptions.SuppressOutput {
|
|
| 181 |
- out = notVerboseBuffer |
|
| 182 |
- } |
|
| 183 |
- |
|
| 184 | 181 |
imgID, err := br.backend.Build(ctx, backend.BuildConfig{
|
| 185 | 182 |
Source: r.Body, |
| 186 | 183 |
Options: buildOptions, |
| 187 |
- ProgressWriter: buildProgressWriter(out, sf, createProgressReader), |
|
| 184 |
+ ProgressWriter: buildProgressWriter(out, createProgressReader), |
|
| 188 | 185 |
}) |
| 189 | 186 |
if err != nil {
|
| 190 | 187 |
return errf(err) |
| ... | ... |
@@ -193,8 +189,7 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r * |
| 193 | 193 |
// Everything worked so if -q was provided the output from the daemon |
| 194 | 194 |
// should be just the image ID and we'll print that to stdout. |
| 195 | 195 |
if buildOptions.SuppressOutput {
|
| 196 |
- stdout := &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf}
|
|
| 197 |
- fmt.Fprintln(stdout, imgID) |
|
| 196 |
+ fmt.Fprintln(streamformatter.NewStdoutWriter(output), imgID) |
|
| 198 | 197 |
} |
| 199 | 198 |
return nil |
| 200 | 199 |
} |
| ... | ... |
@@ -226,15 +221,13 @@ func (s *syncWriter) Write(b []byte) (count int, err error) {
|
| 226 | 226 |
return |
| 227 | 227 |
} |
| 228 | 228 |
|
| 229 |
-func buildProgressWriter(out io.Writer, sf *streamformatter.StreamFormatter, createProgressReader func(io.ReadCloser) io.ReadCloser) backend.ProgressWriter {
|
|
| 229 |
+func buildProgressWriter(out io.Writer, createProgressReader func(io.ReadCloser) io.ReadCloser) backend.ProgressWriter {
|
|
| 230 | 230 |
out = &syncWriter{w: out}
|
| 231 |
- stdout := &streamformatter.StdoutFormatter{Writer: out, StreamFormatter: sf}
|
|
| 232 |
- stderr := &streamformatter.StderrFormatter{Writer: out, StreamFormatter: sf}
|
|
| 233 | 231 |
|
| 234 | 232 |
return backend.ProgressWriter{
|
| 235 | 233 |
Output: out, |
| 236 |
- StdoutFormatter: stdout, |
|
| 237 |
- StderrFormatter: stderr, |
|
| 234 |
+ StdoutFormatter: streamformatter.NewStdoutWriter(out), |
|
| 235 |
+ StderrFormatter: streamformatter.NewStderrWriter(out), |
|
| 238 | 236 |
ProgressReaderFunc: createProgressReader, |
| 239 | 237 |
} |
| 240 | 238 |
} |
| ... | ... |
@@ -118,8 +118,7 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite |
| 118 | 118 |
if !output.Flushed() {
|
| 119 | 119 |
return err |
| 120 | 120 |
} |
| 121 |
- sf := streamformatter.NewJSONStreamFormatter() |
|
| 122 |
- output.Write(sf.FormatError(err)) |
|
| 121 |
+ output.Write(streamformatter.FormatError(err)) |
|
| 123 | 122 |
} |
| 124 | 123 |
|
| 125 | 124 |
return nil |
| ... | ... |
@@ -164,8 +163,7 @@ func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, |
| 164 | 164 |
if !output.Flushed() {
|
| 165 | 165 |
return err |
| 166 | 166 |
} |
| 167 |
- sf := streamformatter.NewJSONStreamFormatter() |
|
| 168 |
- output.Write(sf.FormatError(err)) |
|
| 167 |
+ output.Write(streamformatter.FormatError(err)) |
|
| 169 | 168 |
} |
| 170 | 169 |
return nil |
| 171 | 170 |
} |
| ... | ... |
@@ -190,8 +188,7 @@ func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r |
| 190 | 190 |
if !output.Flushed() {
|
| 191 | 191 |
return err |
| 192 | 192 |
} |
| 193 |
- sf := streamformatter.NewJSONStreamFormatter() |
|
| 194 |
- output.Write(sf.FormatError(err)) |
|
| 193 |
+ output.Write(streamformatter.FormatError(err)) |
|
| 195 | 194 |
} |
| 196 | 195 |
return nil |
| 197 | 196 |
} |
| ... | ... |
@@ -207,7 +204,7 @@ func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, |
| 207 | 207 |
output := ioutils.NewWriteFlusher(w) |
| 208 | 208 |
defer output.Close() |
| 209 | 209 |
if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
|
| 210 |
- output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err)) |
|
| 210 |
+ output.Write(streamformatter.FormatError(err)) |
|
| 211 | 211 |
} |
| 212 | 212 |
return nil |
| 213 | 213 |
} |
| ... | ... |
@@ -121,7 +121,7 @@ func (pr *pluginRouter) upgradePlugin(ctx context.Context, w http.ResponseWriter |
| 121 | 121 |
if !output.Flushed() {
|
| 122 | 122 |
return err |
| 123 | 123 |
} |
| 124 |
- output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err)) |
|
| 124 |
+ output.Write(streamformatter.FormatError(err)) |
|
| 125 | 125 |
} |
| 126 | 126 |
|
| 127 | 127 |
return nil |
| ... | ... |
@@ -160,7 +160,7 @@ func (pr *pluginRouter) pullPlugin(ctx context.Context, w http.ResponseWriter, r |
| 160 | 160 |
if !output.Flushed() {
|
| 161 | 161 |
return err |
| 162 | 162 |
} |
| 163 |
- output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err)) |
|
| 163 |
+ output.Write(streamformatter.FormatError(err)) |
|
| 164 | 164 |
} |
| 165 | 165 |
|
| 166 | 166 |
return nil |
| ... | ... |
@@ -268,7 +268,7 @@ func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r |
| 268 | 268 |
if !output.Flushed() {
|
| 269 | 269 |
return err |
| 270 | 270 |
} |
| 271 |
- output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err)) |
|
| 271 |
+ output.Write(streamformatter.FormatError(err)) |
|
| 272 | 272 |
} |
| 273 | 273 |
return nil |
| 274 | 274 |
} |
| ... | ... |
@@ -4,14 +4,13 @@ import ( |
| 4 | 4 |
"io" |
| 5 | 5 |
|
| 6 | 6 |
"github.com/docker/docker/api/types" |
| 7 |
- "github.com/docker/docker/pkg/streamformatter" |
|
| 8 | 7 |
) |
| 9 | 8 |
|
| 10 | 9 |
// ProgressWriter is a data object to transport progress streams to the client |
| 11 | 10 |
type ProgressWriter struct {
|
| 12 | 11 |
Output io.Writer |
| 13 |
- StdoutFormatter *streamformatter.StdoutFormatter |
|
| 14 |
- StderrFormatter *streamformatter.StderrFormatter |
|
| 12 |
+ StdoutFormatter io.Writer |
|
| 13 |
+ StderrFormatter io.Writer |
|
| 15 | 14 |
ProgressReaderFunc func(io.ReadCloser) io.ReadCloser |
| 16 | 15 |
} |
| 17 | 16 |
|
| ... | ... |
@@ -275,8 +275,7 @@ func (b *Builder) download(srcURL string) (remote builder.Source, p string, err |
| 275 | 275 |
return |
| 276 | 276 |
} |
| 277 | 277 |
|
| 278 |
- stdoutFormatter := b.Stdout.(*streamformatter.StdoutFormatter) |
|
| 279 |
- progressOutput := stdoutFormatter.StreamFormatter.NewProgressOutput(stdoutFormatter.Writer, true) |
|
| 278 |
+ progressOutput := streamformatter.NewJSONProgressOutput(b.Output, true) |
|
| 280 | 279 |
progressReader := progress.NewProgressReader(resp.Body, progressOutput, resp.ContentLength, "", "Downloading") |
| 281 | 280 |
// Download and dump result to tmp file |
| 282 | 281 |
// TODO: add filehash directly |
| ... | ... |
@@ -269,7 +269,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
|
| 269 | 269 |
} |
| 270 | 270 |
|
| 271 | 271 |
// Setup an upload progress bar |
| 272 |
- progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true) |
|
| 272 |
+ progressOutput := streamformatter.NewProgressOutput(progBuff) |
|
| 273 | 273 |
if !dockerCli.Out().IsTerminal() {
|
| 274 | 274 |
progressOutput = &lastProgressOutput{output: progressOutput}
|
| 275 | 275 |
} |
| ... | ... |
@@ -154,7 +154,7 @@ func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.Read |
| 154 | 154 |
if err != nil {
|
| 155 | 155 |
return nil, "", errors.Errorf("unable to download remote context %s: %v", remoteURL, err)
|
| 156 | 156 |
} |
| 157 |
- progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(out, true) |
|
| 157 |
+ progressOutput := streamformatter.NewProgressOutput(out) |
|
| 158 | 158 |
|
| 159 | 159 |
// Pass the response body through a progress reader. |
| 160 | 160 |
progReader := progress.NewProgressReader(response.Body, progressOutput, response.ContentLength, "", fmt.Sprintf("Downloading build context from remote url: %s", remoteURL))
|
| ... | ... |
@@ -62,7 +62,7 @@ func stateToProgress(state swarm.TaskState, rollback bool) int64 {
|
| 62 | 62 |
func ServiceProgress(ctx context.Context, client client.APIClient, serviceID string, progressWriter io.WriteCloser) error {
|
| 63 | 63 |
defer progressWriter.Close() |
| 64 | 64 |
|
| 65 |
- progressOut := streamformatter.NewJSONStreamFormatter().NewProgressOutput(progressWriter, false) |
|
| 65 |
+ progressOut := streamformatter.NewJSONProgressOutput(progressWriter, false) |
|
| 66 | 66 |
|
| 67 | 67 |
sigint := make(chan os.Signal, 1) |
| 68 | 68 |
signal.Notify(sigint, os.Interrupt) |
| ... | ... |
@@ -28,7 +28,6 @@ import ( |
| 28 | 28 |
// the repo and tag arguments, respectively. |
| 29 | 29 |
func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
|
| 30 | 30 |
var ( |
| 31 |
- sf = streamformatter.NewJSONStreamFormatter() |
|
| 32 | 31 |
rc io.ReadCloser |
| 33 | 32 |
resp *http.Response |
| 34 | 33 |
newRef reference.Named |
| ... | ... |
@@ -72,8 +71,8 @@ func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string |
| 72 | 72 |
if err != nil {
|
| 73 | 73 |
return err |
| 74 | 74 |
} |
| 75 |
- outStream.Write(sf.FormatStatus("", "Downloading from %s", u))
|
|
| 76 |
- progressOutput := sf.NewProgressOutput(outStream, true) |
|
| 75 |
+ outStream.Write(streamformatter.FormatStatus("", "Downloading from %s", u))
|
|
| 76 |
+ progressOutput := streamformatter.NewJSONProgressOutput(outStream, true) |
|
| 77 | 77 |
rc = progress.NewProgressReader(resp.Body, progressOutput, resp.ContentLength, "", "Importing") |
| 78 | 78 |
} |
| 79 | 79 |
|
| ... | ... |
@@ -129,6 +128,6 @@ func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string |
| 129 | 129 |
} |
| 130 | 130 |
|
| 131 | 131 |
daemon.LogImageEvent(id.String(), id.String(), "import") |
| 132 |
- outStream.Write(sf.FormatStatus("", id.String()))
|
|
| 132 |
+ outStream.Write(streamformatter.FormatStatus("", id.String()))
|
|
| 133 | 133 |
return nil |
| 134 | 134 |
} |
| ... | ... |
@@ -14,7 +14,7 @@ import ( |
| 14 | 14 |
// WriteDistributionProgress is a helper for writing progress from chan to JSON |
| 15 | 15 |
// stream with an optional cancel function. |
| 16 | 16 |
func WriteDistributionProgress(cancelFunc func(), outStream io.Writer, progressChan <-chan progress.Progress) {
|
| 17 |
- progressOutput := streamformatter.NewJSONStreamFormatter().NewProgressOutput(outStream, false) |
|
| 17 |
+ progressOutput := streamformatter.NewJSONProgressOutput(outStream, false) |
|
| 18 | 18 |
operationCancelled := false |
| 19 | 19 |
|
| 20 | 20 |
for prog := range progressChan {
|
| ... | ... |
@@ -26,14 +26,11 @@ import ( |
| 26 | 26 |
) |
| 27 | 27 |
|
| 28 | 28 |
func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
|
| 29 |
- var ( |
|
| 30 |
- sf = streamformatter.NewJSONStreamFormatter() |
|
| 31 |
- progressOutput progress.Output |
|
| 32 |
- ) |
|
| 29 |
+ var progressOutput progress.Output |
|
| 33 | 30 |
if !quiet {
|
| 34 |
- progressOutput = sf.NewProgressOutput(outStream, false) |
|
| 31 |
+ progressOutput = streamformatter.NewJSONProgressOutput(outStream, false) |
|
| 35 | 32 |
} |
| 36 |
- outStream = &streamformatter.StdoutFormatter{Writer: outStream, StreamFormatter: streamformatter.NewJSONStreamFormatter()}
|
|
| 33 |
+ outStream = streamformatter.NewStdoutWriter(outStream) |
|
| 37 | 34 |
|
| 38 | 35 |
tmpDir, err := ioutil.TempDir("", "docker-import-")
|
| 39 | 36 |
if err != nil {
|
| ... | ... |
@@ -10,91 +10,76 @@ import ( |
| 10 | 10 |
"github.com/docker/docker/pkg/progress" |
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 |
-// StreamFormatter formats a stream, optionally using JSON. |
|
| 14 |
-type StreamFormatter struct {
|
|
| 15 |
- json bool |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-// NewStreamFormatter returns a simple StreamFormatter |
|
| 19 |
-func NewStreamFormatter() *StreamFormatter {
|
|
| 20 |
- return &StreamFormatter{}
|
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-// NewJSONStreamFormatter returns a StreamFormatter configured to stream json |
|
| 24 |
-func NewJSONStreamFormatter() *StreamFormatter {
|
|
| 25 |
- return &StreamFormatter{true}
|
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 | 13 |
const streamNewline = "\r\n" |
| 29 | 14 |
|
| 30 |
-var streamNewlineBytes = []byte(streamNewline) |
|
| 15 |
+type jsonProgressFormatter struct{}
|
|
| 31 | 16 |
|
| 32 |
-// FormatStream formats the specified stream. |
|
| 33 |
-func (sf *StreamFormatter) FormatStream(str string) []byte {
|
|
| 34 |
- if sf.json {
|
|
| 35 |
- b, err := json.Marshal(&jsonmessage.JSONMessage{Stream: str})
|
|
| 36 |
- if err != nil {
|
|
| 37 |
- return sf.FormatError(err) |
|
| 38 |
- } |
|
| 39 |
- return append(b, streamNewlineBytes...) |
|
| 40 |
- } |
|
| 41 |
- return []byte(str + "\r") |
|
| 17 |
+func appendNewline(source []byte) []byte {
|
|
| 18 |
+ return append(source, []byte(streamNewline)...) |
|
| 42 | 19 |
} |
| 43 | 20 |
|
| 44 | 21 |
// FormatStatus formats the specified objects according to the specified format (and id). |
| 45 |
-func (sf *StreamFormatter) FormatStatus(id, format string, a ...interface{}) []byte {
|
|
| 22 |
+func FormatStatus(id, format string, a ...interface{}) []byte {
|
|
| 46 | 23 |
str := fmt.Sprintf(format, a...) |
| 47 |
- if sf.json {
|
|
| 48 |
- b, err := json.Marshal(&jsonmessage.JSONMessage{ID: id, Status: str})
|
|
| 49 |
- if err != nil {
|
|
| 50 |
- return sf.FormatError(err) |
|
| 51 |
- } |
|
| 52 |
- return append(b, streamNewlineBytes...) |
|
| 24 |
+ b, err := json.Marshal(&jsonmessage.JSONMessage{ID: id, Status: str})
|
|
| 25 |
+ if err != nil {
|
|
| 26 |
+ return FormatError(err) |
|
| 53 | 27 |
} |
| 54 |
- return []byte(str + streamNewline) |
|
| 28 |
+ return appendNewline(b) |
|
| 55 | 29 |
} |
| 56 | 30 |
|
| 57 |
-// FormatError formats the specified error. |
|
| 58 |
-func (sf *StreamFormatter) FormatError(err error) []byte {
|
|
| 59 |
- if sf.json {
|
|
| 60 |
- jsonError, ok := err.(*jsonmessage.JSONError) |
|
| 61 |
- if !ok {
|
|
| 62 |
- jsonError = &jsonmessage.JSONError{Message: err.Error()}
|
|
| 63 |
- } |
|
| 64 |
- if b, err := json.Marshal(&jsonmessage.JSONMessage{Error: jsonError, ErrorMessage: err.Error()}); err == nil {
|
|
| 65 |
- return append(b, streamNewlineBytes...) |
|
| 66 |
- } |
|
| 67 |
- return []byte("{\"error\":\"format error\"}" + streamNewline)
|
|
| 31 |
+// FormatError formats the error as a JSON object |
|
| 32 |
+func FormatError(err error) []byte {
|
|
| 33 |
+ jsonError, ok := err.(*jsonmessage.JSONError) |
|
| 34 |
+ if !ok {
|
|
| 35 |
+ jsonError = &jsonmessage.JSONError{Message: err.Error()}
|
|
| 68 | 36 |
} |
| 69 |
- return []byte("Error: " + err.Error() + streamNewline)
|
|
| 37 |
+ if b, err := json.Marshal(&jsonmessage.JSONMessage{Error: jsonError, ErrorMessage: err.Error()}); err == nil {
|
|
| 38 |
+ return appendNewline(b) |
|
| 39 |
+ } |
|
| 40 |
+ return []byte(`{"error":"format error"}` + streamNewline)
|
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+func (sf *jsonProgressFormatter) formatStatus(id, format string, a ...interface{}) []byte {
|
|
| 44 |
+ return FormatStatus(id, format, a...) |
|
| 70 | 45 |
} |
| 71 | 46 |
|
| 72 |
-// FormatProgress formats the progress information for a specified action. |
|
| 73 |
-func (sf *StreamFormatter) FormatProgress(id, action string, progress *jsonmessage.JSONProgress, aux interface{}) []byte {
|
|
| 47 |
+// formatProgress formats the progress information for a specified action. |
|
| 48 |
+func (sf *jsonProgressFormatter) formatProgress(id, action string, progress *jsonmessage.JSONProgress, aux interface{}) []byte {
|
|
| 74 | 49 |
if progress == nil {
|
| 75 | 50 |
progress = &jsonmessage.JSONProgress{}
|
| 76 | 51 |
} |
| 77 |
- if sf.json {
|
|
| 78 |
- var auxJSON *json.RawMessage |
|
| 79 |
- if aux != nil {
|
|
| 80 |
- auxJSONBytes, err := json.Marshal(aux) |
|
| 81 |
- if err != nil {
|
|
| 82 |
- return nil |
|
| 83 |
- } |
|
| 84 |
- auxJSON = new(json.RawMessage) |
|
| 85 |
- *auxJSON = auxJSONBytes |
|
| 86 |
- } |
|
| 87 |
- b, err := json.Marshal(&jsonmessage.JSONMessage{
|
|
| 88 |
- Status: action, |
|
| 89 |
- ProgressMessage: progress.String(), |
|
| 90 |
- Progress: progress, |
|
| 91 |
- ID: id, |
|
| 92 |
- Aux: auxJSON, |
|
| 93 |
- }) |
|
| 52 |
+ var auxJSON *json.RawMessage |
|
| 53 |
+ if aux != nil {
|
|
| 54 |
+ auxJSONBytes, err := json.Marshal(aux) |
|
| 94 | 55 |
if err != nil {
|
| 95 | 56 |
return nil |
| 96 | 57 |
} |
| 97 |
- return append(b, streamNewlineBytes...) |
|
| 58 |
+ auxJSON = new(json.RawMessage) |
|
| 59 |
+ *auxJSON = auxJSONBytes |
|
| 60 |
+ } |
|
| 61 |
+ b, err := json.Marshal(&jsonmessage.JSONMessage{
|
|
| 62 |
+ Status: action, |
|
| 63 |
+ ProgressMessage: progress.String(), |
|
| 64 |
+ Progress: progress, |
|
| 65 |
+ ID: id, |
|
| 66 |
+ Aux: auxJSON, |
|
| 67 |
+ }) |
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ return nil |
|
| 70 |
+ } |
|
| 71 |
+ return appendNewline(b) |
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+type rawProgressFormatter struct{}
|
|
| 75 |
+ |
|
| 76 |
+func (sf *rawProgressFormatter) formatStatus(id, format string, a ...interface{}) []byte {
|
|
| 77 |
+ return []byte(fmt.Sprintf(format, a...) + streamNewline) |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+func (sf *rawProgressFormatter) formatProgress(id, action string, progress *jsonmessage.JSONProgress, aux interface{}) []byte {
|
|
| 81 |
+ if progress == nil {
|
|
| 82 |
+ progress = &jsonmessage.JSONProgress{}
|
|
| 98 | 83 |
} |
| 99 | 84 |
endl := "\r" |
| 100 | 85 |
if progress.String() == "" {
|
| ... | ... |
@@ -105,16 +90,23 @@ func (sf *StreamFormatter) FormatProgress(id, action string, progress *jsonmessa |
| 105 | 105 |
|
| 106 | 106 |
// NewProgressOutput returns a progress.Output object that can be passed to |
| 107 | 107 |
// progress.NewProgressReader. |
| 108 |
-func (sf *StreamFormatter) NewProgressOutput(out io.Writer, newLines bool) progress.Output {
|
|
| 109 |
- return &progressOutput{
|
|
| 110 |
- sf: sf, |
|
| 111 |
- out: out, |
|
| 112 |
- newLines: newLines, |
|
| 113 |
- } |
|
| 108 |
+func NewProgressOutput(out io.Writer) progress.Output {
|
|
| 109 |
+ return &progressOutput{sf: &rawProgressFormatter{}, out: out, newLines: true}
|
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+// NewJSONProgressOutput returns a progress.Output that that formats output |
|
| 113 |
+// using JSON objects |
|
| 114 |
+func NewJSONProgressOutput(out io.Writer, newLines bool) progress.Output {
|
|
| 115 |
+ return &progressOutput{sf: &jsonProgressFormatter{}, out: out, newLines: newLines}
|
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+type formatProgress interface {
|
|
| 119 |
+ formatStatus(id, format string, a ...interface{}) []byte
|
|
| 120 |
+ formatProgress(id, action string, progress *jsonmessage.JSONProgress, aux interface{}) []byte
|
|
| 114 | 121 |
} |
| 115 | 122 |
|
| 116 | 123 |
type progressOutput struct {
|
| 117 |
- sf *StreamFormatter |
|
| 124 |
+ sf formatProgress |
|
| 118 | 125 |
out io.Writer |
| 119 | 126 |
newLines bool |
| 120 | 127 |
} |
| ... | ... |
@@ -123,10 +115,10 @@ type progressOutput struct {
|
| 123 | 123 |
func (out *progressOutput) WriteProgress(prog progress.Progress) error {
|
| 124 | 124 |
var formatted []byte |
| 125 | 125 |
if prog.Message != "" {
|
| 126 |
- formatted = out.sf.FormatStatus(prog.ID, prog.Message) |
|
| 126 |
+ formatted = out.sf.formatStatus(prog.ID, prog.Message) |
|
| 127 | 127 |
} else {
|
| 128 | 128 |
jsonProgress := jsonmessage.JSONProgress{Current: prog.Current, Total: prog.Total, HideCounts: prog.HideCounts}
|
| 129 |
- formatted = out.sf.FormatProgress(prog.ID, prog.Action, &jsonProgress, prog.Aux) |
|
| 129 |
+ formatted = out.sf.formatProgress(prog.ID, prog.Action, &jsonProgress, prog.Aux) |
|
| 130 | 130 |
} |
| 131 | 131 |
_, err := out.out.Write(formatted) |
| 132 | 132 |
if err != nil {
|
| ... | ... |
@@ -134,39 +126,9 @@ func (out *progressOutput) WriteProgress(prog progress.Progress) error {
|
| 134 | 134 |
} |
| 135 | 135 |
|
| 136 | 136 |
if out.newLines && prog.LastUpdate {
|
| 137 |
- _, err = out.out.Write(out.sf.FormatStatus("", ""))
|
|
| 137 |
+ _, err = out.out.Write(out.sf.formatStatus("", ""))
|
|
| 138 | 138 |
return err |
| 139 | 139 |
} |
| 140 | 140 |
|
| 141 | 141 |
return nil |
| 142 | 142 |
} |
| 143 |
- |
|
| 144 |
-// StdoutFormatter is a streamFormatter that writes to the standard output. |
|
| 145 |
-type StdoutFormatter struct {
|
|
| 146 |
- io.Writer |
|
| 147 |
- *StreamFormatter |
|
| 148 |
-} |
|
| 149 |
- |
|
| 150 |
-func (sf *StdoutFormatter) Write(buf []byte) (int, error) {
|
|
| 151 |
- formattedBuf := sf.StreamFormatter.FormatStream(string(buf)) |
|
| 152 |
- n, err := sf.Writer.Write(formattedBuf) |
|
| 153 |
- if n != len(formattedBuf) {
|
|
| 154 |
- return n, io.ErrShortWrite |
|
| 155 |
- } |
|
| 156 |
- return len(buf), err |
|
| 157 |
-} |
|
| 158 |
- |
|
| 159 |
-// StderrFormatter is a streamFormatter that writes to the standard error. |
|
| 160 |
-type StderrFormatter struct {
|
|
| 161 |
- io.Writer |
|
| 162 |
- *StreamFormatter |
|
| 163 |
-} |
|
| 164 |
- |
|
| 165 |
-func (sf *StderrFormatter) Write(buf []byte) (int, error) {
|
|
| 166 |
- formattedBuf := sf.StreamFormatter.FormatStream("\033[91m" + string(buf) + "\033[0m")
|
|
| 167 |
- n, err := sf.Writer.Write(formattedBuf) |
|
| 168 |
- if n != len(formattedBuf) {
|
|
| 169 |
- return n, io.ErrShortWrite |
|
| 170 |
- } |
|
| 171 |
- return len(buf), err |
|
| 172 |
-} |
| ... | ... |
@@ -3,88 +3,65 @@ package streamformatter |
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
"errors" |
| 6 |
- "reflect" |
|
| 7 | 6 |
"strings" |
| 8 | 7 |
"testing" |
| 9 | 8 |
|
| 10 | 9 |
"github.com/docker/docker/pkg/jsonmessage" |
| 10 |
+ "github.com/stretchr/testify/assert" |
|
| 11 |
+ "github.com/stretchr/testify/require" |
|
| 11 | 12 |
) |
| 12 | 13 |
|
| 13 |
-func TestFormatStream(t *testing.T) {
|
|
| 14 |
- sf := NewStreamFormatter() |
|
| 15 |
- res := sf.FormatStream("stream")
|
|
| 16 |
- if string(res) != "stream"+"\r" {
|
|
| 17 |
- t.Fatalf("%q", res)
|
|
| 18 |
- } |
|
| 14 |
+func TestRawProgressFormatterFormatStatus(t *testing.T) {
|
|
| 15 |
+ sf := rawProgressFormatter{}
|
|
| 16 |
+ res := sf.formatStatus("ID", "%s%d", "a", 1)
|
|
| 17 |
+ assert.Equal(t, "a1\r\n", string(res)) |
|
| 19 | 18 |
} |
| 20 | 19 |
|
| 21 |
-func TestFormatJSONStatus(t *testing.T) {
|
|
| 22 |
- sf := NewStreamFormatter() |
|
| 23 |
- res := sf.FormatStatus("ID", "%s%d", "a", 1)
|
|
| 24 |
- if string(res) != "a1\r\n" {
|
|
| 25 |
- t.Fatalf("%q", res)
|
|
| 26 |
- } |
|
| 27 |
-} |
|
| 28 |
- |
|
| 29 |
-func TestFormatSimpleError(t *testing.T) {
|
|
| 30 |
- sf := NewStreamFormatter() |
|
| 31 |
- res := sf.FormatError(errors.New("Error for formatter"))
|
|
| 32 |
- if string(res) != "Error: Error for formatter\r\n" {
|
|
| 33 |
- t.Fatalf("%q", res)
|
|
| 34 |
- } |
|
| 35 |
-} |
|
| 36 |
- |
|
| 37 |
-func TestJSONFormatStream(t *testing.T) {
|
|
| 38 |
- sf := NewJSONStreamFormatter() |
|
| 39 |
- res := sf.FormatStream("stream")
|
|
| 40 |
- if string(res) != `{"stream":"stream"}`+"\r\n" {
|
|
| 41 |
- t.Fatalf("%q", res)
|
|
| 20 |
+func TestRawProgressFormatterFormatProgress(t *testing.T) {
|
|
| 21 |
+ sf := rawProgressFormatter{}
|
|
| 22 |
+ progress := &jsonmessage.JSONProgress{
|
|
| 23 |
+ Current: 15, |
|
| 24 |
+ Total: 30, |
|
| 25 |
+ Start: 1, |
|
| 42 | 26 |
} |
| 27 |
+ res := sf.formatProgress("id", "action", progress, nil)
|
|
| 28 |
+ out := string(res) |
|
| 29 |
+ assert.True(t, strings.HasPrefix(out, "action [====")) |
|
| 30 |
+ assert.Contains(t, out, "15B/30B") |
|
| 31 |
+ assert.True(t, strings.HasSuffix(out, "\r")) |
|
| 43 | 32 |
} |
| 44 | 33 |
|
| 45 |
-func TestJSONFormatStatus(t *testing.T) {
|
|
| 46 |
- sf := NewJSONStreamFormatter() |
|
| 47 |
- res := sf.FormatStatus("ID", "%s%d", "a", 1)
|
|
| 48 |
- if string(res) != `{"status":"a1","id":"ID"}`+"\r\n" {
|
|
| 49 |
- t.Fatalf("%q", res)
|
|
| 50 |
- } |
|
| 34 |
+func TestFormatStatus(t *testing.T) {
|
|
| 35 |
+ res := FormatStatus("ID", "%s%d", "a", 1)
|
|
| 36 |
+ expected := `{"status":"a1","id":"ID"}` + streamNewline
|
|
| 37 |
+ assert.Equal(t, expected, string(res)) |
|
| 51 | 38 |
} |
| 52 | 39 |
|
| 53 |
-func TestJSONFormatSimpleError(t *testing.T) {
|
|
| 54 |
- sf := NewJSONStreamFormatter() |
|
| 55 |
- res := sf.FormatError(errors.New("Error for formatter"))
|
|
| 56 |
- if string(res) != `{"errorDetail":{"message":"Error for formatter"},"error":"Error for formatter"}`+"\r\n" {
|
|
| 57 |
- t.Fatalf("%q", res)
|
|
| 58 |
- } |
|
| 40 |
+func TestFormatError(t *testing.T) {
|
|
| 41 |
+ res := FormatError(errors.New("Error for formatter"))
|
|
| 42 |
+ expected := `{"errorDetail":{"message":"Error for formatter"},"error":"Error for formatter"}` + "\r\n"
|
|
| 43 |
+ assert.Equal(t, expected, string(res)) |
|
| 59 | 44 |
} |
| 60 | 45 |
|
| 61 |
-func TestJSONFormatJSONError(t *testing.T) {
|
|
| 62 |
- sf := NewJSONStreamFormatter() |
|
| 46 |
+func TestFormatJSONError(t *testing.T) {
|
|
| 63 | 47 |
err := &jsonmessage.JSONError{Code: 50, Message: "Json error"}
|
| 64 |
- res := sf.FormatError(err) |
|
| 65 |
- if string(res) != `{"errorDetail":{"code":50,"message":"Json error"},"error":"Json error"}`+"\r\n" {
|
|
| 66 |
- t.Fatalf("%q", res)
|
|
| 67 |
- } |
|
| 48 |
+ res := FormatError(err) |
|
| 49 |
+ expected := `{"errorDetail":{"code":50,"message":"Json error"},"error":"Json error"}` + streamNewline
|
|
| 50 |
+ assert.Equal(t, expected, string(res)) |
|
| 68 | 51 |
} |
| 69 | 52 |
|
| 70 |
-func TestJSONFormatProgress(t *testing.T) {
|
|
| 71 |
- sf := NewJSONStreamFormatter() |
|
| 53 |
+func TestJsonProgressFormatterFormatProgress(t *testing.T) {
|
|
| 54 |
+ sf := &jsonProgressFormatter{}
|
|
| 72 | 55 |
progress := &jsonmessage.JSONProgress{
|
| 73 | 56 |
Current: 15, |
| 74 | 57 |
Total: 30, |
| 75 | 58 |
Start: 1, |
| 76 | 59 |
} |
| 77 |
- res := sf.FormatProgress("id", "action", progress, nil)
|
|
| 60 |
+ res := sf.formatProgress("id", "action", progress, nil)
|
|
| 78 | 61 |
msg := &jsonmessage.JSONMessage{}
|
| 79 |
- if err := json.Unmarshal(res, msg); err != nil {
|
|
| 80 |
- t.Fatal(err) |
|
| 81 |
- } |
|
| 82 |
- if msg.ID != "id" {
|
|
| 83 |
- t.Fatalf("ID must be 'id', got: %s", msg.ID)
|
|
| 84 |
- } |
|
| 85 |
- if msg.Status != "action" {
|
|
| 86 |
- t.Fatalf("Status must be 'action', got: %s", msg.Status)
|
|
| 87 |
- } |
|
| 62 |
+ require.NoError(t, json.Unmarshal(res, msg)) |
|
| 63 |
+ assert.Equal(t, "id", msg.ID) |
|
| 64 |
+ assert.Equal(t, "action", msg.Status) |
|
| 88 | 65 |
|
| 89 | 66 |
// The progress will always be in the format of: |
| 90 | 67 |
// [=========================> ] 15B/30B 412910h51m30s |
| ... | ... |
@@ -102,7 +79,5 @@ func TestJSONFormatProgress(t *testing.T) {
|
| 102 | 102 |
expectedProgress, expectedProgressShort, msg.ProgressMessage) |
| 103 | 103 |
} |
| 104 | 104 |
|
| 105 |
- if !reflect.DeepEqual(msg.Progress, progress) {
|
|
| 106 |
- t.Fatal("Original progress not equals progress from FormatProgress")
|
|
| 107 |
- } |
|
| 105 |
+ assert.Equal(t, progress, msg.Progress) |
|
| 108 | 106 |
} |
| 109 | 107 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,47 @@ |
| 0 |
+package streamformatter |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "io" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+type streamWriter struct {
|
|
| 10 |
+ io.Writer |
|
| 11 |
+ lineFormat func([]byte) string |
|
| 12 |
+} |
|
| 13 |
+ |
|
| 14 |
+func (sw *streamWriter) Write(buf []byte) (int, error) {
|
|
| 15 |
+ formattedBuf := sw.format(buf) |
|
| 16 |
+ n, err := sw.Writer.Write(formattedBuf) |
|
| 17 |
+ if n != len(formattedBuf) {
|
|
| 18 |
+ return n, io.ErrShortWrite |
|
| 19 |
+ } |
|
| 20 |
+ return len(buf), err |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func (sw *streamWriter) format(buf []byte) []byte {
|
|
| 24 |
+ msg := &jsonmessage.JSONMessage{Stream: sw.lineFormat(buf)}
|
|
| 25 |
+ b, err := json.Marshal(msg) |
|
| 26 |
+ if err != nil {
|
|
| 27 |
+ return FormatError(err) |
|
| 28 |
+ } |
|
| 29 |
+ return appendNewline(b) |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+// NewStdoutWriter returns a writer which formats the output as json message |
|
| 33 |
+// representing stdout lines |
|
| 34 |
+func NewStdoutWriter(out io.Writer) io.Writer {
|
|
| 35 |
+ return &streamWriter{Writer: out, lineFormat: func(buf []byte) string {
|
|
| 36 |
+ return string(buf) |
|
| 37 |
+ }} |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+// NewStderrWriter returns a writer which formats the output as json message |
|
| 41 |
+// representing stderr lines |
|
| 42 |
+func NewStderrWriter(out io.Writer) io.Writer {
|
|
| 43 |
+ return &streamWriter{Writer: out, lineFormat: func(buf []byte) string {
|
|
| 44 |
+ return "\033[91m" + string(buf) + "\033[0m" |
|
| 45 |
+ }} |
|
| 46 |
+} |
| 0 | 47 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,35 @@ |
| 0 |
+package streamformatter |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ "bytes" |
|
| 6 |
+ "github.com/stretchr/testify/assert" |
|
| 7 |
+ "github.com/stretchr/testify/require" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func TestStreamWriterStdout(t *testing.T) {
|
|
| 11 |
+ buffer := &bytes.Buffer{}
|
|
| 12 |
+ content := "content" |
|
| 13 |
+ sw := NewStdoutWriter(buffer) |
|
| 14 |
+ size, err := sw.Write([]byte(content)) |
|
| 15 |
+ |
|
| 16 |
+ require.NoError(t, err) |
|
| 17 |
+ assert.Equal(t, len(content), size) |
|
| 18 |
+ |
|
| 19 |
+ expected := `{"stream":"content"}` + streamNewline
|
|
| 20 |
+ assert.Equal(t, expected, buffer.String()) |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func TestStreamWriterStderr(t *testing.T) {
|
|
| 24 |
+ buffer := &bytes.Buffer{}
|
|
| 25 |
+ content := "content" |
|
| 26 |
+ sw := NewStderrWriter(buffer) |
|
| 27 |
+ size, err := sw.Write([]byte(content)) |
|
| 28 |
+ |
|
| 29 |
+ require.NoError(t, err) |
|
| 30 |
+ assert.Equal(t, len(content), size) |
|
| 31 |
+ |
|
| 32 |
+ expected := `{"stream":"\u001b[91mcontent\u001b[0m"}` + streamNewline
|
|
| 33 |
+ assert.Equal(t, expected, buffer.String()) |
|
| 34 |
+} |