Skip to content

Commit

Permalink
Inspect remote images
Browse files Browse the repository at this point in the history
Added new flag to `docker inspect` command:

    $ docker inspect --remote <imageName>[:<tag>]...

Which inspects remote images. If a short name is given, all additional
registries will be queried until a match is found. Additional `[:TAG]`
suffix may further specify desired `<image>`.

This allows for remote inspection without downloading image layers.

Signed-off-by: Michal Minar <miminar@redhat.com>
  • Loading branch information
Michal Minar committed May 18, 2015
1 parent e73a596 commit de208e7
Show file tree
Hide file tree
Showing 6 changed files with 382 additions and 18 deletions.
68 changes: 55 additions & 13 deletions api/client/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"text/template"

"github.com/docker/docker/api/types"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/registry"
)

// CmdInspect displays low-level information on one or more containers or images.
Expand All @@ -18,6 +22,7 @@ import (
func (cli *DockerCli) CmdInspect(args ...string) error {
cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container or image", true)
tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
remote := cmd.Bool([]string{"r", "-remote"}, false, "Inspect remote images")
cmd.Require(flag.Min, 1)

cmd.ParseFlags(args, true)
Expand All @@ -37,20 +42,46 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
isImage := false

for _, name := range cmd.Args() {
obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, nil))
if err != nil {
obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
var (
err error
stream io.ReadCloser
statusCode int
)
if !*remote {
stream, statusCode, err = cli.call("GET", "/containers/"+name+"/json", nil, nil)
}
if *remote || err != nil {
if *remote {
taglessRemote, _ := parsers.ParseRepositoryTag(name)
// Resolve the Repository name from fqn to RepositoryInfo
repoInfo, err := registry.ParseRepositoryInfo(taglessRemote)
if err != nil {
return err
}
v := url.Values{}
v.Set("remote", "1")
stream, statusCode, err = cli.clientRequestAttemptLogin("GET", "/images/"+name+"/json?"+v.Encode(), nil, nil, repoInfo.Index, "inspect")
} else {
stream, statusCode, err = cli.call("GET", "/images/"+name+"/json", nil, nil)
}
isImage = true
if err != nil {
if strings.Contains(err.Error(), "No such") {
fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
} else {
if err != nil || statusCode != http.StatusOK {
if (err != nil && strings.Contains(err.Error(), "No such")) || statusCode == http.StatusNotFound {
if *remote {
fmt.Fprintf(cli.err, "Error: No such image: %s\n", name)
} else {
fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
}
} else if err != nil {
fmt.Fprintf(cli.err, "%s", err)
} else {
fmt.Fprintf(cli.err, "Image lookup failed with status %d (%s)\n", statusCode, http.StatusText(statusCode))
}
status = 1
continue
}
}
obj, _, err := readBody(stream, statusCode, err)

if tmpl == nil {
if err = json.Indent(indented, obj, "", " "); err != nil {
Expand All @@ -63,13 +94,24 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
dec := json.NewDecoder(rdr)

if isImage {
inspPtr := types.ImageInspect{}
if err := dec.Decode(&inspPtr); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
status = 1
continue
if *remote {
inspPtr := types.RemoteImageInspect{}
if err := dec.Decode(&inspPtr); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
status = 1
continue
}
err = tmpl.Execute(cli.out, inspPtr)
} else {
inspPtr := types.ImageInspect{}
if err := dec.Decode(&inspPtr); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
status = 1
continue
}
err = tmpl.Execute(cli.out, inspPtr)
}
if err := tmpl.Execute(cli.out, inspPtr); err != nil {
if err != nil {
rdr.Seek(0, 0)
var raw interface{}
if err := dec.Decode(&raw); err != nil {
Expand Down
41 changes: 36 additions & 5 deletions api/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1163,12 +1163,43 @@ func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter,
return fmt.Errorf("Missing parameter")
}

imageInspect, err := s.daemon.Repositories().Lookup(vars["name"])
if err != nil {
return err
}
name := vars["name"]

if boolValue(r, "remote") {
authEncoded := r.Header.Get("X-Registry-Auth")
authConfig := &cliconfig.AuthConfig{}
if authEncoded != "" {
authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
// for a pull it is not an error if no auth was given
// to increase compatibility with the existing api it is defaulting to be empty
authConfig = &cliconfig.AuthConfig{}
}
}

return writeJSON(w, http.StatusOK, imageInspect)
image, tag := parsers.ParseRepositoryTag(name)
metaHeaders := map[string][]string{}
for k, v := range r.Header {
if strings.HasPrefix(k, "X-Meta-") {
metaHeaders[k] = v
}
}
lookupRemoteConfig := &graph.LookupRemoteConfig{
MetaHeaders: metaHeaders,
AuthConfig: authConfig,
}
imageInspect, err := s.daemon.Repositories().LookupRemote(image, tag, lookupRemoteConfig)
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, imageInspect)
} else {
imageInspect, err := s.daemon.Repositories().Lookup(name)
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, imageInspect)
}
}

func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
Expand Down
16 changes: 16 additions & 0 deletions api/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,22 @@ type ImageInspect struct {
VirtualSize int64
}

type RemoteImageInspect struct {
ImageInspect
Registry string
Digest string
Tag string
}

type LegacyImage struct {
ID string `json:"Id"`
Repository string
Tag string
Created int
Size int
VirtualSize int
}

// GET "/containers/json"
type Port struct {
IP string
Expand Down
1 change: 1 addition & 0 deletions contrib/completion/fish/docker.fish
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -a info -d 'Display syste
complete -c docker -f -n '__fish_docker_no_subcommand' -a inspect -d 'Return low-level information on a container or image'
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -s f -l format -d 'Format the output using the given go template.'
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -l help -d 'Print usage'
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -s r -l remote -d 'Inspect remote images'
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -a '(__fish_print_docker_images)' -d "Image"
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -a '(__fish_print_docker_containers all)' -d "Container"

Expand Down
8 changes: 8 additions & 0 deletions docs/man/docker-inspect.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ docker-inspect - Return low-level information on a container or image
**docker inspect**
[**--help**]
[**-f**|**--format**[=*FORMAT*]]
[**-r**|**--remote**[=*false*]]
CONTAINER|IMAGE [CONTAINER|IMAGE...]

# DESCRIPTION
Expand All @@ -24,6 +25,13 @@ each result.
**-f**, **--format**=""
Format the output using the given go template.

**-r**, **--remote**=*true*|*false*
Inspect remote images.

Image name can be suffixed with [:TAG]. If short name is given, all
additional registries will be searched until a match is found. The default is
*false*.

# EXAMPLES

## Getting information on a container
Expand Down

0 comments on commit de208e7

Please sign in to comment.