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
|
||||
ContainerExport(containerID string) (io.ReadCloser, error)
|
||||
ContainerInspect(containerID string) (types.ContainerJSON, error)
|
||||
ContainerInspectWithRaw(containerID string, getSize bool) (types.ContainerJSON, []byte, error)
|
||||
ContainerKill(containerID, signal string) error
|
||||
ContainerList(options types.ContainerListOptions) ([]types.Container, error)
|
||||
ContainerLogs(options types.ContainerLogsOptions) (io.ReadCloser, error)
|
||||
|
@ -47,6 +48,7 @@ type apiClient interface {
|
|||
ImageCreate(options types.ImageCreateOptions) (io.ReadCloser, error)
|
||||
ImageHistory(imageID string) ([]types.ImageHistory, error)
|
||||
ImageImport(options types.ImageImportOptions) (io.ReadCloser, error)
|
||||
ImageInspectWithRaw(imageID string, getSize bool) (types.ImageInspect, []byte, error)
|
||||
ImageList(options types.ImageListOptions) ([]types.Image, error)
|
||||
ImageLoad(input io.Reader) (io.ReadCloser, error)
|
||||
ImagePull(options types.ImagePullOptions, privilegeFunc lib.RequestPrivilegeFunc) (io.ReadCloser, error)
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"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"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
|
@ -34,10 +30,15 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
|||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var tmpl *template.Template
|
||||
var err error
|
||||
var obj []byte
|
||||
var statusCode int
|
||||
if *inspectType != "" && *inspectType != "container" && *inspectType != "image" {
|
||||
return fmt.Errorf("%q is not a valid value for --type", *inspectType)
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
tmpl *template.Template
|
||||
elementInspector inspect.Inspector
|
||||
)
|
||||
|
||||
if *tmplStr != "" {
|
||||
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" {
|
||||
return fmt.Errorf("%q is not a valid value for --type", *inspectType)
|
||||
if tmpl != nil {
|
||||
elementInspector = inspect.NewTemplateInspector(cli.out, tmpl)
|
||||
} else {
|
||||
elementInspector = inspect.NewIndentedInspector(cli.out)
|
||||
}
|
||||
|
||||
indented := new(bytes.Buffer)
|
||||
indented.WriteString("[\n")
|
||||
status := 0
|
||||
isImage := false
|
||||
|
||||
v := url.Values{}
|
||||
if *size {
|
||||
v.Set("size", "1")
|
||||
switch *inspectType {
|
||||
case "container":
|
||||
err = cli.inspectContainers(cmd.Args(), *size, elementInspector)
|
||||
case "images":
|
||||
err = cli.inspectImages(cmd.Args(), *size, elementInspector)
|
||||
default:
|
||||
err = cli.inspectAll(cmd.Args(), *size, elementInspector)
|
||||
}
|
||||
|
||||
for _, name := range cmd.Args() {
|
||||
if *inspectType == "" || *inspectType == "container" {
|
||||
obj, statusCode, err = readBody(cli.call("GET", "/containers/"+name+"/json?"+v.Encode(), nil, nil))
|
||||
if err != nil {
|
||||
if err == errConnectionFailed {
|
||||
if err := elementInspector.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if *inspectType == "container" {
|
||||
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") {
|
||||
obj, statusCode, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
|
||||
isImage = true
|
||||
if err != nil {
|
||||
if err == errConnectionFailed {
|
||||
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
|
||||
func (cli *DockerCli) inspectContainers(containerIDs []string, getSize bool, elementInspector inspect.Inspector) error {
|
||||
for _, containerID := range containerIDs {
|
||||
if err := cli.inspectContainer(containerID, getSize, elementInspector); err != nil {
|
||||
if lib.IsErrContainerNotFound(err) {
|
||||
return fmt.Errorf("Error: No such container: %s\n", containerID)
|
||||
}
|
||||
} 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
|
||||
}
|
||||
|
||||
// decodeRawInspect executes the inspect template with a raw interface.
|
||||
// This allows docker cli to parse inspect structs injected with Swarm fields.
|
||||
// Unfortunately, go 1.4 doesn't fail executing invalid templates when the input is an interface.
|
||||
// It doesn't allow to modify this behavior either, sending <no value> messages to the output.
|
||||
// We assume that the template is invalid when there is a <no value>, if the template was valid
|
||||
// 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
|
||||
func (cli *DockerCli) inspectImages(imageIDs []string, getSize bool, elementInspector inspect.Inspector) error {
|
||||
for _, imageID := range imageIDs {
|
||||
if err := cli.inspectImage(imageID, getSize, elementInspector); err != nil {
|
||||
if lib.IsErrImageNotFound(err) {
|
||||
return fmt.Errorf("Error: No such image: %s\n", imageID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if rawErr := tmpl.Execute(buf, raw); rawErr != nil {
|
||||
return buf, false
|
||||
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
|
||||
}
|
||||
|
||||
if strings.Contains(buf.String(), "<no value>") {
|
||||
return buf, false
|
||||
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 buf, true
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"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) {
|
||||
serverResp, err := cli.get("/containers/"+containerID+"/json", nil, nil)
|
||||
if err != nil {
|
||||
if serverResp.statusCode == http.StatusNotFound {
|
||||
return types.ContainerJSON{}, containerNotFoundError{containerID}
|
||||
}
|
||||
return types.ContainerJSON{}, err
|
||||
}
|
||||
defer ensureReaderClosed(serverResp)
|
||||
|
||||
var response types.ContainerJSON
|
||||
json.NewDecoder(serverResp.body).Decode(&response)
|
||||
err = json.NewDecoder(serverResp.body).Decode(&response)
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
type unauthorizedError struct {
|
||||
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