github.com/tsuna/docker@v1.7.0-rc3/pkg/jsonmessage/jsonmessage.go (about)

     1  package jsonmessage
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/docker/docker/pkg/term"
    11  	"github.com/docker/docker/pkg/timeutils"
    12  	"github.com/docker/docker/pkg/units"
    13  )
    14  
    15  type JSONError struct {
    16  	Code    int    `json:"code,omitempty"`
    17  	Message string `json:"message,omitempty"`
    18  }
    19  
    20  func (e *JSONError) Error() string {
    21  	return e.Message
    22  }
    23  
    24  type JSONProgress struct {
    25  	terminalFd uintptr
    26  	Current    int   `json:"current,omitempty"`
    27  	Total      int   `json:"total,omitempty"`
    28  	Start      int64 `json:"start,omitempty"`
    29  }
    30  
    31  func (p *JSONProgress) String() string {
    32  	var (
    33  		width       = 200
    34  		pbBox       string
    35  		numbersBox  string
    36  		timeLeftBox string
    37  	)
    38  
    39  	ws, err := term.GetWinsize(p.terminalFd)
    40  	if err == nil {
    41  		width = int(ws.Width)
    42  	}
    43  
    44  	if p.Current <= 0 && p.Total <= 0 {
    45  		return ""
    46  	}
    47  	current := units.HumanSize(float64(p.Current))
    48  	if p.Total <= 0 {
    49  		return fmt.Sprintf("%8v", current)
    50  	}
    51  	total := units.HumanSize(float64(p.Total))
    52  	percentage := int(float64(p.Current)/float64(p.Total)*100) / 2
    53  	if percentage > 50 {
    54  		percentage = 50
    55  	}
    56  	if width > 110 {
    57  		// this number can't be negetive gh#7136
    58  		numSpaces := 0
    59  		if 50-percentage > 0 {
    60  			numSpaces = 50 - percentage
    61  		}
    62  		pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces))
    63  	}
    64  	numbersBox = fmt.Sprintf("%8v/%v", current, total)
    65  
    66  	if p.Current > 0 && p.Start > 0 && percentage < 50 {
    67  		fromStart := time.Now().UTC().Sub(time.Unix(int64(p.Start), 0))
    68  		perEntry := fromStart / time.Duration(p.Current)
    69  		left := time.Duration(p.Total-p.Current) * perEntry
    70  		left = (left / time.Second) * time.Second
    71  
    72  		if width > 50 {
    73  			timeLeftBox = " " + left.String()
    74  		}
    75  	}
    76  	return pbBox + numbersBox + timeLeftBox
    77  }
    78  
    79  type JSONMessage struct {
    80  	Stream          string        `json:"stream,omitempty"`
    81  	Status          string        `json:"status,omitempty"`
    82  	Progress        *JSONProgress `json:"progressDetail,omitempty"`
    83  	ProgressMessage string        `json:"progress,omitempty"` //deprecated
    84  	ID              string        `json:"id,omitempty"`
    85  	From            string        `json:"from,omitempty"`
    86  	Time            int64         `json:"time,omitempty"`
    87  	Error           *JSONError    `json:"errorDetail,omitempty"`
    88  	ErrorMessage    string        `json:"error,omitempty"` //deprecated
    89  }
    90  
    91  func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
    92  	if jm.Error != nil {
    93  		if jm.Error.Code == 401 {
    94  			return fmt.Errorf("Authentication is required.")
    95  		}
    96  		return jm.Error
    97  	}
    98  	var endl string
    99  	if isTerminal && jm.Stream == "" && jm.Progress != nil {
   100  		// <ESC>[2K = erase entire current line
   101  		fmt.Fprintf(out, "%c[2K\r", 27)
   102  		endl = "\r"
   103  	} else if jm.Progress != nil && jm.Progress.String() != "" { //disable progressbar in non-terminal
   104  		return nil
   105  	}
   106  	if jm.Time != 0 {
   107  		fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(timeutils.RFC3339NanoFixed))
   108  	}
   109  	if jm.ID != "" {
   110  		fmt.Fprintf(out, "%s: ", jm.ID)
   111  	}
   112  	if jm.From != "" {
   113  		fmt.Fprintf(out, "(from %s) ", jm.From)
   114  	}
   115  	if jm.Progress != nil && isTerminal {
   116  		fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
   117  	} else if jm.ProgressMessage != "" { //deprecated
   118  		fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl)
   119  	} else if jm.Stream != "" {
   120  		fmt.Fprintf(out, "%s%s", jm.Stream, endl)
   121  	} else {
   122  		fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
   123  	}
   124  	return nil
   125  }
   126  
   127  func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool) error {
   128  	var (
   129  		dec  = json.NewDecoder(in)
   130  		ids  = make(map[string]int)
   131  		diff = 0
   132  	)
   133  	for {
   134  		var jm JSONMessage
   135  		if err := dec.Decode(&jm); err != nil {
   136  			if err == io.EOF {
   137  				break
   138  			}
   139  			return err
   140  		}
   141  
   142  		if jm.Progress != nil {
   143  			jm.Progress.terminalFd = terminalFd
   144  		}
   145  		if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") {
   146  			line, ok := ids[jm.ID]
   147  			if !ok {
   148  				line = len(ids)
   149  				ids[jm.ID] = line
   150  				if isTerminal {
   151  					fmt.Fprintf(out, "\n")
   152  				}
   153  				diff = 0
   154  			} else {
   155  				diff = len(ids) - line
   156  			}
   157  			if jm.ID != "" && isTerminal {
   158  				// <ESC>[{diff}A = move cursor up diff rows
   159  				fmt.Fprintf(out, "%c[%dA", 27, diff)
   160  			}
   161  		}
   162  		err := jm.Display(out, isTerminal)
   163  		if jm.ID != "" && isTerminal {
   164  			// <ESC>[{diff}B = move cursor down diff rows
   165  			fmt.Fprintf(out, "%c[%dB", 27, diff)
   166  		}
   167  		if err != nil {
   168  			return err
   169  		}
   170  	}
   171  	return nil
   172  }