inspect.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. package client
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net/url"
  8. "strings"
  9. "text/template"
  10. "github.com/docker/docker/api/types"
  11. Cli "github.com/docker/docker/cli"
  12. flag "github.com/docker/docker/pkg/mflag"
  13. )
  14. var funcMap = template.FuncMap{
  15. "json": func(v interface{}) string {
  16. a, _ := json.Marshal(v)
  17. return string(a)
  18. },
  19. }
  20. // CmdInspect displays low-level information on one or more containers or images.
  21. //
  22. // Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
  23. func (cli *DockerCli) CmdInspect(args ...string) error {
  24. cmd := Cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, Cli.DockerCommands["inspect"].Description, true)
  25. tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template")
  26. inspectType := cmd.String([]string{"-type"}, "", "Return JSON for specified type, (e.g image or container)")
  27. size := cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes if the type is container")
  28. cmd.Require(flag.Min, 1)
  29. cmd.ParseFlags(args, true)
  30. var tmpl *template.Template
  31. var err error
  32. var obj []byte
  33. if *tmplStr != "" {
  34. if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
  35. return Cli.StatusError{StatusCode: 64,
  36. Status: "Template parsing error: " + err.Error()}
  37. }
  38. }
  39. if *inspectType != "" && *inspectType != "container" && *inspectType != "image" {
  40. return fmt.Errorf("%q is not a valid value for --type", *inspectType)
  41. }
  42. indented := new(bytes.Buffer)
  43. indented.WriteString("[\n")
  44. status := 0
  45. isImage := false
  46. v := url.Values{}
  47. if *size {
  48. v.Set("size", "1")
  49. }
  50. for _, name := range cmd.Args() {
  51. if *inspectType == "" || *inspectType == "container" {
  52. obj, _, err = readBody(cli.call("GET", "/containers/"+name+"/json?"+v.Encode(), nil, nil))
  53. if err != nil {
  54. if err == errConnectionFailed {
  55. return err
  56. }
  57. if *inspectType == "container" {
  58. if strings.Contains(err.Error(), "No such") {
  59. fmt.Fprintf(cli.err, "Error: No such container: %s\n", name)
  60. } else {
  61. fmt.Fprintf(cli.err, "%s", err)
  62. }
  63. status = 1
  64. continue
  65. }
  66. }
  67. }
  68. if obj == nil && (*inspectType == "" || *inspectType == "image") {
  69. obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
  70. isImage = true
  71. if err != nil {
  72. if err == errConnectionFailed {
  73. return err
  74. }
  75. if strings.Contains(err.Error(), "No such") {
  76. if *inspectType == "" {
  77. fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
  78. } else {
  79. fmt.Fprintf(cli.err, "Error: No such image: %s\n", name)
  80. }
  81. } else {
  82. fmt.Fprintf(cli.err, "%s", err)
  83. }
  84. status = 1
  85. continue
  86. }
  87. }
  88. if tmpl == nil {
  89. if err := json.Indent(indented, obj, "", " "); err != nil {
  90. fmt.Fprintf(cli.err, "%s\n", err)
  91. status = 1
  92. continue
  93. }
  94. } else {
  95. rdr := bytes.NewReader(obj)
  96. dec := json.NewDecoder(rdr)
  97. buf := bytes.NewBufferString("")
  98. if isImage {
  99. inspPtr := types.ImageInspect{}
  100. if err := dec.Decode(&inspPtr); err != nil {
  101. fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err)
  102. status = 1
  103. break
  104. }
  105. if err := tmpl.Execute(buf, inspPtr); err != nil {
  106. rdr.Seek(0, 0)
  107. var ok bool
  108. if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
  109. fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
  110. status = 1
  111. break
  112. }
  113. }
  114. } else {
  115. inspPtr := types.ContainerJSON{}
  116. if err := dec.Decode(&inspPtr); err != nil {
  117. fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err)
  118. status = 1
  119. break
  120. }
  121. if err := tmpl.Execute(buf, inspPtr); err != nil {
  122. rdr.Seek(0, 0)
  123. var ok bool
  124. if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
  125. fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
  126. status = 1
  127. break
  128. }
  129. }
  130. }
  131. cli.out.Write(buf.Bytes())
  132. cli.out.Write([]byte{'\n'})
  133. }
  134. indented.WriteString(",")
  135. }
  136. if indented.Len() > 1 {
  137. // Remove trailing ','
  138. indented.Truncate(indented.Len() - 1)
  139. }
  140. indented.WriteString("]\n")
  141. if tmpl == nil {
  142. // Note that we will always write "[]" when "-f" isn't specified,
  143. // to make sure the output would always be array, see
  144. // https://github.com/docker/docker/pull/9500#issuecomment-65846734
  145. if _, err := io.Copy(cli.out, indented); err != nil {
  146. return err
  147. }
  148. }
  149. if status != 0 {
  150. return Cli.StatusError{StatusCode: status}
  151. }
  152. return nil
  153. }
  154. // decodeRawInspect executes the inspect template with a raw interface.
  155. // This allows docker cli to parse inspect structs injected with Swarm fields.
  156. // Unfortunately, go 1.4 doesn't fail executing invalid templates when the input is an interface.
  157. // It doesn't allow to modify this behavior either, sending <no value> messages to the output.
  158. // We assume that the template is invalid when there is a <no value>, if the template was valid
  159. // we'd get <nil> or "" values. In that case we fail with the original error raised executing the
  160. // template with the typed input.
  161. //
  162. // TODO: Go 1.5 allows to customize the error behavior, we can probably get rid of this as soon as
  163. // we build Docker with that version:
  164. // https://golang.org/pkg/text/template/#Template.Option
  165. func (cli *DockerCli) decodeRawInspect(tmpl *template.Template, dec *json.Decoder) (*bytes.Buffer, bool) {
  166. var raw interface{}
  167. buf := bytes.NewBufferString("")
  168. if rawErr := dec.Decode(&raw); rawErr != nil {
  169. fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", rawErr)
  170. return buf, false
  171. }
  172. if rawErr := tmpl.Execute(buf, raw); rawErr != nil {
  173. return buf, false
  174. }
  175. if strings.Contains(buf.String(), "<no value>") {
  176. return buf, false
  177. }
  178. return buf, true
  179. }