| package images | |
| import ( | |
| "context" | |
| "time" | |
| "github.com/distribution/reference" | |
| imagetypes "github.com/moby/moby/api/types/image" | |
| "github.com/moby/moby/api/types/storage" | |
| "github.com/moby/moby/v2/daemon/internal/image" | |
| "github.com/moby/moby/v2/daemon/internal/layer" | |
| "github.com/moby/moby/v2/daemon/server/imagebackend" | |
| ) | |
| func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, opts imagebackend.ImageInspectOpts) (*imagebackend.InspectData, error) { | |
| img, err := i.GetImage(ctx, refOrID, imagebackend.GetImageOpts{Platform: opts.Platform}) | |
| if err != nil { | |
| return nil, err | |
| } | |
| size, layerMetadata, err := i.getLayerSizeAndMetadata(img) | |
| if err != nil { | |
| return nil, err | |
| } | |
| lastUpdated, err := i.imageStore.GetLastUpdated(img.ID()) | |
| if err != nil { | |
| return nil, err | |
| } | |
| var repoTags, repoDigests []string | |
| for _, ref := range i.referenceStore.References(img.ID().Digest()) { | |
| switch ref.(type) { | |
| case reference.NamedTagged: | |
| repoTags = append(repoTags, reference.FamiliarString(ref)) | |
| case reference.Canonical: | |
| repoDigests = append(repoDigests, reference.FamiliarString(ref)) | |
| } | |
| } | |
| comment := img.Comment | |
| if comment == "" && len(img.History) > 0 { | |
| comment = img.History[len(img.History)-1].Comment | |
| } | |
| var created string | |
| if img.Created != nil { | |
| created = img.Created.Format(time.RFC3339Nano) | |
| } | |
| var layers []string | |
| for _, l := range img.RootFS.DiffIDs { | |
| layers = append(layers, l.String()) | |
| } | |
| imgConfig := containerConfigToDockerOCIImageConfig(img.Config) | |
| return &imagebackend.InspectData{ | |
| InspectResponse: imagetypes.InspectResponse{ | |
| ID: img.ID().String(), | |
| RepoTags: repoTags, | |
| RepoDigests: repoDigests, | |
| Comment: comment, | |
| Created: created, | |
| Author: img.Author, | |
| Config: &imgConfig, | |
| Architecture: img.Architecture, | |
| Variant: img.Variant, | |
| Os: img.OperatingSystem(), | |
| OsVersion: img.OSVersion, | |
| Size: size, | |
| GraphDriver: &storage.DriverData{ | |
| Name: i.layerStore.DriverName(), | |
| Data: layerMetadata, | |
| }, | |
| RootFS: imagetypes.RootFS{ | |
| Type: img.RootFS.Type, | |
| Layers: layers, | |
| }, | |
| Metadata: imagetypes.Metadata{ | |
| LastTagTime: lastUpdated, | |
| }, | |
| }, | |
| Parent: img.Parent.String(), // field is deprecated with the legacy builder, but still included in response when present (built with legacy builder). | |
| DockerVersion: img.DockerVersion, // field is deprecated with the legacy builder, but still included in response when present. | |
| Container: img.Container, // field is deprecated, but still set on API < v1.45. | |
| ContainerConfig: &img.ContainerConfig, // field is deprecated, but still set on API < v1.45. | |
| }, nil | |
| } | |
| func (i *ImageService) getLayerSizeAndMetadata(img *image.Image) (int64, map[string]string, error) { | |
| var size int64 | |
| var layerMetadata map[string]string | |
| layerID := img.RootFS.ChainID() | |
| if layerID != "" { | |
| l, err := i.layerStore.Get(layerID) | |
| if err != nil { | |
| return 0, nil, err | |
| } | |
| defer layer.ReleaseAndLog(i.layerStore, l) | |
| size = l.Size() | |
| layerMetadata, err = l.Metadata() | |
| if err != nil { | |
| return 0, nil, err | |
| } | |
| } | |
| return size, layerMetadata, nil | |
| } |