jsonmessage.go 4.3 KB

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