inspector.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package inspect
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "text/template"
  8. "github.com/Sirupsen/logrus"
  9. "github.com/docker/docker/cli"
  10. "github.com/docker/docker/utils/templates"
  11. )
  12. // Inspector defines an interface to implement to process elements
  13. type Inspector interface {
  14. Inspect(typedElement interface{}, rawElement []byte) error
  15. Flush() error
  16. }
  17. // TemplateInspector uses a text template to inspect elements.
  18. type TemplateInspector struct {
  19. outputStream io.Writer
  20. buffer *bytes.Buffer
  21. tmpl *template.Template
  22. }
  23. // NewTemplateInspector creates a new inspector with a template.
  24. func NewTemplateInspector(outputStream io.Writer, tmpl *template.Template) Inspector {
  25. return &TemplateInspector{
  26. outputStream: outputStream,
  27. buffer: new(bytes.Buffer),
  28. tmpl: tmpl,
  29. }
  30. }
  31. // NewTemplateInspectorFromString creates a new TemplateInspector from a string
  32. // which is compiled into a template.
  33. func NewTemplateInspectorFromString(out io.Writer, tmplStr string) (Inspector, error) {
  34. if tmplStr == "" {
  35. return NewIndentedInspector(out), nil
  36. }
  37. tmpl, err := templates.Parse(tmplStr)
  38. if err != nil {
  39. return nil, fmt.Errorf("Template parsing error: %s", err)
  40. }
  41. return NewTemplateInspector(out, tmpl), nil
  42. }
  43. // GetRefFunc is a function which used by Inspect to fetch an object from a
  44. // reference
  45. type GetRefFunc func(ref string) (interface{}, []byte, error)
  46. // Inspect fetches objects by reference using GetRefFunc and writes the json
  47. // representation to the output writer.
  48. func Inspect(out io.Writer, references []string, tmplStr string, getRef GetRefFunc) error {
  49. inspector, err := NewTemplateInspectorFromString(out, tmplStr)
  50. if err != nil {
  51. return cli.StatusError{StatusCode: 64, Status: err.Error()}
  52. }
  53. var inspectErr error
  54. for _, ref := range references {
  55. element, raw, err := getRef(ref)
  56. if err != nil {
  57. inspectErr = err
  58. break
  59. }
  60. if err := inspector.Inspect(element, raw); err != nil {
  61. inspectErr = err
  62. break
  63. }
  64. }
  65. if err := inspector.Flush(); err != nil {
  66. logrus.Errorf("%s\n", err)
  67. }
  68. if inspectErr != nil {
  69. return cli.StatusError{StatusCode: 1, Status: inspectErr.Error()}
  70. }
  71. return nil
  72. }
  73. // Inspect executes the inspect template.
  74. // It decodes the raw element into a map if the initial execution fails.
  75. // This allows docker cli to parse inspect structs injected with Swarm fields.
  76. func (i *TemplateInspector) Inspect(typedElement interface{}, rawElement []byte) error {
  77. buffer := new(bytes.Buffer)
  78. if err := i.tmpl.Execute(buffer, typedElement); err != nil {
  79. if rawElement == nil {
  80. return fmt.Errorf("Template parsing error: %v", err)
  81. }
  82. return i.tryRawInspectFallback(rawElement)
  83. }
  84. i.buffer.Write(buffer.Bytes())
  85. i.buffer.WriteByte('\n')
  86. return nil
  87. }
  88. // tryRawInspectFallback executes the inspect template with a raw interface.
  89. // This allows docker cli to parse inspect structs injected with Swarm fields.
  90. func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte) error {
  91. var raw interface{}
  92. buffer := new(bytes.Buffer)
  93. rdr := bytes.NewReader(rawElement)
  94. dec := json.NewDecoder(rdr)
  95. if rawErr := dec.Decode(&raw); rawErr != nil {
  96. return fmt.Errorf("unable to read inspect data: %v", rawErr)
  97. }
  98. tmplMissingKey := i.tmpl.Option("missingkey=error")
  99. if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
  100. return fmt.Errorf("Template parsing error: %v", rawErr)
  101. }
  102. i.buffer.Write(buffer.Bytes())
  103. i.buffer.WriteByte('\n')
  104. return nil
  105. }
  106. // Flush write the result of inspecting all elements into the output stream.
  107. func (i *TemplateInspector) Flush() error {
  108. if i.buffer.Len() == 0 {
  109. _, err := io.WriteString(i.outputStream, "\n")
  110. return err
  111. }
  112. _, err := io.Copy(i.outputStream, i.buffer)
  113. return err
  114. }
  115. // IndentedInspector uses a buffer to stop the indented representation of an element.
  116. type IndentedInspector struct {
  117. outputStream io.Writer
  118. elements []interface{}
  119. rawElements [][]byte
  120. }
  121. // NewIndentedInspector generates a new IndentedInspector.
  122. func NewIndentedInspector(outputStream io.Writer) Inspector {
  123. return &IndentedInspector{
  124. outputStream: outputStream,
  125. }
  126. }
  127. // Inspect writes the raw element with an indented json format.
  128. func (i *IndentedInspector) Inspect(typedElement interface{}, rawElement []byte) error {
  129. if rawElement != nil {
  130. i.rawElements = append(i.rawElements, rawElement)
  131. } else {
  132. i.elements = append(i.elements, typedElement)
  133. }
  134. return nil
  135. }
  136. // Flush write the result of inspecting all elements into the output stream.
  137. func (i *IndentedInspector) Flush() error {
  138. if len(i.elements) == 0 && len(i.rawElements) == 0 {
  139. _, err := io.WriteString(i.outputStream, "[]\n")
  140. return err
  141. }
  142. var buffer io.Reader
  143. if len(i.rawElements) > 0 {
  144. bytesBuffer := new(bytes.Buffer)
  145. bytesBuffer.WriteString("[")
  146. for idx, r := range i.rawElements {
  147. bytesBuffer.Write(r)
  148. if idx < len(i.rawElements)-1 {
  149. bytesBuffer.WriteString(",")
  150. }
  151. }
  152. bytesBuffer.WriteString("]")
  153. indented := new(bytes.Buffer)
  154. if err := json.Indent(indented, bytesBuffer.Bytes(), "", " "); err != nil {
  155. return err
  156. }
  157. buffer = indented
  158. } else {
  159. b, err := json.MarshalIndent(i.elements, "", " ")
  160. if err != nil {
  161. return err
  162. }
  163. buffer = bytes.NewReader(b)
  164. }
  165. if _, err := io.Copy(i.outputStream, buffer); err != nil {
  166. return err
  167. }
  168. _, err := io.WriteString(i.outputStream, "\n")
  169. return err
  170. }