| ... | ... |
@@ -2294,7 +2294,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h |
| 2294 | 2294 |
} |
| 2295 | 2295 |
|
| 2296 | 2296 |
if matchesContentType(resp.Header.Get("Content-Type"), "application/json") {
|
| 2297 |
- return utils.DisplayJSONMessagesStream(resp.Body, out, cli.isTerminal) |
|
| 2297 |
+ return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal) |
|
| 2298 | 2298 |
} |
| 2299 | 2299 |
if _, err := io.Copy(out, resp.Body); err != nil {
|
| 2300 | 2300 |
return err |
| ... | ... |
@@ -3,6 +3,7 @@ package utils |
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
"fmt" |
| 6 |
+ "github.com/dotcloud/docker/term" |
|
| 6 | 7 |
"io" |
| 7 | 8 |
"strings" |
| 8 | 9 |
"time" |
| ... | ... |
@@ -18,27 +19,50 @@ func (e *JSONError) Error() string {
|
| 18 | 18 |
} |
| 19 | 19 |
|
| 20 | 20 |
type JSONProgress struct {
|
| 21 |
- Current int `json:"current,omitempty"` |
|
| 22 |
- Total int `json:"total,omitempty"` |
|
| 23 |
- Start int64 `json:"start,omitempty"` |
|
| 21 |
+ terminalFd uintptr |
|
| 22 |
+ Current int `json:"current,omitempty"` |
|
| 23 |
+ Total int `json:"total,omitempty"` |
|
| 24 |
+ Start int64 `json:"start,omitempty"` |
|
| 24 | 25 |
} |
| 25 | 26 |
|
| 26 | 27 |
func (p *JSONProgress) String() string {
|
| 28 |
+ var ( |
|
| 29 |
+ width = 200 |
|
| 30 |
+ pbBox string |
|
| 31 |
+ numbersBox string |
|
| 32 |
+ timeLeftBox string |
|
| 33 |
+ ) |
|
| 34 |
+ |
|
| 35 |
+ ws, err := term.GetWinsize(p.terminalFd) |
|
| 36 |
+ if err == nil {
|
|
| 37 |
+ width = int(ws.Width) |
|
| 38 |
+ } |
|
| 39 |
+ |
|
| 27 | 40 |
if p.Current == 0 && p.Total == 0 {
|
| 28 | 41 |
return "" |
| 29 | 42 |
} |
| 30 | 43 |
current := HumanSize(int64(p.Current)) |
| 31 | 44 |
if p.Total == 0 {
|
| 32 |
- return fmt.Sprintf("%8v/?", current)
|
|
| 45 |
+ return fmt.Sprintf("%8v", current)
|
|
| 33 | 46 |
} |
| 34 | 47 |
total := HumanSize(int64(p.Total)) |
| 35 | 48 |
percentage := int(float64(p.Current)/float64(p.Total)*100) / 2 |
| 49 |
+ if width > 110 {
|
|
| 50 |
+ pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", 50-percentage))
|
|
| 51 |
+ } |
|
| 52 |
+ numbersBox = fmt.Sprintf("%8v/%v", current, total)
|
|
| 36 | 53 |
|
| 37 |
- fromStart := time.Now().UTC().Sub(time.Unix(int64(p.Start), 0)) |
|
| 38 |
- perEntry := fromStart / time.Duration(p.Current) |
|
| 39 |
- left := time.Duration(p.Total-p.Current) * perEntry |
|
| 40 |
- left = (left / time.Second) * time.Second |
|
| 41 |
- return fmt.Sprintf("[%s>%s] %8v/%v %s", strings.Repeat("=", percentage), strings.Repeat(" ", 50-percentage), current, total, left.String())
|
|
| 54 |
+ if p.Start > 0 {
|
|
| 55 |
+ fromStart := time.Now().UTC().Sub(time.Unix(int64(p.Start), 0)) |
|
| 56 |
+ perEntry := fromStart / time.Duration(p.Current) |
|
| 57 |
+ left := time.Duration(p.Total-p.Current) * perEntry |
|
| 58 |
+ left = (left / time.Second) * time.Second |
|
| 59 |
+ |
|
| 60 |
+ if width > 50 {
|
|
| 61 |
+ timeLeftBox = " " + left.String() |
|
| 62 |
+ } |
|
| 63 |
+ } |
|
| 64 |
+ return pbBox + numbersBox + timeLeftBox |
|
| 42 | 65 |
} |
| 43 | 66 |
|
| 44 | 67 |
type JSONMessage struct {
|
| ... | ... |
@@ -84,7 +108,7 @@ func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
|
| 84 | 84 |
return nil |
| 85 | 85 |
} |
| 86 | 86 |
|
| 87 |
-func DisplayJSONMessagesStream(in io.Reader, out io.Writer, isTerminal bool) error {
|
|
| 87 |
+func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool) error {
|
|
| 88 | 88 |
var ( |
| 89 | 89 |
dec = json.NewDecoder(in) |
| 90 | 90 |
ids = make(map[string]int) |
| ... | ... |
@@ -98,6 +122,10 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, isTerminal bool) err |
| 98 | 98 |
} |
| 99 | 99 |
return err |
| 100 | 100 |
} |
| 101 |
+ |
|
| 102 |
+ if jm.Progress != nil {
|
|
| 103 |
+ jm.Progress.terminalFd = terminalFd |
|
| 104 |
+ } |
|
| 101 | 105 |
if (jm.Progress != nil || jm.ProgressMessage != "") && jm.ID != "" {
|
| 102 | 106 |
line, ok := ids[jm.ID] |
| 103 | 107 |
if !ok {
|
| ... | ... |
@@ -12,13 +12,18 @@ func TestError(t *testing.T) {
|
| 12 | 12 |
} |
| 13 | 13 |
|
| 14 | 14 |
func TestProgress(t *testing.T) {
|
| 15 |
- jp := JSONProgress{0, 0, 0}
|
|
| 15 |
+ jp := JSONProgress{}
|
|
| 16 | 16 |
if jp.String() != "" {
|
| 17 | 17 |
t.Fatalf("Expected empty string, got '%s'", jp.String())
|
| 18 | 18 |
} |
| 19 | 19 |
|
| 20 |
- jp2 := JSONProgress{1, 0, 0}
|
|
| 21 |
- if jp2.String() != " 1 B/?" {
|
|
| 22 |
- t.Fatalf("Expected ' 1/?', got '%s'", jp2.String())
|
|
| 20 |
+ jp2 := JSONProgress{Current: 1}
|
|
| 21 |
+ if jp2.String() != " 1 B" {
|
|
| 22 |
+ t.Fatalf("Expected ' 1 B', got '%s'", jp2.String())
|
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ jp3 := JSONProgress{Current: 50, Total: 100}
|
|
| 26 |
+ if jp3.String() != "[=========================> ] 50 B/100 B" {
|
|
| 27 |
+ t.Fatalf("Expected '[=========================> ] 50 B/100 B', got '%s'", jp3.String())
|
|
| 23 | 28 |
} |
| 24 | 29 |
} |