- Do not execute the template directly in the cli outout, go is not atomic
in this operation and can send bytes before failing the execution.
- Fail after evaluating a raw interface if the typed execution also
failed, assuming there is a template parsing error.
Signed-off-by: David Calavera <david.calavera@gmail.com>
| ... | ... |
@@ -59,7 +59,6 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
| 59 | 59 |
} |
| 60 | 60 |
|
| 61 | 61 |
for _, name := range cmd.Args() {
|
| 62 |
- |
|
| 63 | 62 |
if *inspectType == "" || *inspectType == "container" {
|
| 64 | 63 |
obj, _, err = readBody(cli.call("GET", "/containers/"+name+"/json?"+v.Encode(), nil, nil))
|
| 65 | 64 |
if err != nil && *inspectType == "container" {
|
| ... | ... |
@@ -101,42 +100,45 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
| 101 | 101 |
} else {
|
| 102 | 102 |
rdr := bytes.NewReader(obj) |
| 103 | 103 |
dec := json.NewDecoder(rdr) |
| 104 |
+ buf := bytes.NewBufferString("")
|
|
| 104 | 105 |
|
| 105 | 106 |
if isImage {
|
| 106 | 107 |
inspPtr := types.ImageInspect{}
|
| 107 | 108 |
if err := dec.Decode(&inspPtr); err != nil {
|
| 108 |
- fmt.Fprintf(cli.err, "%s\n", err) |
|
| 109 |
+ fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err) |
|
| 109 | 110 |
status = 1 |
| 110 |
- continue |
|
| 111 |
+ break |
|
| 111 | 112 |
} |
| 112 |
- if err := tmpl.Execute(cli.out, inspPtr); err != nil {
|
|
| 113 |
+ if err := tmpl.Execute(buf, inspPtr); err != nil {
|
|
| 113 | 114 |
rdr.Seek(0, 0) |
| 114 |
- var raw interface{}
|
|
| 115 |
- if err := dec.Decode(&raw); err != nil {
|
|
| 116 |
- return err |
|
| 117 |
- } |
|
| 118 |
- if err = tmpl.Execute(cli.out, raw); err != nil {
|
|
| 119 |
- return err |
|
| 115 |
+ var ok bool |
|
| 116 |
+ |
|
| 117 |
+ if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
|
|
| 118 |
+ fmt.Fprintf(cli.err, "Template parsing error: %v\n", err) |
|
| 119 |
+ status = 1 |
|
| 120 |
+ break |
|
| 120 | 121 |
} |
| 121 | 122 |
} |
| 122 | 123 |
} else {
|
| 123 | 124 |
inspPtr := types.ContainerJSON{}
|
| 124 | 125 |
if err := dec.Decode(&inspPtr); err != nil {
|
| 125 |
- fmt.Fprintf(cli.err, "%s\n", err) |
|
| 126 |
+ fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err) |
|
| 126 | 127 |
status = 1 |
| 127 |
- continue |
|
| 128 |
+ break |
|
| 128 | 129 |
} |
| 129 |
- if err := tmpl.Execute(cli.out, inspPtr); err != nil {
|
|
| 130 |
+ if err := tmpl.Execute(buf, inspPtr); err != nil {
|
|
| 130 | 131 |
rdr.Seek(0, 0) |
| 131 |
- var raw interface{}
|
|
| 132 |
- if err := dec.Decode(&raw); err != nil {
|
|
| 133 |
- return err |
|
| 134 |
- } |
|
| 135 |
- if err = tmpl.Execute(cli.out, raw); err != nil {
|
|
| 136 |
- return err |
|
| 132 |
+ var ok bool |
|
| 133 |
+ |
|
| 134 |
+ if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
|
|
| 135 |
+ fmt.Fprintf(cli.err, "Template parsing error: %v\n", err) |
|
| 136 |
+ status = 1 |
|
| 137 |
+ break |
|
| 137 | 138 |
} |
| 138 | 139 |
} |
| 139 | 140 |
} |
| 141 |
+ |
|
| 142 |
+ cli.out.Write(buf.Bytes()) |
|
| 140 | 143 |
cli.out.Write([]byte{'\n'})
|
| 141 | 144 |
} |
| 142 | 145 |
indented.WriteString(",")
|
| ... | ... |
@@ -162,3 +164,33 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
| 162 | 162 |
} |
| 163 | 163 |
return nil |
| 164 | 164 |
} |
| 165 |
+ |
|
| 166 |
+// decodeRawInspect executes the inspect template with a raw interface. |
|
| 167 |
+// This allows docker cli to parse inspect structs injected with Swarm fields. |
|
| 168 |
+// Unfortunately, go 1.4 doesn't fail executing invalid templates when the input is an interface. |
|
| 169 |
+// It doesn't allow to modify this behavior either, sending <no value> messages to the output. |
|
| 170 |
+// We assume that the template is invalid when there is a <no value>, if the template was valid |
|
| 171 |
+// we'd get <nil> or "" values. In that case we fail with the original error raised executing the |
|
| 172 |
+// template with the typed input. |
|
| 173 |
+// |
|
| 174 |
+// TODO: Go 1.5 allows to customize the error behavior, we can probably get rid of this as soon as |
|
| 175 |
+// we build Docker with that version: |
|
| 176 |
+// https://golang.org/pkg/text/template/#Template.Option |
|
| 177 |
+func (cli *DockerCli) decodeRawInspect(tmpl *template.Template, dec *json.Decoder) (*bytes.Buffer, bool) {
|
|
| 178 |
+ var raw interface{}
|
|
| 179 |
+ buf := bytes.NewBufferString("")
|
|
| 180 |
+ |
|
| 181 |
+ if rawErr := dec.Decode(&raw); rawErr != nil {
|
|
| 182 |
+ fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", rawErr) |
|
| 183 |
+ return buf, false |
|
| 184 |
+ } |
|
| 185 |
+ |
|
| 186 |
+ if rawErr := tmpl.Execute(buf, raw); rawErr != nil {
|
|
| 187 |
+ return buf, false |
|
| 188 |
+ } |
|
| 189 |
+ |
|
| 190 |
+ if strings.Contains(buf.String(), "<no value>") {
|
|
| 191 |
+ return buf, false |
|
| 192 |
+ } |
|
| 193 |
+ return buf, true |
|
| 194 |
+} |
| ... | ... |
@@ -82,7 +82,7 @@ func (s *DockerSuite) TestInspectTypeFlagContainer(c *check.C) {
|
| 82 | 82 |
|
| 83 | 83 |
dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "top") |
| 84 | 84 |
|
| 85 |
- formatStr := fmt.Sprintf("--format='{{.State.Running}}'")
|
|
| 85 |
+ formatStr := "--format='{{.State.Running}}'"
|
|
| 86 | 86 |
out, _ := dockerCmd(c, "inspect", "--type=container", formatStr, "busybox") |
| 87 | 87 |
c.Assert(out, checker.Equals, "true\n") // not a container JSON |
| 88 | 88 |
} |
| ... | ... |
@@ -290,19 +290,15 @@ func (s *DockerSuite) TestInspectNoSizeFlagContainer(c *check.C) {
|
| 290 | 290 |
|
| 291 | 291 |
dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "top") |
| 292 | 292 |
|
| 293 |
- formatStr := fmt.Sprintf("--format='{{.SizeRw}},{{.SizeRootFs}}'")
|
|
| 293 |
+ formatStr := "--format='{{.SizeRw}},{{.SizeRootFs}}'"
|
|
| 294 | 294 |
out, _ := dockerCmd(c, "inspect", "--type=container", formatStr, "busybox") |
| 295 | 295 |
c.Assert(strings.TrimSpace(out), check.Equals, "<nil>,<nil>", check.Commentf("Exepcted not to display size info: %s", out))
|
| 296 | 296 |
} |
| 297 | 297 |
|
| 298 | 298 |
func (s *DockerSuite) TestInspectSizeFlagContainer(c *check.C) {
|
| 299 |
- |
|
| 300 |
- //Both the container and image are named busybox. docker inspect will fetch container |
|
| 301 |
- //JSON SizeRw and SizeRootFs field. If there is a flag --size/-s, the fields are not <no value>. |
|
| 302 |
- |
|
| 303 | 299 |
dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "top") |
| 304 | 300 |
|
| 305 |
- formatStr := fmt.Sprintf("--format='{{.SizeRw}},{{.SizeRootFs}}'")
|
|
| 301 |
+ formatStr := "--format='{{.SizeRw}},{{.SizeRootFs}}'"
|
|
| 306 | 302 |
out, _ := dockerCmd(c, "inspect", "-s", "--type=container", formatStr, "busybox") |
| 307 | 303 |
sz := strings.Split(out, ",") |
| 308 | 304 |
|
| ... | ... |
@@ -311,14 +307,25 @@ func (s *DockerSuite) TestInspectSizeFlagContainer(c *check.C) {
|
| 311 | 311 |
} |
| 312 | 312 |
|
| 313 | 313 |
func (s *DockerSuite) TestInspectSizeFlagImage(c *check.C) {
|
| 314 |
+ dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "top") |
|
| 314 | 315 |
|
| 315 |
- //Both the container and image are named busybox. docker inspect will fetch image |
|
| 316 |
- //JSON SizeRw and SizeRootFs field. There are no these fields since they are only in containers. |
|
| 316 |
+ formatStr := "--format='{{.SizeRw}},{{.SizeRootFs}}'"
|
|
| 317 |
+ out, _, err := dockerCmdWithError("inspect", "-s", "--type=image", formatStr, "busybox")
|
|
| 318 |
+ |
|
| 319 |
+ // Template error rather than <no value> |
|
| 320 |
+ // This is a more correct behavior because images don't have sizes associated. |
|
| 321 |
+ c.Assert(err, check.Not(check.IsNil)) |
|
| 322 |
+ c.Assert(out, checker.Contains, "Template parsing error") |
|
| 323 |
+} |
|
| 324 |
+ |
|
| 325 |
+func (s *DockerSuite) TestInspectTempateError(c *check.C) {
|
|
| 326 |
+ //Both the container and image are named busybox. docker inspect will fetch container |
|
| 327 |
+ //JSON State.Running field. If the field is true, it's a container. |
|
| 317 | 328 |
|
| 318 | 329 |
dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "top") |
| 319 | 330 |
|
| 320 |
- formatStr := fmt.Sprintf("--format='{{.SizeRw}},{{.SizeRootFs}}'")
|
|
| 321 |
- out, _ := dockerCmd(c, "inspect", "-s", "--type=image", formatStr, "busybox") |
|
| 331 |
+ out, _, err := dockerCmdWithError("inspect", "--type=container", "--format='Format container: {{.ThisDoesNotExist}}'", "busybox")
|
|
| 322 | 332 |
|
| 323 |
- c.Assert(strings.TrimSpace(out), check.Equals, "<no value>,<no value>", check.Commentf("Fields SizeRw and SizeRootFs are not exepcted to exist"))
|
|
| 333 |
+ c.Assert(err, check.Not(check.IsNil)) |
|
| 334 |
+ c.Assert(out, checker.Contains, "Template parsing error") |
|
| 324 | 335 |
} |