jsonmessage.go 3.9 KB

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