inspector.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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, err)
  83. }
  84. i.buffer.Write(buffer.Bytes())
  85. i.buffer.WriteByte('\n')
  86. return nil
  87. }
  88. // Flush write the result of inspecting all elements into the output stream.
  89. func (i *TemplateInspector) Flush() error {
  90. if i.buffer.Len() == 0 {
  91. _, err := io.WriteString(i.outputStream, "\n")
  92. return err
  93. }
  94. _, err := io.Copy(i.outputStream, i.buffer)
  95. return err
  96. }
  97. // IndentedInspector uses a buffer to stop the indented representation of an element.
  98. type IndentedInspector struct {
  99. outputStream io.Writer
  100. elements []interface{}
  101. rawElements [][]byte
  102. }
  103. // NewIndentedInspector generates a new IndentedInspector.
  104. func NewIndentedInspector(outputStream io.Writer) Inspector {
  105. return &IndentedInspector{
  106. outputStream: outputStream,
  107. }
  108. }
  109. // Inspect writes the raw element with an indented json format.
  110. func (i *IndentedInspector) Inspect(typedElement interface{}, rawElement []byte) error {
  111. if rawElement != nil {
  112. i.rawElements = append(i.rawElements, rawElement)
  113. } else {
  114. i.elements = append(i.elements, typedElement)
  115. }
  116. return nil
  117. }
  118. // Flush write the result of inspecting all elements into the output stream.
  119. func (i *IndentedInspector) Flush() error {
  120. if len(i.elements) == 0 && len(i.rawElements) == 0 {
  121. _, err := io.WriteString(i.outputStream, "[]\n")
  122. return err
  123. }
  124. var buffer io.Reader
  125. if len(i.rawElements) > 0 {
  126. bytesBuffer := new(bytes.Buffer)
  127. bytesBuffer.WriteString("[")
  128. for idx, r := range i.rawElements {
  129. bytesBuffer.Write(r)
  130. if idx < len(i.rawElements)-1 {
  131. bytesBuffer.WriteString(",")
  132. }
  133. }
  134. bytesBuffer.WriteString("]")
  135. indented := new(bytes.Buffer)
  136. if err := json.Indent(indented, bytesBuffer.Bytes(), "", " "); err != nil {
  137. return err
  138. }
  139. buffer = indented
  140. } else {
  141. b, err := json.MarshalIndent(i.elements, "", " ")
  142. if err != nil {
  143. return err
  144. }
  145. buffer = bytes.NewReader(b)
  146. }
  147. if _, err := io.Copy(i.outputStream, buffer); err != nil {
  148. return err
  149. }
  150. _, err := io.WriteString(i.outputStream, "\n")
  151. return err
  152. }