diff --git a/commands.go b/commands.go index c024333a65..96c16fa268 100644 --- a/commands.go +++ b/commands.go @@ -2303,7 +2303,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h } if matchesContentType(resp.Header.Get("Content-Type"), "application/json") { - return utils.DisplayJSONMessagesStream(resp.Body, out, cli.isTerminal) + return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal) } if _, err := io.Copy(out, resp.Body); err != nil { return err diff --git a/utils/jsonmessage.go b/utils/jsonmessage.go index 809524cd28..eaa90a5b64 100644 --- a/utils/jsonmessage.go +++ b/utils/jsonmessage.go @@ -3,6 +3,7 @@ package utils import ( "encoding/json" "fmt" + "github.com/dotcloud/docker/term" "io" "strings" "time" @@ -18,27 +19,50 @@ func (e *JSONError) Error() string { } type JSONProgress struct { - Current int `json:"current,omitempty"` - Total int `json:"total,omitempty"` - Start int64 `json:"start,omitempty"` + terminalFd uintptr + Current int `json:"current,omitempty"` + Total int `json:"total,omitempty"` + Start int64 `json:"start,omitempty"` } func (p *JSONProgress) String() string { + var ( + width = 200 + pbBox string + numbersBox string + timeLeftBox string + ) + + ws, err := term.GetWinsize(p.terminalFd) + if err == nil { + width = int(ws.Width) + } + if p.Current == 0 && p.Total == 0 { return "" } current := HumanSize(int64(p.Current)) if p.Total == 0 { - return fmt.Sprintf("%8v/?", current) + return fmt.Sprintf("%8v", current) } total := HumanSize(int64(p.Total)) percentage := int(float64(p.Current)/float64(p.Total)*100) / 2 + if width > 110 { + pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", 50-percentage)) + } + numbersBox = fmt.Sprintf("%8v/%v", current, total) - fromStart := time.Now().UTC().Sub(time.Unix(int64(p.Start), 0)) - perEntry := fromStart / time.Duration(p.Current) - left := time.Duration(p.Total-p.Current) * perEntry - left = (left / time.Second) * time.Second - return fmt.Sprintf("[%s>%s] %8v/%v %s", strings.Repeat("=", percentage), strings.Repeat(" ", 50-percentage), current, total, left.String()) + if p.Start > 0 { + fromStart := time.Now().UTC().Sub(time.Unix(int64(p.Start), 0)) + perEntry := fromStart / time.Duration(p.Current) + left := time.Duration(p.Total-p.Current) * perEntry + left = (left / time.Second) * time.Second + + if width > 50 { + timeLeftBox = " " + left.String() + } + } + return pbBox + numbersBox + timeLeftBox } type JSONMessage struct { @@ -84,7 +108,7 @@ func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error { return nil } -func DisplayJSONMessagesStream(in io.Reader, out io.Writer, isTerminal bool) error { +func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool) error { var ( dec = json.NewDecoder(in) ids = make(map[string]int) @@ -98,6 +122,10 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, isTerminal bool) err } return err } + + if jm.Progress != nil { + jm.Progress.terminalFd = terminalFd + } if (jm.Progress != nil || jm.ProgressMessage != "") && jm.ID != "" { line, ok := ids[jm.ID] if !ok { diff --git a/utils/jsonmessage_test.go b/utils/jsonmessage_test.go index 9421d97a86..ecf1896762 100644 --- a/utils/jsonmessage_test.go +++ b/utils/jsonmessage_test.go @@ -12,13 +12,18 @@ func TestError(t *testing.T) { } func TestProgress(t *testing.T) { - jp := JSONProgress{0, 0, 0} + jp := JSONProgress{} if jp.String() != "" { t.Fatalf("Expected empty string, got '%s'", jp.String()) } - jp2 := JSONProgress{1, 0, 0} - if jp2.String() != " 1 B/?" { - t.Fatalf("Expected ' 1/?', got '%s'", jp2.String()) + jp2 := JSONProgress{Current: 1} + if jp2.String() != " 1 B" { + t.Fatalf("Expected ' 1 B', got '%s'", jp2.String()) + } + + jp3 := JSONProgress{Current: 50, Total: 100} + if jp3.String() != "[=========================> ] 50 B/100 B" { + t.Fatalf("Expected '[=========================> ] 50 B/100 B', got '%s'", jp3.String()) } }