inspect.go 5.6 KB

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