Skip to content

Commit

Permalink
Merge pull request #752 from brettbuddin/writer-errors
Browse files Browse the repository at this point in the history
Surface io.Writer errors in jsonmessage
  • Loading branch information
fsouza committed Nov 2, 2018
2 parents 623c4c4 + b2159e1 commit 1015523
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 25 deletions.
26 changes: 26 additions & 0 deletions client_test.go
Expand Up @@ -8,6 +8,7 @@ import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -804,6 +805,31 @@ func TestClientStreamTimeoutNativeClient(t *testing.T) {
}
}

func TestClientStreamJSONDecoderFailingOutputWriter(t *testing.T) {
t.Parallel()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "{}")
time.Sleep(500 * time.Millisecond)
}))
client, err := NewClient(srv.URL)
if err != nil {
t.Fatal(err)
}
var w eofWriter
err = client.stream("POST", "/image/create", streamOptions{
setRawTerminal: true,
useJSONDecoder: true,
stdout: &w,
})
if err != io.EOF {
t.Fatalf("expected eof error, got: %s", err)
}
}

type eofWriter struct{}

func (w eofWriter) Write(b []byte) (int, error) { return len(b), io.EOF }

type FakeRoundTripper struct {
message string
status int
Expand Down
113 changes: 88 additions & 25 deletions internal/jsonmessage/jsonmessage.go
Expand Up @@ -166,43 +166,72 @@ func (ti *noTermInfo) Parse(attr string, params ...interface{}) (string, error)
return "", fmt.Errorf("noTermInfo")
}

func clearLine(out io.Writer, ti termInfo) {
func clearLine(out io.Writer, ti termInfo) error {
// el2 (clear whole line) is not exposed by terminfo.

// First clear line from beginning to cursor
if attr, err := ti.Parse("el1"); err == nil {
fmt.Fprintf(out, "%s", attr)
_, err = fmt.Fprintf(out, "%s", attr)
if err != nil {
return err
}
} else {
fmt.Fprintf(out, "\x1b[1K")
_, err := fmt.Fprintf(out, "\x1b[1K")
if err != nil {
return err
}
}
// Then clear line from cursor to end
if attr, err := ti.Parse("el"); err == nil {
fmt.Fprintf(out, "%s", attr)
_, err = fmt.Fprintf(out, "%s", attr)
if err != nil {
return err
}
} else {
fmt.Fprintf(out, "\x1b[K")
_, err := fmt.Fprintf(out, "\x1b[K")
if err != nil {
return err
}
}

return nil
}

func cursorUp(out io.Writer, ti termInfo, l int) {
func cursorUp(out io.Writer, ti termInfo, l int) error {
if l == 0 { // Should never be the case, but be tolerant
return
return nil
}
if attr, err := ti.Parse("cuu", l); err == nil {
fmt.Fprintf(out, "%s", attr)
_, err = fmt.Fprintf(out, "%s", attr)
if err != nil {
return err
}
} else {
fmt.Fprintf(out, "\x1b[%dA", l)
_, err := fmt.Fprintf(out, "\x1b[%dA", l)
if err != nil {
return err
}
}
return nil
}

func cursorDown(out io.Writer, ti termInfo, l int) {
func cursorDown(out io.Writer, ti termInfo, l int) error {
if l == 0 { // Should never be the case, but be tolerant
return
return nil
}
if attr, err := ti.Parse("cud", l); err == nil {
fmt.Fprintf(out, "%s", attr)
_, err = fmt.Fprintf(out, "%s", attr)
if err != nil {
return err
}
} else {
fmt.Fprintf(out, "\x1b[%dB", l)
_, err := fmt.Fprintf(out, "\x1b[%dB", l)
if err != nil {
return err
}
}

return nil
}

// Display displays the JSONMessage to `out`. `termInfo` is non-nil if `out`
Expand All @@ -219,29 +248,56 @@ func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error {
if termInfo != nil && jm.Stream == "" && jm.Progress != nil {
clearLine(out, termInfo)
endl = "\r"
fmt.Fprintf(out, endl)
_, err := fmt.Fprintf(out, endl)
if err != nil {
return err
}
} else if jm.Progress != nil && jm.Progress.String() != "" { //disable progressbar in non-terminal
return nil
}
if jm.TimeNano != 0 {
fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(RFC3339NanoFixed))
_, err := fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(RFC3339NanoFixed))
if err != nil {
return err
}
} else if jm.Time != 0 {
fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(RFC3339NanoFixed))
_, err := fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(RFC3339NanoFixed))
if err != nil {
return err
}
}
if jm.ID != "" {
fmt.Fprintf(out, "%s: ", jm.ID)
_, err := fmt.Fprintf(out, "%s: ", jm.ID)
if err != nil {
return err
}
}
if jm.From != "" {
fmt.Fprintf(out, "(from %s) ", jm.From)
_, err := fmt.Fprintf(out, "(from %s) ", jm.From)
if err != nil {
return err
}
}
if jm.Progress != nil && termInfo != nil {
fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
_, err := fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
if err != nil {
return err
}
} else if jm.ProgressMessage != "" { //deprecated
fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl)
_, err := fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl)
if err != nil {
return err
}
} else if jm.Stream != "" {
fmt.Fprintf(out, "%s%s", jm.Stream, endl)
_, err := fmt.Fprintf(out, "%s%s", jm.Stream, endl)
if err != nil {
return err
}
} else {
fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
_, err := fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
if err != nil {
return err
}
}
return nil
}
Expand Down Expand Up @@ -301,12 +357,17 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr,
line = len(ids)
ids[jm.ID] = line
if termInfo != nil {
fmt.Fprintf(out, "\n")
_, err := fmt.Fprintf(out, "\n")
if err != nil {
return err
}
}
}
diff = len(ids) - line
if termInfo != nil {
cursorUp(out, termInfo, diff)
if err := cursorUp(out, termInfo, diff); err != nil {
return err
}
}
} else {
// When outputting something that isn't progress
Expand All @@ -318,7 +379,9 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr,
}
err := jm.Display(out, termInfo)
if jm.ID != "" && termInfo != nil {
cursorDown(out, termInfo, diff)
if err := cursorDown(out, termInfo, diff); err != nil {
return err
}
}
if err != nil {
return err
Expand Down

0 comments on commit 1015523

Please sign in to comment.