mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Implement docker inspect with standalone client lib.
Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
parent
42670e30ee
commit
cf3efd05d4
6 changed files with 281 additions and 155 deletions
|
@ -26,6 +26,7 @@ type apiClient interface {
|
||||||
ContainerExecStart(execID string, config types.ExecStartCheck) error
|
ContainerExecStart(execID string, config types.ExecStartCheck) error
|
||||||
ContainerExport(containerID string) (io.ReadCloser, error)
|
ContainerExport(containerID string) (io.ReadCloser, error)
|
||||||
ContainerInspect(containerID string) (types.ContainerJSON, error)
|
ContainerInspect(containerID string) (types.ContainerJSON, error)
|
||||||
|
ContainerInspectWithRaw(containerID string, getSize bool) (types.ContainerJSON, []byte, error)
|
||||||
ContainerKill(containerID, signal string) error
|
ContainerKill(containerID, signal string) error
|
||||||
ContainerList(options types.ContainerListOptions) ([]types.Container, error)
|
ContainerList(options types.ContainerListOptions) ([]types.Container, error)
|
||||||
ContainerLogs(options types.ContainerLogsOptions) (io.ReadCloser, error)
|
ContainerLogs(options types.ContainerLogsOptions) (io.ReadCloser, error)
|
||||||
|
@ -47,6 +48,7 @@ type apiClient interface {
|
||||||
ImageCreate(options types.ImageCreateOptions) (io.ReadCloser, error)
|
ImageCreate(options types.ImageCreateOptions) (io.ReadCloser, error)
|
||||||
ImageHistory(imageID string) ([]types.ImageHistory, error)
|
ImageHistory(imageID string) ([]types.ImageHistory, error)
|
||||||
ImageImport(options types.ImageImportOptions) (io.ReadCloser, error)
|
ImageImport(options types.ImageImportOptions) (io.ReadCloser, error)
|
||||||
|
ImageInspectWithRaw(imageID string, getSize bool) (types.ImageInspect, []byte, error)
|
||||||
ImageList(options types.ImageListOptions) ([]types.Image, error)
|
ImageList(options types.ImageListOptions) ([]types.Image, error)
|
||||||
ImageLoad(input io.Reader) (io.ReadCloser, error)
|
ImageLoad(input io.Reader) (io.ReadCloser, error)
|
||||||
ImagePull(options types.ImagePullOptions, privilegeFunc lib.RequestPrivilegeFunc) (io.ReadCloser, error)
|
ImagePull(options types.ImagePullOptions, privilegeFunc lib.RequestPrivilegeFunc) (io.ReadCloser, error)
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/client/inspect"
|
||||||
|
"github.com/docker/docker/api/client/lib"
|
||||||
Cli "github.com/docker/docker/cli"
|
Cli "github.com/docker/docker/cli"
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
)
|
)
|
||||||
|
@ -34,10 +30,15 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||||
|
|
||||||
cmd.ParseFlags(args, true)
|
cmd.ParseFlags(args, true)
|
||||||
|
|
||||||
var tmpl *template.Template
|
if *inspectType != "" && *inspectType != "container" && *inspectType != "image" {
|
||||||
var err error
|
return fmt.Errorf("%q is not a valid value for --type", *inspectType)
|
||||||
var obj []byte
|
}
|
||||||
var statusCode int
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
tmpl *template.Template
|
||||||
|
elementInspector inspect.Inspector
|
||||||
|
)
|
||||||
|
|
||||||
if *tmplStr != "" {
|
if *tmplStr != "" {
|
||||||
if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
|
if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
|
||||||
|
@ -46,160 +47,84 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *inspectType != "" && *inspectType != "container" && *inspectType != "image" {
|
if tmpl != nil {
|
||||||
return fmt.Errorf("%q is not a valid value for --type", *inspectType)
|
elementInspector = inspect.NewTemplateInspector(cli.out, tmpl)
|
||||||
|
} else {
|
||||||
|
elementInspector = inspect.NewIndentedInspector(cli.out)
|
||||||
}
|
}
|
||||||
|
|
||||||
indented := new(bytes.Buffer)
|
switch *inspectType {
|
||||||
indented.WriteString("[\n")
|
case "container":
|
||||||
status := 0
|
err = cli.inspectContainers(cmd.Args(), *size, elementInspector)
|
||||||
isImage := false
|
case "images":
|
||||||
|
err = cli.inspectImages(cmd.Args(), *size, elementInspector)
|
||||||
v := url.Values{}
|
default:
|
||||||
if *size {
|
err = cli.inspectAll(cmd.Args(), *size, elementInspector)
|
||||||
v.Set("size", "1")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range cmd.Args() {
|
if err := elementInspector.Flush(); err != nil {
|
||||||
if *inspectType == "" || *inspectType == "container" {
|
|
||||||
obj, statusCode, err = readBody(cli.call("GET", "/containers/"+name+"/json?"+v.Encode(), nil, nil))
|
|
||||||
if err != nil {
|
|
||||||
if err == errConnectionFailed {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if *inspectType == "container" {
|
return err
|
||||||
if statusCode == http.StatusNotFound {
|
}
|
||||||
fmt.Fprintf(cli.err, "Error: No such container: %s\n", name)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(cli.err, "%s", err)
|
|
||||||
}
|
|
||||||
status = 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj == nil && (*inspectType == "" || *inspectType == "image") {
|
func (cli *DockerCli) inspectContainers(containerIDs []string, getSize bool, elementInspector inspect.Inspector) error {
|
||||||
obj, statusCode, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
|
for _, containerID := range containerIDs {
|
||||||
isImage = true
|
if err := cli.inspectContainer(containerID, getSize, elementInspector); err != nil {
|
||||||
if err != nil {
|
if lib.IsErrContainerNotFound(err) {
|
||||||
if err == errConnectionFailed {
|
return fmt.Errorf("Error: No such container: %s\n", containerID)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if statusCode == http.StatusNotFound {
|
|
||||||
if *inspectType == "" {
|
|
||||||
fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(cli.err, "Error: No such image: %s\n", name)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(cli.err, "%s", err)
|
|
||||||
}
|
|
||||||
status = 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if tmpl == nil {
|
|
||||||
if err := json.Indent(indented, obj, "", " "); err != nil {
|
|
||||||
fmt.Fprintf(cli.err, "%s\n", err)
|
|
||||||
status = 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rdr := bytes.NewReader(obj)
|
|
||||||
dec := json.NewDecoder(rdr)
|
|
||||||
buf := bytes.NewBufferString("")
|
|
||||||
|
|
||||||
if isImage {
|
|
||||||
inspPtr := types.ImageInspect{}
|
|
||||||
if err := dec.Decode(&inspPtr); err != nil {
|
|
||||||
fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err)
|
|
||||||
status = 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err := tmpl.Execute(buf, inspPtr); err != nil {
|
|
||||||
rdr.Seek(0, 0)
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
|
|
||||||
fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
|
|
||||||
status = 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inspPtr := types.ContainerJSON{}
|
|
||||||
if err := dec.Decode(&inspPtr); err != nil {
|
|
||||||
fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err)
|
|
||||||
status = 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err := tmpl.Execute(buf, inspPtr); err != nil {
|
|
||||||
rdr.Seek(0, 0)
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
|
|
||||||
fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
|
|
||||||
status = 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cli.out.Write(buf.Bytes())
|
|
||||||
cli.out.Write([]byte{'\n'})
|
|
||||||
}
|
|
||||||
indented.WriteString(",")
|
|
||||||
}
|
|
||||||
|
|
||||||
if indented.Len() > 1 {
|
|
||||||
// Remove trailing ','
|
|
||||||
indented.Truncate(indented.Len() - 1)
|
|
||||||
}
|
|
||||||
indented.WriteString("]\n")
|
|
||||||
|
|
||||||
if tmpl == nil {
|
|
||||||
// Note that we will always write "[]" when "-f" isn't specified,
|
|
||||||
// to make sure the output would always be array, see
|
|
||||||
// https://github.com/docker/docker/pull/9500#issuecomment-65846734
|
|
||||||
if _, err := io.Copy(cli.out, indented); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if status != 0 {
|
|
||||||
return Cli.StatusError{StatusCode: status}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeRawInspect executes the inspect template with a raw interface.
|
func (cli *DockerCli) inspectImages(imageIDs []string, getSize bool, elementInspector inspect.Inspector) error {
|
||||||
// This allows docker cli to parse inspect structs injected with Swarm fields.
|
for _, imageID := range imageIDs {
|
||||||
// Unfortunately, go 1.4 doesn't fail executing invalid templates when the input is an interface.
|
if err := cli.inspectImage(imageID, getSize, elementInspector); err != nil {
|
||||||
// It doesn't allow to modify this behavior either, sending <no value> messages to the output.
|
if lib.IsErrImageNotFound(err) {
|
||||||
// We assume that the template is invalid when there is a <no value>, if the template was valid
|
return fmt.Errorf("Error: No such image: %s\n", imageID)
|
||||||
// we'd get <nil> or "" values. In that case we fail with the original error raised executing the
|
|
||||||
// template with the typed input.
|
|
||||||
//
|
|
||||||
// TODO: Go 1.5 allows to customize the error behavior, we can probably get rid of this as soon as
|
|
||||||
// we build Docker with that version:
|
|
||||||
// https://golang.org/pkg/text/template/#Template.Option
|
|
||||||
func (cli *DockerCli) decodeRawInspect(tmpl *template.Template, dec *json.Decoder) (*bytes.Buffer, bool) {
|
|
||||||
var raw interface{}
|
|
||||||
buf := bytes.NewBufferString("")
|
|
||||||
|
|
||||||
if rawErr := dec.Decode(&raw); rawErr != nil {
|
|
||||||
fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", rawErr)
|
|
||||||
return buf, false
|
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
if rawErr := tmpl.Execute(buf, raw); rawErr != nil {
|
|
||||||
return buf, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(buf.String(), "<no value>") {
|
|
||||||
return buf, false
|
|
||||||
}
|
}
|
||||||
return buf, true
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *DockerCli) inspectAll(ids []string, getSize bool, elementInspector inspect.Inspector) error {
|
||||||
|
for _, id := range ids {
|
||||||
|
if err := cli.inspectContainer(id, getSize, elementInspector); err != nil {
|
||||||
|
// Search for image with that id if a container doesn't exist.
|
||||||
|
if lib.IsErrContainerNotFound(err) {
|
||||||
|
if err := cli.inspectImage(id, getSize, elementInspector); err != nil {
|
||||||
|
if lib.IsErrImageNotFound(err) {
|
||||||
|
return fmt.Errorf("Error: No such image or container: %s", id)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *DockerCli) inspectContainer(containerID string, getSize bool, elementInspector inspect.Inspector) error {
|
||||||
|
c, raw, err := cli.client.ContainerInspectWithRaw(containerID, getSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return elementInspector.Inspect(c, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *DockerCli) inspectImage(imageID string, getSize bool, elementInspector inspect.Inspector) error {
|
||||||
|
i, raw, err := cli.client.ImageInspectWithRaw(imageID, getSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return elementInspector.Inspect(i, raw)
|
||||||
}
|
}
|
||||||
|
|
101
api/client/inspect/inspector.go
Normal file
101
api/client/inspect/inspector.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package inspect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Inspector defines an interface to implement to process elements
|
||||||
|
type Inspector interface {
|
||||||
|
Inspect(typedElement interface{}, rawElement []byte) error
|
||||||
|
Flush() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TemplateInspector uses a text template to inspect elements.
|
||||||
|
type TemplateInspector struct {
|
||||||
|
outputStream io.Writer
|
||||||
|
buffer *bytes.Buffer
|
||||||
|
tmpl *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTemplateInspector creates a new inspector with a template.
|
||||||
|
func NewTemplateInspector(outputStream io.Writer, tmpl *template.Template) Inspector {
|
||||||
|
return &TemplateInspector{
|
||||||
|
outputStream: outputStream,
|
||||||
|
buffer: new(bytes.Buffer),
|
||||||
|
tmpl: tmpl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspect executes the inspect template.
|
||||||
|
// It decodes the raw element into a map if the initial execution fails.
|
||||||
|
// This allows docker cli to parse inspect structs injected with Swarm fields.
|
||||||
|
func (i TemplateInspector) Inspect(typedElement interface{}, rawElement []byte) error {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
if err := i.tmpl.Execute(buffer, typedElement); err != nil {
|
||||||
|
var raw interface{}
|
||||||
|
rdr := bytes.NewReader(rawElement)
|
||||||
|
dec := json.NewDecoder(rdr)
|
||||||
|
|
||||||
|
if rawErr := dec.Decode(&raw); rawErr != nil {
|
||||||
|
return fmt.Errorf("unable to read inspect data: %v\n", rawErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmplMissingKey := i.tmpl.Option("missingkey=error")
|
||||||
|
if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
|
||||||
|
return fmt.Errorf("Template parsing error: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.buffer.Write(buffer.Bytes())
|
||||||
|
i.buffer.WriteByte('\n')
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush write the result of inspecting all elements into the output stream.
|
||||||
|
func (i TemplateInspector) Flush() error {
|
||||||
|
_, err := io.Copy(i.outputStream, i.buffer)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndentedInspector uses a buffer to stop the indented representation of an element.
|
||||||
|
type IndentedInspector struct {
|
||||||
|
outputStream io.Writer
|
||||||
|
indented *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIndentedInspector generates a new IndentedInspector.
|
||||||
|
func NewIndentedInspector(outputStream io.Writer) Inspector {
|
||||||
|
indented := new(bytes.Buffer)
|
||||||
|
indented.WriteString("[\n")
|
||||||
|
return &IndentedInspector{
|
||||||
|
outputStream: outputStream,
|
||||||
|
indented: indented,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspect writes the raw element with an indented json format.
|
||||||
|
func (i IndentedInspector) Inspect(_ interface{}, rawElement []byte) error {
|
||||||
|
if err := json.Indent(i.indented, rawElement, "", " "); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.indented.WriteByte(',')
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush write the result of inspecting all elements into the output stream.
|
||||||
|
func (i IndentedInspector) Flush() error {
|
||||||
|
if i.indented.Len() > 1 {
|
||||||
|
// Remove trailing ','
|
||||||
|
i.indented.Truncate(i.indented.Len() - 1)
|
||||||
|
}
|
||||||
|
i.indented.WriteString("]\n")
|
||||||
|
|
||||||
|
// Note that we will always write "[]" when "-f" isn't specified,
|
||||||
|
// to make sure the output would always be array, see
|
||||||
|
// https://github.com/docker/docker/pull/9500#issuecomment-65846734
|
||||||
|
_, err := io.Copy(i.outputStream, i.indented)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -1,20 +1,64 @@
|
||||||
package lib
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerInspect returns the all the container information.
|
// ContainerInspect returns the container information.
|
||||||
func (cli *Client) ContainerInspect(containerID string) (types.ContainerJSON, error) {
|
func (cli *Client) ContainerInspect(containerID string) (types.ContainerJSON, error) {
|
||||||
serverResp, err := cli.get("/containers/"+containerID+"/json", nil, nil)
|
serverResp, err := cli.get("/containers/"+containerID+"/json", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if serverResp.statusCode == http.StatusNotFound {
|
||||||
|
return types.ContainerJSON{}, containerNotFoundError{containerID}
|
||||||
|
}
|
||||||
return types.ContainerJSON{}, err
|
return types.ContainerJSON{}, err
|
||||||
}
|
}
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
|
|
||||||
var response types.ContainerJSON
|
var response types.ContainerJSON
|
||||||
json.NewDecoder(serverResp.body).Decode(&response)
|
err = json.NewDecoder(serverResp.body).Decode(&response)
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerInspectWithRaw returns the container information and it's raw representation.
|
||||||
|
func (cli *Client) ContainerInspectWithRaw(containerID string, getSize bool) (types.ContainerJSON, []byte, error) {
|
||||||
|
query := url.Values{}
|
||||||
|
if getSize {
|
||||||
|
query.Set("size", "1")
|
||||||
|
}
|
||||||
|
serverResp, err := cli.get("/containers/"+containerID+"/json", query, nil)
|
||||||
|
if err != nil {
|
||||||
|
if serverResp.statusCode == http.StatusNotFound {
|
||||||
|
return types.ContainerJSON{}, nil, containerNotFoundError{containerID}
|
||||||
|
}
|
||||||
|
return types.ContainerJSON{}, nil, err
|
||||||
|
}
|
||||||
|
defer ensureReaderClosed(serverResp)
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(serverResp.body)
|
||||||
|
if err != nil {
|
||||||
|
return types.ContainerJSON{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response types.ContainerJSON
|
||||||
|
rdr := bytes.NewReader(body)
|
||||||
|
err = json.NewDecoder(rdr).Decode(&response)
|
||||||
|
return response, body, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *Client) containerInspectWithResponse(containerID string, query url.Values) (types.ContainerJSON, *serverResponse, error) {
|
||||||
|
serverResp, err := cli.get("/containers/"+containerID+"/json", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return types.ContainerJSON{}, serverResp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response types.ContainerJSON
|
||||||
|
err = json.NewDecoder(serverResp.body).Decode(&response)
|
||||||
|
return response, serverResp, err
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,23 @@ func IsErrImageNotFound(err error) bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// containerNotFoundError implements an error returned when a container is not in the docker host.
|
||||||
|
type containerNotFoundError struct {
|
||||||
|
containerID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a string representation of an containerNotFoundError
|
||||||
|
func (e containerNotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("Container not found: %s", e.containerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrContainerNotFound returns true if the error is caused
|
||||||
|
// when a container is not found in the docker host.
|
||||||
|
func IsErrContainerNotFound(err error) bool {
|
||||||
|
_, ok := err.(containerNotFoundError)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// unauthorizedError represents an authorization error in a remote registry.
|
// unauthorizedError represents an authorization error in a remote registry.
|
||||||
type unauthorizedError struct {
|
type unauthorizedError struct {
|
||||||
cause error
|
cause error
|
||||||
|
|
37
api/client/lib/image_inspect.go
Normal file
37
api/client/lib/image_inspect.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImageInspectWithRaw returns the image information and it's raw representation.
|
||||||
|
func (cli *Client) ImageInspectWithRaw(imageID string, getSize bool) (types.ImageInspect, []byte, error) {
|
||||||
|
query := url.Values{}
|
||||||
|
if getSize {
|
||||||
|
query.Set("size", "1")
|
||||||
|
}
|
||||||
|
serverResp, err := cli.get("/images/"+imageID+"/json", query, nil)
|
||||||
|
if err != nil {
|
||||||
|
if serverResp.statusCode == http.StatusNotFound {
|
||||||
|
return types.ImageInspect{}, nil, imageNotFoundError{imageID}
|
||||||
|
}
|
||||||
|
return types.ImageInspect{}, nil, err
|
||||||
|
}
|
||||||
|
defer ensureReaderClosed(serverResp)
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(serverResp.body)
|
||||||
|
if err != nil {
|
||||||
|
return types.ImageInspect{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response types.ImageInspect
|
||||||
|
rdr := bytes.NewReader(body)
|
||||||
|
err = json.NewDecoder(rdr).Decode(&response)
|
||||||
|
return response, body, err
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue