diff --git a/api/server/router/image/backend.go b/api/server/router/image/backend.go index 7003dfc67e..7ec0dc59c9 100644 --- a/api/server/router/image/backend.go +++ b/api/server/router/image/backend.go @@ -22,7 +22,7 @@ type Backend interface { type imageBackend interface { ImageDelete(ctx context.Context, imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error) - ImageHistory(imageName string) ([]*image.HistoryResponseItem, error) + ImageHistory(ctx context.Context, imageName string) ([]*image.HistoryResponseItem, error) Images(ctx context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error) GetImage(ctx context.Context, refOrID string, options image.GetImageOpts) (*dockerimage.Image, error) TagImage(imageName, repository, tag string) (string, error) diff --git a/api/server/router/image/image_routes.go b/api/server/router/image/image_routes.go index 52a4b5e95c..16c9eccc7d 100644 --- a/api/server/router/image/image_routes.go +++ b/api/server/router/image/image_routes.go @@ -17,7 +17,6 @@ import ( "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" @@ -193,7 +192,7 @@ func (ir *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, } func (ir *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - img, err := ir.backend.GetImage(ctx, vars["name"], opts.GetImageOpts{}) + img, err := ir.backend.GetImage(ctx, vars["name"], opts.GetImageOpts{Details: true}) if err != nil { return err } @@ -218,31 +217,11 @@ func (ir *imageRouter) toImageInspect(img *image.Image) (*types.ImageInspect, er } } - var size int64 - var layerMetadata map[string]string - if layerID := img.RootFS.ChainID(); layerID != "" { - l, err := ir.layerStore.Get(layerID) - if err != nil { - return nil, err - } - defer layer.ReleaseAndLog(ir.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 := ir.imageStore.GetLastUpdated(img.ID()) - if err != nil { - return nil, err - } - return &types.ImageInspect{ ID: img.ID().String(), RepoTags: repoTags, @@ -259,15 +238,15 @@ func (ir *imageRouter) toImageInspect(img *image.Image) (*types.ImageInspect, er Variant: img.Variant, Os: img.OperatingSystem(), OsVersion: img.OSVersion, - Size: size, - VirtualSize: size, // TODO: field unused, deprecate + Size: img.Details.Size, + VirtualSize: img.Details.Size, // TODO: field unused, deprecate GraphDriver: types.GraphDriverData{ - Name: ir.layerStore.DriverName(), - Data: layerMetadata, + Name: img.Details.Driver, + Data: img.Details.Metadata, }, RootFS: rootFSToAPIType(img.RootFS), Metadata: types.ImageMetadata{ - LastTagTime: lastUpdated, + LastTagTime: img.Details.LastUpdated, }, }, nil } @@ -321,7 +300,7 @@ func (ir *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, } func (ir *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - history, err := ir.backend.ImageHistory(vars["name"]) + history, err := ir.backend.ImageHistory(ctx, vars["name"]) if err != nil { return err } diff --git a/api/types/image/opts.go b/api/types/image/opts.go index 2a99696228..a24f9059ab 100644 --- a/api/types/image/opts.go +++ b/api/types/image/opts.go @@ -5,4 +5,5 @@ import specs "github.com/opencontainers/image-spec/specs-go/v1" // GetImageOpts holds parameters to inspect an image. type GetImageOpts struct { Platform *specs.Platform + Details bool } diff --git a/daemon/containerd/image_history.go b/daemon/containerd/image_history.go index d30c81f4e9..0dc23a413a 100644 --- a/daemon/containerd/image_history.go +++ b/daemon/containerd/image_history.go @@ -1,6 +1,7 @@ package containerd import ( + "context" "errors" imagetype "github.com/docker/docker/api/types/image" @@ -9,6 +10,6 @@ import ( // ImageHistory returns a slice of ImageHistory structures for the specified // image name by walking the image lineage. -func (i *ImageService) ImageHistory(name string) ([]*imagetype.HistoryResponseItem, error) { +func (i *ImageService) ImageHistory(ctx context.Context, name string) ([]*imagetype.HistoryResponseItem, error) { return nil, errdefs.NotImplemented(errors.New("not implemented")) } diff --git a/daemon/image_service.go b/daemon/image_service.go index 3af584bdba..c29da5f20c 100644 --- a/daemon/image_service.go +++ b/daemon/image_service.go @@ -40,7 +40,7 @@ type ImageService interface { TagImage(imageName, repository, tag string) (string, error) TagImageWithReference(imageID image.ID, newTag reference.Named) error GetImage(ctx context.Context, refOrID string, options imagetype.GetImageOpts) (*image.Image, error) - ImageHistory(name string) ([]*imagetype.HistoryResponseItem, error) + ImageHistory(ctx context.Context, name string) ([]*imagetype.HistoryResponseItem, error) CommitImage(c backend.CommitConfig) (image.ID, error) SquashImage(id, parent string) (string, error) diff --git a/daemon/images/image.go b/daemon/images/image.go index 12f3d53b10..a7b56e7941 100644 --- a/daemon/images/image.go +++ b/daemon/images/image.go @@ -15,6 +15,7 @@ import ( imagetypes "github.com/docker/docker/api/types/image" "github.com/docker/docker/errdefs" "github.com/docker/docker/image" + "github.com/docker/docker/layer" "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -148,7 +149,43 @@ func (i *ImageService) manifestMatchesPlatform(ctx context.Context, img *image.I } // GetImage returns an image corresponding to the image referred to by refOrID. -func (i *ImageService) GetImage(ctx context.Context, refOrID string, options imagetypes.GetImageOpts) (retImg *image.Image, retErr error) { +func (i *ImageService) GetImage(ctx context.Context, refOrID string, options imagetypes.GetImageOpts) (*image.Image, error) { + img, err := i.getImage(ctx, refOrID, options) + if err != nil { + return nil, err + } + if options.Details { + var size int64 + var layerMetadata map[string]string + layerID := img.RootFS.ChainID() + if layerID != "" { + l, err := i.layerStore.Get(layerID) + if err != nil { + return nil, err + } + defer layer.ReleaseAndLog(i.layerStore, l) + size = l.Size() + layerMetadata, err = l.Metadata() + if err != nil { + return nil, err + } + } + + lastUpdated, err := i.imageStore.GetLastUpdated(img.ID()) + if err != nil { + return nil, err + } + img.Details = &image.Details{ + Size: size, + Metadata: layerMetadata, + Driver: i.layerStore.DriverName(), + LastUpdated: lastUpdated, + } + } + return img, nil +} + +func (i *ImageService) getImage(ctx context.Context, refOrID string, options imagetypes.GetImageOpts) (retImg *image.Image, retErr error) { defer func() { if retErr != nil || retImg == nil || options.Platform == nil { return diff --git a/daemon/images/image_history.go b/daemon/images/image_history.go index d32c873925..8a9f03d950 100644 --- a/daemon/images/image_history.go +++ b/daemon/images/image_history.go @@ -12,8 +12,7 @@ import ( // ImageHistory returns a slice of ImageHistory structures for the specified image // name by walking the image lineage. -func (i *ImageService) ImageHistory(name string) ([]*image.HistoryResponseItem, error) { - ctx := context.TODO() +func (i *ImageService) ImageHistory(ctx context.Context, name string) ([]*image.HistoryResponseItem, error) { start := time.Now() img, err := i.GetImage(ctx, name, image.GetImageOpts{}) if err != nil { diff --git a/image/image.go b/image/image.go index 99e8af0cc7..25179a1c8a 100644 --- a/image/image.go +++ b/image/image.go @@ -112,6 +112,17 @@ type Image struct { // computedID is the ID computed from the hash of the image config. // Not to be confused with the legacy V1 ID in V1Image. computedID ID + + // Details holds additional details about image + Details *Details `json:"-"` +} + +// Details provides additional image data +type Details struct { + Size int64 + Metadata map[string]string + Driver string + LastUpdated time.Time } // RawJSON returns the immutable JSON associated with the image.