Signed-off-by: David Calavera <david.calavera@gmail.com>
| ... | ... |
@@ -26,6 +26,7 @@ type apiClient interface {
|
| 26 | 26 |
ContainerExecStart(execID string, config types.ExecStartCheck) error |
| 27 | 27 |
ContainerExport(containerID string) (io.ReadCloser, error) |
| 28 | 28 |
ContainerInspect(containerID string) (types.ContainerJSON, error) |
| 29 |
+ ContainerInspectWithRaw(containerID string, getSize bool) (types.ContainerJSON, []byte, error) |
|
| 29 | 30 |
ContainerKill(containerID, signal string) error |
| 30 | 31 |
ContainerList(options types.ContainerListOptions) ([]types.Container, error) |
| 31 | 32 |
ContainerLogs(options types.ContainerLogsOptions) (io.ReadCloser, error) |
| ... | ... |
@@ -47,6 +48,7 @@ type apiClient interface {
|
| 47 | 47 |
ImageCreate(options types.ImageCreateOptions) (io.ReadCloser, error) |
| 48 | 48 |
ImageHistory(imageID string) ([]types.ImageHistory, error) |
| 49 | 49 |
ImageImport(options types.ImageImportOptions) (io.ReadCloser, error) |
| 50 |
+ ImageInspectWithRaw(imageID string, getSize bool) (types.ImageInspect, []byte, error) |
|
| 50 | 51 |
ImageList(options types.ImageListOptions) ([]types.Image, error) |
| 51 | 52 |
ImageLoad(input io.Reader) (io.ReadCloser, error) |
| 52 | 53 |
ImagePull(options types.ImagePullOptions, privilegeFunc lib.RequestPrivilegeFunc) (io.ReadCloser, error) |
| ... | ... |
@@ -1,16 +1,12 @@ |
| 1 | 1 |
package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "bytes" |
|
| 5 | 4 |
"encoding/json" |
| 6 | 5 |
"fmt" |
| 7 |
- "io" |
|
| 8 |
- "net/http" |
|
| 9 |
- "net/url" |
|
| 10 |
- "strings" |
|
| 11 | 6 |
"text/template" |
| 12 | 7 |
|
| 13 |
- "github.com/docker/docker/api/types" |
|
| 8 |
+ "github.com/docker/docker/api/client/inspect" |
|
| 9 |
+ "github.com/docker/docker/api/client/lib" |
|
| 14 | 10 |
Cli "github.com/docker/docker/cli" |
| 15 | 11 |
flag "github.com/docker/docker/pkg/mflag" |
| 16 | 12 |
) |
| ... | ... |
@@ -34,10 +30,15 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
| 34 | 34 |
|
| 35 | 35 |
cmd.ParseFlags(args, true) |
| 36 | 36 |
|
| 37 |
- var tmpl *template.Template |
|
| 38 |
- var err error |
|
| 39 |
- var obj []byte |
|
| 40 |
- var statusCode int |
|
| 37 |
+ if *inspectType != "" && *inspectType != "container" && *inspectType != "image" {
|
|
| 38 |
+ return fmt.Errorf("%q is not a valid value for --type", *inspectType)
|
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ var ( |
|
| 42 |
+ err error |
|
| 43 |
+ tmpl *template.Template |
|
| 44 |
+ elementInspector inspect.Inspector |
|
| 45 |
+ ) |
|
| 41 | 46 |
|
| 42 | 47 |
if *tmplStr != "" {
|
| 43 | 48 |
if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
|
| ... | ... |
@@ -46,160 +47,84 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
| 46 | 46 |
} |
| 47 | 47 |
} |
| 48 | 48 |
|
| 49 |
- if *inspectType != "" && *inspectType != "container" && *inspectType != "image" {
|
|
| 50 |
- return fmt.Errorf("%q is not a valid value for --type", *inspectType)
|
|
| 49 |
+ if tmpl != nil {
|
|
| 50 |
+ elementInspector = inspect.NewTemplateInspector(cli.out, tmpl) |
|
| 51 |
+ } else {
|
|
| 52 |
+ elementInspector = inspect.NewIndentedInspector(cli.out) |
|
| 51 | 53 |
} |
| 52 | 54 |
|
| 53 |
- indented := new(bytes.Buffer) |
|
| 54 |
- indented.WriteString("[\n")
|
|
| 55 |
- status := 0 |
|
| 56 |
- isImage := false |
|
| 55 |
+ switch *inspectType {
|
|
| 56 |
+ case "container": |
|
| 57 |
+ err = cli.inspectContainers(cmd.Args(), *size, elementInspector) |
|
| 58 |
+ case "images": |
|
| 59 |
+ err = cli.inspectImages(cmd.Args(), *size, elementInspector) |
|
| 60 |
+ default: |
|
| 61 |
+ err = cli.inspectAll(cmd.Args(), *size, elementInspector) |
|
| 62 |
+ } |
|
| 57 | 63 |
|
| 58 |
- v := url.Values{}
|
|
| 59 |
- if *size {
|
|
| 60 |
- v.Set("size", "1")
|
|
| 64 |
+ if err := elementInspector.Flush(); err != nil {
|
|
| 65 |
+ return err |
|
| 61 | 66 |
} |
| 67 |
+ return err |
|
| 68 |
+} |
|
| 62 | 69 |
|
| 63 |
- for _, name := range cmd.Args() {
|
|
| 64 |
- if *inspectType == "" || *inspectType == "container" {
|
|
| 65 |
- obj, statusCode, err = readBody(cli.call("GET", "/containers/"+name+"/json?"+v.Encode(), nil, nil))
|
|
| 66 |
- if err != nil {
|
|
| 67 |
- if err == errConnectionFailed {
|
|
| 68 |
- return err |
|
| 69 |
- } |
|
| 70 |
- if *inspectType == "container" {
|
|
| 71 |
- if statusCode == http.StatusNotFound {
|
|
| 72 |
- fmt.Fprintf(cli.err, "Error: No such container: %s\n", name) |
|
| 73 |
- } else {
|
|
| 74 |
- fmt.Fprintf(cli.err, "%s", err) |
|
| 75 |
- } |
|
| 76 |
- status = 1 |
|
| 77 |
- continue |
|
| 78 |
- } |
|
| 70 |
+func (cli *DockerCli) inspectContainers(containerIDs []string, getSize bool, elementInspector inspect.Inspector) error {
|
|
| 71 |
+ for _, containerID := range containerIDs {
|
|
| 72 |
+ if err := cli.inspectContainer(containerID, getSize, elementInspector); err != nil {
|
|
| 73 |
+ if lib.IsErrContainerNotFound(err) {
|
|
| 74 |
+ return fmt.Errorf("Error: No such container: %s\n", containerID)
|
|
| 79 | 75 |
} |
| 76 |
+ return err |
|
| 80 | 77 |
} |
| 78 |
+ } |
|
| 79 |
+ return nil |
|
| 80 |
+} |
|
| 81 | 81 |
|
| 82 |
- if obj == nil && (*inspectType == "" || *inspectType == "image") {
|
|
| 83 |
- obj, statusCode, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
|
|
| 84 |
- isImage = true |
|
| 85 |
- if err != nil {
|
|
| 86 |
- if err == errConnectionFailed {
|
|
| 87 |
- return err |
|
| 88 |
- } |
|
| 89 |
- if statusCode == http.StatusNotFound {
|
|
| 90 |
- if *inspectType == "" {
|
|
| 91 |
- fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name) |
|
| 92 |
- } else {
|
|
| 93 |
- fmt.Fprintf(cli.err, "Error: No such image: %s\n", name) |
|
| 94 |
- } |
|
| 95 |
- } else {
|
|
| 96 |
- fmt.Fprintf(cli.err, "%s", err) |
|
| 97 |
- } |
|
| 98 |
- status = 1 |
|
| 99 |
- continue |
|
| 82 |
+func (cli *DockerCli) inspectImages(imageIDs []string, getSize bool, elementInspector inspect.Inspector) error {
|
|
| 83 |
+ for _, imageID := range imageIDs {
|
|
| 84 |
+ if err := cli.inspectImage(imageID, getSize, elementInspector); err != nil {
|
|
| 85 |
+ if lib.IsErrImageNotFound(err) {
|
|
| 86 |
+ return fmt.Errorf("Error: No such image: %s\n", imageID)
|
|
| 100 | 87 |
} |
| 88 |
+ return err |
|
| 101 | 89 |
} |
| 90 |
+ } |
|
| 91 |
+ return nil |
|
| 92 |
+} |
|
| 102 | 93 |
|
| 103 |
- if tmpl == nil {
|
|
| 104 |
- if err := json.Indent(indented, obj, "", " "); err != nil {
|
|
| 105 |
- fmt.Fprintf(cli.err, "%s\n", err) |
|
| 106 |
- status = 1 |
|
| 107 |
- continue |
|
| 108 |
- } |
|
| 109 |
- } else {
|
|
| 110 |
- rdr := bytes.NewReader(obj) |
|
| 111 |
- dec := json.NewDecoder(rdr) |
|
| 112 |
- buf := bytes.NewBufferString("")
|
|
| 113 |
- |
|
| 114 |
- if isImage {
|
|
| 115 |
- inspPtr := types.ImageInspect{}
|
|
| 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 |
- |
|
| 125 |
- if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
|
|
| 126 |
- fmt.Fprintf(cli.err, "Template parsing error: %v\n", err) |
|
| 127 |
- status = 1 |
|
| 128 |
- break |
|
| 129 |
- } |
|
| 130 |
- } |
|
| 131 |
- } else {
|
|
| 132 |
- inspPtr := types.ContainerJSON{}
|
|
| 133 |
- if err := dec.Decode(&inspPtr); err != nil {
|
|
| 134 |
- fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err) |
|
| 135 |
- status = 1 |
|
| 136 |
- break |
|
| 137 |
- } |
|
| 138 |
- if err := tmpl.Execute(buf, inspPtr); err != nil {
|
|
| 139 |
- rdr.Seek(0, 0) |
|
| 140 |
- var ok bool |
|
| 141 |
- |
|
| 142 |
- if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
|
|
| 143 |
- fmt.Fprintf(cli.err, "Template parsing error: %v\n", err) |
|
| 144 |
- status = 1 |
|
| 145 |
- break |
|
| 94 |
+func (cli *DockerCli) inspectAll(ids []string, getSize bool, elementInspector inspect.Inspector) error {
|
|
| 95 |
+ for _, id := range ids {
|
|
| 96 |
+ if err := cli.inspectContainer(id, getSize, elementInspector); err != nil {
|
|
| 97 |
+ // Search for image with that id if a container doesn't exist. |
|
| 98 |
+ if lib.IsErrContainerNotFound(err) {
|
|
| 99 |
+ if err := cli.inspectImage(id, getSize, elementInspector); err != nil {
|
|
| 100 |
+ if lib.IsErrImageNotFound(err) {
|
|
| 101 |
+ return fmt.Errorf("Error: No such image or container: %s", id)
|
|
| 146 | 102 |
} |
| 103 |
+ return err |
|
| 147 | 104 |
} |
| 105 |
+ continue |
|
| 148 | 106 |
} |
| 149 |
- |
|
| 150 |
- cli.out.Write(buf.Bytes()) |
|
| 151 |
- cli.out.Write([]byte{'\n'})
|
|
| 152 |
- } |
|
| 153 |
- indented.WriteString(",")
|
|
| 154 |
- } |
|
| 155 |
- |
|
| 156 |
- if indented.Len() > 1 {
|
|
| 157 |
- // Remove trailing ',' |
|
| 158 |
- indented.Truncate(indented.Len() - 1) |
|
| 159 |
- } |
|
| 160 |
- indented.WriteString("]\n")
|
|
| 161 |
- |
|
| 162 |
- if tmpl == nil {
|
|
| 163 |
- // Note that we will always write "[]" when "-f" isn't specified, |
|
| 164 |
- // to make sure the output would always be array, see |
|
| 165 |
- // https://github.com/docker/docker/pull/9500#issuecomment-65846734 |
|
| 166 |
- if _, err := io.Copy(cli.out, indented); err != nil {
|
|
| 167 | 107 |
return err |
| 168 | 108 |
} |
| 169 | 109 |
} |
| 170 |
- |
|
| 171 |
- if status != 0 {
|
|
| 172 |
- return Cli.StatusError{StatusCode: status}
|
|
| 173 |
- } |
|
| 174 | 110 |
return nil |
| 175 | 111 |
} |
| 176 | 112 |
|
| 177 |
-// decodeRawInspect executes the inspect template with a raw interface. |
|
| 178 |
-// This allows docker cli to parse inspect structs injected with Swarm fields. |
|
| 179 |
-// Unfortunately, go 1.4 doesn't fail executing invalid templates when the input is an interface. |
|
| 180 |
-// It doesn't allow to modify this behavior either, sending <no value> messages to the output. |
|
| 181 |
-// We assume that the template is invalid when there is a <no value>, if the template was valid |
|
| 182 |
-// we'd get <nil> or "" values. In that case we fail with the original error raised executing the |
|
| 183 |
-// template with the typed input. |
|
| 184 |
-// |
|
| 185 |
-// TODO: Go 1.5 allows to customize the error behavior, we can probably get rid of this as soon as |
|
| 186 |
-// we build Docker with that version: |
|
| 187 |
-// https://golang.org/pkg/text/template/#Template.Option |
|
| 188 |
-func (cli *DockerCli) decodeRawInspect(tmpl *template.Template, dec *json.Decoder) (*bytes.Buffer, bool) {
|
|
| 189 |
- var raw interface{}
|
|
| 190 |
- buf := bytes.NewBufferString("")
|
|
| 191 |
- |
|
| 192 |
- if rawErr := dec.Decode(&raw); rawErr != nil {
|
|
| 193 |
- fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", rawErr) |
|
| 194 |
- return buf, false |
|
| 113 |
+func (cli *DockerCli) inspectContainer(containerID string, getSize bool, elementInspector inspect.Inspector) error {
|
|
| 114 |
+ c, raw, err := cli.client.ContainerInspectWithRaw(containerID, getSize) |
|
| 115 |
+ if err != nil {
|
|
| 116 |
+ return err |
|
| 195 | 117 |
} |
| 196 | 118 |
|
| 197 |
- if rawErr := tmpl.Execute(buf, raw); rawErr != nil {
|
|
| 198 |
- return buf, false |
|
| 199 |
- } |
|
| 119 |
+ return elementInspector.Inspect(c, raw) |
|
| 120 |
+} |
|
| 200 | 121 |
|
| 201 |
- if strings.Contains(buf.String(), "<no value>") {
|
|
| 202 |
- return buf, false |
|
| 122 |
+func (cli *DockerCli) inspectImage(imageID string, getSize bool, elementInspector inspect.Inspector) error {
|
|
| 123 |
+ i, raw, err := cli.client.ImageInspectWithRaw(imageID, getSize) |
|
| 124 |
+ if err != nil {
|
|
| 125 |
+ return err |
|
| 203 | 126 |
} |
| 204 |
- return buf, true |
|
| 127 |
+ |
|
| 128 |
+ return elementInspector.Inspect(i, raw) |
|
| 205 | 129 |
} |
| 206 | 130 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,101 @@ |
| 0 |
+package inspect |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "encoding/json" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "io" |
|
| 7 |
+ "text/template" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// Inspector defines an interface to implement to process elements |
|
| 11 |
+type Inspector interface {
|
|
| 12 |
+ Inspect(typedElement interface{}, rawElement []byte) error
|
|
| 13 |
+ Flush() error |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+// TemplateInspector uses a text template to inspect elements. |
|
| 17 |
+type TemplateInspector struct {
|
|
| 18 |
+ outputStream io.Writer |
|
| 19 |
+ buffer *bytes.Buffer |
|
| 20 |
+ tmpl *template.Template |
|
| 21 |
+} |
|
| 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 |
+ |
|
| 32 |
+// Inspect executes the inspect template. |
|
| 33 |
+// It decodes the raw element into a map if the initial execution fails. |
|
| 34 |
+// This allows docker cli to parse inspect structs injected with Swarm fields. |
|
| 35 |
+func (i TemplateInspector) Inspect(typedElement interface{}, rawElement []byte) error {
|
|
| 36 |
+ buffer := new(bytes.Buffer) |
|
| 37 |
+ if err := i.tmpl.Execute(buffer, typedElement); err != nil {
|
|
| 38 |
+ var raw interface{}
|
|
| 39 |
+ rdr := bytes.NewReader(rawElement) |
|
| 40 |
+ dec := json.NewDecoder(rdr) |
|
| 41 |
+ |
|
| 42 |
+ if rawErr := dec.Decode(&raw); rawErr != nil {
|
|
| 43 |
+ return fmt.Errorf("unable to read inspect data: %v\n", rawErr)
|
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ tmplMissingKey := i.tmpl.Option("missingkey=error")
|
|
| 47 |
+ if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
|
|
| 48 |
+ return fmt.Errorf("Template parsing error: %v\n", err)
|
|
| 49 |
+ } |
|
| 50 |
+ } |
|
| 51 |
+ i.buffer.Write(buffer.Bytes()) |
|
| 52 |
+ i.buffer.WriteByte('\n')
|
|
| 53 |
+ return nil |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// Flush write the result of inspecting all elements into the output stream. |
|
| 57 |
+func (i TemplateInspector) Flush() error {
|
|
| 58 |
+ _, err := io.Copy(i.outputStream, i.buffer) |
|
| 59 |
+ return err |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+// IndentedInspector uses a buffer to stop the indented representation of an element. |
|
| 63 |
+type IndentedInspector struct {
|
|
| 64 |
+ outputStream io.Writer |
|
| 65 |
+ indented *bytes.Buffer |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+// NewIndentedInspector generates a new IndentedInspector. |
|
| 69 |
+func NewIndentedInspector(outputStream io.Writer) Inspector {
|
|
| 70 |
+ indented := new(bytes.Buffer) |
|
| 71 |
+ indented.WriteString("[\n")
|
|
| 72 |
+ return &IndentedInspector{
|
|
| 73 |
+ outputStream: outputStream, |
|
| 74 |
+ indented: indented, |
|
| 75 |
+ } |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+// Inspect writes the raw element with an indented json format. |
|
| 79 |
+func (i IndentedInspector) Inspect(_ interface{}, rawElement []byte) error {
|
|
| 80 |
+ if err := json.Indent(i.indented, rawElement, "", " "); err != nil {
|
|
| 81 |
+ return err |
|
| 82 |
+ } |
|
| 83 |
+ i.indented.WriteByte(',')
|
|
| 84 |
+ return nil |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+// Flush write the result of inspecting all elements into the output stream. |
|
| 88 |
+func (i IndentedInspector) Flush() error {
|
|
| 89 |
+ if i.indented.Len() > 1 {
|
|
| 90 |
+ // Remove trailing ',' |
|
| 91 |
+ i.indented.Truncate(i.indented.Len() - 1) |
|
| 92 |
+ } |
|
| 93 |
+ i.indented.WriteString("]\n")
|
|
| 94 |
+ |
|
| 95 |
+ // Note that we will always write "[]" when "-f" isn't specified, |
|
| 96 |
+ // to make sure the output would always be array, see |
|
| 97 |
+ // https://github.com/docker/docker/pull/9500#issuecomment-65846734 |
|
| 98 |
+ _, err := io.Copy(i.outputStream, i.indented) |
|
| 99 |
+ return err |
|
| 100 |
+} |
| ... | ... |
@@ -1,20 +1,64 @@ |
| 1 | 1 |
package lib |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "bytes" |
|
| 4 | 5 |
"encoding/json" |
| 6 |
+ "io/ioutil" |
|
| 7 |
+ "net/http" |
|
| 8 |
+ "net/url" |
|
| 5 | 9 |
|
| 6 | 10 |
"github.com/docker/docker/api/types" |
| 7 | 11 |
) |
| 8 | 12 |
|
| 9 |
-// ContainerInspect returns the all the container information. |
|
| 13 |
+// ContainerInspect returns the container information. |
|
| 10 | 14 |
func (cli *Client) ContainerInspect(containerID string) (types.ContainerJSON, error) {
|
| 11 | 15 |
serverResp, err := cli.get("/containers/"+containerID+"/json", nil, nil)
|
| 12 | 16 |
if err != nil {
|
| 17 |
+ if serverResp.statusCode == http.StatusNotFound {
|
|
| 18 |
+ return types.ContainerJSON{}, containerNotFoundError{containerID}
|
|
| 19 |
+ } |
|
| 13 | 20 |
return types.ContainerJSON{}, err
|
| 14 | 21 |
} |
| 15 | 22 |
defer ensureReaderClosed(serverResp) |
| 16 | 23 |
|
| 17 | 24 |
var response types.ContainerJSON |
| 18 |
- json.NewDecoder(serverResp.body).Decode(&response) |
|
| 25 |
+ err = json.NewDecoder(serverResp.body).Decode(&response) |
|
| 19 | 26 |
return response, err |
| 20 | 27 |
} |
| 28 |
+ |
|
| 29 |
+// ContainerInspectWithRaw returns the container information and it's raw representation. |
|
| 30 |
+func (cli *Client) ContainerInspectWithRaw(containerID string, getSize bool) (types.ContainerJSON, []byte, error) {
|
|
| 31 |
+ query := url.Values{}
|
|
| 32 |
+ if getSize {
|
|
| 33 |
+ query.Set("size", "1")
|
|
| 34 |
+ } |
|
| 35 |
+ serverResp, err := cli.get("/containers/"+containerID+"/json", query, nil)
|
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ if serverResp.statusCode == http.StatusNotFound {
|
|
| 38 |
+ return types.ContainerJSON{}, nil, containerNotFoundError{containerID}
|
|
| 39 |
+ } |
|
| 40 |
+ return types.ContainerJSON{}, nil, err
|
|
| 41 |
+ } |
|
| 42 |
+ defer ensureReaderClosed(serverResp) |
|
| 43 |
+ |
|
| 44 |
+ body, err := ioutil.ReadAll(serverResp.body) |
|
| 45 |
+ if err != nil {
|
|
| 46 |
+ return types.ContainerJSON{}, nil, err
|
|
| 47 |
+ } |
|
| 48 |
+ |
|
| 49 |
+ var response types.ContainerJSON |
|
| 50 |
+ rdr := bytes.NewReader(body) |
|
| 51 |
+ err = json.NewDecoder(rdr).Decode(&response) |
|
| 52 |
+ return response, body, err |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+func (cli *Client) containerInspectWithResponse(containerID string, query url.Values) (types.ContainerJSON, *serverResponse, error) {
|
|
| 56 |
+ serverResp, err := cli.get("/containers/"+containerID+"/json", nil, nil)
|
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ return types.ContainerJSON{}, serverResp, err
|
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ var response types.ContainerJSON |
|
| 62 |
+ err = json.NewDecoder(serverResp.body).Decode(&response) |
|
| 63 |
+ return response, serverResp, err |
|
| 64 |
+} |
| ... | ... |
@@ -25,6 +25,23 @@ func IsErrImageNotFound(err error) bool {
|
| 25 | 25 |
return ok |
| 26 | 26 |
} |
| 27 | 27 |
|
| 28 |
+// containerNotFoundError implements an error returned when a container is not in the docker host. |
|
| 29 |
+type containerNotFoundError struct {
|
|
| 30 |
+ containerID string |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+// Error returns a string representation of an containerNotFoundError |
|
| 34 |
+func (e containerNotFoundError) Error() string {
|
|
| 35 |
+ return fmt.Sprintf("Container not found: %s", e.containerID)
|
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+// IsErrContainerNotFound returns true if the error is caused |
|
| 39 |
+// when a container is not found in the docker host. |
|
| 40 |
+func IsErrContainerNotFound(err error) bool {
|
|
| 41 |
+ _, ok := err.(containerNotFoundError) |
|
| 42 |
+ return ok |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 28 | 45 |
// unauthorizedError represents an authorization error in a remote registry. |
| 29 | 46 |
type unauthorizedError struct {
|
| 30 | 47 |
cause error |
| 31 | 48 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,37 @@ |
| 0 |
+package lib |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "encoding/json" |
|
| 5 |
+ "io/ioutil" |
|
| 6 |
+ "net/http" |
|
| 7 |
+ "net/url" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/docker/docker/api/types" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+// ImageInspectWithRaw returns the image information and it's raw representation. |
|
| 13 |
+func (cli *Client) ImageInspectWithRaw(imageID string, getSize bool) (types.ImageInspect, []byte, error) {
|
|
| 14 |
+ query := url.Values{}
|
|
| 15 |
+ if getSize {
|
|
| 16 |
+ query.Set("size", "1")
|
|
| 17 |
+ } |
|
| 18 |
+ serverResp, err := cli.get("/images/"+imageID+"/json", query, nil)
|
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ if serverResp.statusCode == http.StatusNotFound {
|
|
| 21 |
+ return types.ImageInspect{}, nil, imageNotFoundError{imageID}
|
|
| 22 |
+ } |
|
| 23 |
+ return types.ImageInspect{}, nil, err
|
|
| 24 |
+ } |
|
| 25 |
+ defer ensureReaderClosed(serverResp) |
|
| 26 |
+ |
|
| 27 |
+ body, err := ioutil.ReadAll(serverResp.body) |
|
| 28 |
+ if err != nil {
|
|
| 29 |
+ return types.ImageInspect{}, nil, err
|
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ var response types.ImageInspect |
|
| 33 |
+ rdr := bytes.NewReader(body) |
|
| 34 |
+ err = json.NewDecoder(rdr).Decode(&response) |
|
| 35 |
+ return response, body, err |
|
| 36 |
+} |