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