|
@@ -7,13 +7,17 @@ import (
|
|
|
"net/http"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/containerd/containerd/platforms"
|
|
|
+ "github.com/docker/distribution/reference"
|
|
|
"github.com/docker/docker/api/server/httputils"
|
|
|
"github.com/docker/docker/api/types"
|
|
|
"github.com/docker/docker/api/types/filters"
|
|
|
"github.com/docker/docker/api/types/versions"
|
|
|
"github.com/docker/docker/errdefs"
|
|
|
+ "github.com/docker/docker/image"
|
|
|
+ "github.com/docker/docker/layer"
|
|
|
"github.com/docker/docker/pkg/ioutils"
|
|
|
"github.com/docker/docker/pkg/streamformatter"
|
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
@@ -200,7 +204,12 @@ func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r
|
|
|
}
|
|
|
|
|
|
func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
- imageInspect, err := s.backend.LookupImage(vars["name"])
|
|
|
+ image, err := s.backend.GetImage(vars["name"], nil)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ imageInspect, err := s.toImageInspect(image)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
@@ -208,6 +217,85 @@ func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter
|
|
|
return httputils.WriteJSON(w, http.StatusOK, imageInspect)
|
|
|
}
|
|
|
|
|
|
+func (s *imageRouter) toImageInspect(img *image.Image) (*types.ImageInspect, error) {
|
|
|
+ refs := s.referenceBackend.References(img.ID().Digest())
|
|
|
+ repoTags := []string{}
|
|
|
+ repoDigests := []string{}
|
|
|
+ for _, ref := range refs {
|
|
|
+ switch ref.(type) {
|
|
|
+ case reference.NamedTagged:
|
|
|
+ repoTags = append(repoTags, reference.FamiliarString(ref))
|
|
|
+ case reference.Canonical:
|
|
|
+ repoDigests = append(repoDigests, reference.FamiliarString(ref))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var size int64
|
|
|
+ var layerMetadata map[string]string
|
|
|
+ layerID := img.RootFS.ChainID()
|
|
|
+ if layerID != "" {
|
|
|
+ l, err := s.layerStore.Get(layerID)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ defer layer.ReleaseAndLog(s.layerStore, l)
|
|
|
+ size = l.Size()
|
|
|
+ layerMetadata, err = l.Metadata()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ comment := img.Comment
|
|
|
+ if len(comment) == 0 && len(img.History) > 0 {
|
|
|
+ comment = img.History[len(img.History)-1].Comment
|
|
|
+ }
|
|
|
+
|
|
|
+ lastUpdated, err := s.imageStore.GetLastUpdated(img.ID())
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ return &types.ImageInspect{
|
|
|
+ ID: img.ID().String(),
|
|
|
+ RepoTags: repoTags,
|
|
|
+ RepoDigests: repoDigests,
|
|
|
+ Parent: img.Parent.String(),
|
|
|
+ Comment: comment,
|
|
|
+ Created: img.Created.Format(time.RFC3339Nano),
|
|
|
+ Container: img.Container,
|
|
|
+ ContainerConfig: &img.ContainerConfig,
|
|
|
+ DockerVersion: img.DockerVersion,
|
|
|
+ Author: img.Author,
|
|
|
+ Config: img.Config,
|
|
|
+ Architecture: img.Architecture,
|
|
|
+ Variant: img.Variant,
|
|
|
+ Os: img.OperatingSystem(),
|
|
|
+ OsVersion: img.OSVersion,
|
|
|
+ Size: size,
|
|
|
+ VirtualSize: size, // TODO: field unused, deprecate
|
|
|
+ GraphDriver: types.GraphDriverData{
|
|
|
+ Name: s.layerStore.DriverName(),
|
|
|
+ Data: layerMetadata,
|
|
|
+ },
|
|
|
+ RootFS: rootFSToAPIType(img.RootFS),
|
|
|
+ Metadata: types.ImageMetadata{
|
|
|
+ LastTagTime: lastUpdated,
|
|
|
+ },
|
|
|
+ }, nil
|
|
|
+}
|
|
|
+
|
|
|
+func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
|
|
|
+ var layers []string
|
|
|
+ for _, l := range rootfs.DiffIDs {
|
|
|
+ layers = append(layers, l.String())
|
|
|
+ }
|
|
|
+ return types.RootFS{
|
|
|
+ Type: rootfs.Type,
|
|
|
+ Layers: layers,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
if err := httputils.ParseForm(r); err != nil {
|
|
|
return err
|