api/types/image: InspectResponse: remove deprecated Parent, DockerVersion

The InspectResponse type contains various fields that are deprecated
and removed from current API versions, but that were kept for the API
server to produce the fields when downgrading to older API versions.

- The `Parent` field is only used for the legacy builder, and only set for
  images that are built locally (i.e., not persisted when pulling an image).
- The `DockerVersion` field is only set when building images with the legacy
  builder, and empty in most cases.

Both fields were implicitly deprecated with the deprecation of the legacy
builder, and deprecated for the API in [moby@bd8a99b], which was backported
to the 28.x release.

This patch:

- Removes the deprecated fields from the `InspectResposne` struct; this
  means that [`client.ImageInspect`] won't unmarshal those fields, but
  the [`docker image inspect`] CLI command defaults to printing the raw
  output as returned by the API, so can continue to show any field returned
  in the API response. As a side-note; we should change the CLI to default
  to show the unmarshalled response, and introduce a `--format=jsonraw`
  (or `--raw`) option to make printing the raw response opt-in.
- Updates the API server to backfill the fields if they are set.

[moby@bd8a99b]: bd8a99b400
[`client.ImageInspect`]: f739c61c69/client/image_inspect.go (L14-L64)
[`docker image inspect`]: 74e3520724/cli/command/image/inspect.go (L59-L81)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-10-05 17:07:04 +02:00
parent 1a81903854
commit f8d3c4e4a7
7 changed files with 56 additions and 51 deletions

View File

@@ -42,15 +42,6 @@ type InspectResponse struct {
// the manifest is generated and its digest calculated. // the manifest is generated and its digest calculated.
RepoDigests []string RepoDigests []string
// Parent is the ID of the parent image.
//
// Depending on how the image was created, this field may be empty and
// is only set for images that were built/created locally. This field
// is omitted if the image was pulled from an image registry.
//
// Deprecated: this field is deprecated, and will be removed in the next release.
Parent string `json:",omitempty"`
// Comment is an optional message that can be set when committing or // Comment is an optional message that can be set when committing or
// importing the image. This field is omitted if not set. // importing the image. This field is omitted if not set.
Comment string `json:",omitempty"` Comment string `json:",omitempty"`
@@ -62,13 +53,6 @@ type InspectResponse struct {
// and omitted otherwise. // and omitted otherwise.
Created string `json:",omitempty"` Created string `json:",omitempty"`
// DockerVersion is the version of Docker that was used to build the image.
//
// Depending on how the image was created, this field may be omitted.
//
// Deprecated: this field is deprecated, and will be removed in the next release.
DockerVersion string `json:",omitempty"`
// Author is the name of the author that was specified when committing the // Author is the name of the author that was specified when committing the
// image, or as specified through MAINTAINER (deprecated) in the Dockerfile. // image, or as specified through MAINTAINER (deprecated) in the Dockerfile.
// This field is omitted if not set. // This field is omitted if not set.

View File

@@ -91,13 +91,13 @@ func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, opts im
RepoTags: repoTags, RepoTags: repoTags,
Descriptor: &target, Descriptor: &target,
RepoDigests: repoDigests, RepoDigests: repoDigests,
Parent: parent, //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present.
Size: size, Size: size,
Manifests: manifests, Manifests: manifests,
Metadata: imagetypes.Metadata{ Metadata: imagetypes.Metadata{
LastTagTime: lastUpdated, LastTagTime: lastUpdated,
}, },
}, },
Parent: parent, // field is deprecated with the legacy builder, but returned by the API if present.
} }
if multi.Best != nil { if multi.Best != nil {

View File

@@ -56,20 +56,18 @@ func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, opts im
imgConfig := containerConfigToDockerOCIImageConfig(img.Config) imgConfig := containerConfigToDockerOCIImageConfig(img.Config)
return &imagebackend.InspectData{ return &imagebackend.InspectData{
InspectResponse: imagetypes.InspectResponse{ InspectResponse: imagetypes.InspectResponse{
ID: img.ID().String(), ID: img.ID().String(),
RepoTags: repoTags, RepoTags: repoTags,
RepoDigests: repoDigests, RepoDigests: repoDigests,
Parent: img.Parent.String(), //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present (built with legacy builder). Comment: comment,
Comment: comment, Created: created,
Created: created, Author: img.Author,
DockerVersion: img.DockerVersion, //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present. Config: &imgConfig,
Author: img.Author, Architecture: img.Architecture,
Config: &imgConfig, Variant: img.Variant,
Architecture: img.Architecture, Os: img.OperatingSystem(),
Variant: img.Variant, OsVersion: img.OSVersion,
Os: img.OperatingSystem(), Size: size,
OsVersion: img.OSVersion,
Size: size,
GraphDriver: &storage.DriverData{ GraphDriver: &storage.DriverData{
Name: i.layerStore.DriverName(), Name: i.layerStore.DriverName(),
Data: layerMetadata, Data: layerMetadata,
@@ -82,6 +80,8 @@ func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, opts im
LastTagTime: lastUpdated, 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. 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. ContainerConfig: &img.ContainerConfig, // field is deprecated, but still set on API < v1.45.
}, nil }, nil

View File

@@ -61,6 +61,22 @@ type ImageInspectOpts struct {
type InspectData struct { type InspectData struct {
imagetypes.InspectResponse imagetypes.InspectResponse
// Parent is the ID of the parent image.
//
// Depending on how the image was created, this field may be empty and
// is only set for images that were built/created locally. This field
// is omitted if the image was pulled from an image registry.
//
// This field is deprecated with the legacy builder, but returned by the API if present.
Parent string `json:",omitempty"`
// DockerVersion is the version of Docker that was used to build the image.
//
// Depending on how the image was created, this field may be omitted.
//
// This field is deprecated with the legacy builder, but returned by the API if present.
DockerVersion string `json:",omitempty"`
// Container is the ID of the container that was used to create the image. // Container is the ID of the container that was used to create the image.
// //
// Depending on how the image was created, this field may be empty. // Depending on how the image was created, this field may be empty.

View File

@@ -423,9 +423,9 @@ func (ir *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWrite
// These fields have "omitempty" on API v1.52 and higher, // These fields have "omitempty" on API v1.52 and higher,
// but older API versions returned them unconditionally. // but older API versions returned them unconditionally.
legacyOptions = append(legacyOptions, compat.WithExtraFields(map[string]any{ legacyOptions = append(legacyOptions, compat.WithExtraFields(map[string]any{
"Parent": inspectData.Parent, //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present (built with legacy builder). "Parent": inspectData.Parent, // field is deprecated, but still included in response when present (built with legacy builder).
"Comment": inspectData.Comment, "Comment": inspectData.Comment,
"DockerVersion": inspectData.DockerVersion, //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present. "DockerVersion": inspectData.DockerVersion, // field is deprecated, but still included in response when present.
"Author": inspectData.Author, "Author": inspectData.Author,
})) }))
@@ -440,6 +440,15 @@ func (ir *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWrite
"Config": legacyConfigFields["v1.50-v1.51"], "Config": legacyConfigFields["v1.50-v1.51"],
})) }))
} }
} else {
if inspectData.Parent != "" {
// field is deprecated, but still included in response when present (built with legacy builder).
legacyOptions = append(legacyOptions, compat.WithExtraFields(map[string]any{"Parent": inspectData.Parent}))
}
if inspectData.DockerVersion != "" {
// field is deprecated, but still included in response when present.
legacyOptions = append(legacyOptions, compat.WithExtraFields(map[string]any{"DockerVersion": inspectData.DockerVersion}))
}
} }
if len(legacyOptions) > 0 { if len(legacyOptions) > 0 {

View File

@@ -319,10 +319,22 @@ func (s *DockerAPISuite) TestBuildOnBuildCache(c *testing.T) {
// check parentID is correct // check parentID is correct
// Parent is graphdriver-only // Parent is graphdriver-only
if !testEnv.UsingSnapshotter() { if !testEnv.UsingSnapshotter() {
image, err := apiClient.ImageInspect(ctx, childID) var buf bytes.Buffer
_, err := apiClient.ImageInspect(ctx, childID, client.ImageInspectWithRawResponse(&buf))
assert.NilError(c, err) assert.NilError(c, err)
assert.Check(c, is.Equal(parentID, image.Parent)) //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present. var image struct {
// Parent is the ID of the parent image.
//
// Depending on how the image was created, this field may be empty and
// is only set for images that were built/created locally. This field
// is omitted if the image was pulled from an image registry.
Parent string `json:",omitempty"`
}
rawResponse := buf.Bytes()
err = json.Unmarshal(rawResponse, &image)
assert.NilError(c, err, string(rawResponse))
assert.Check(c, is.Equal(parentID, image.Parent), string(rawResponse))
} }
} }

View File

@@ -42,15 +42,6 @@ type InspectResponse struct {
// the manifest is generated and its digest calculated. // the manifest is generated and its digest calculated.
RepoDigests []string RepoDigests []string
// Parent is the ID of the parent image.
//
// Depending on how the image was created, this field may be empty and
// is only set for images that were built/created locally. This field
// is omitted if the image was pulled from an image registry.
//
// Deprecated: this field is deprecated, and will be removed in the next release.
Parent string `json:",omitempty"`
// Comment is an optional message that can be set when committing or // Comment is an optional message that can be set when committing or
// importing the image. This field is omitted if not set. // importing the image. This field is omitted if not set.
Comment string `json:",omitempty"` Comment string `json:",omitempty"`
@@ -62,13 +53,6 @@ type InspectResponse struct {
// and omitted otherwise. // and omitted otherwise.
Created string `json:",omitempty"` Created string `json:",omitempty"`
// DockerVersion is the version of Docker that was used to build the image.
//
// Depending on how the image was created, this field may be omitted.
//
// Deprecated: this field is deprecated, and will be removed in the next release.
DockerVersion string `json:",omitempty"`
// Author is the name of the author that was specified when committing the // Author is the name of the author that was specified when committing the
// image, or as specified through MAINTAINER (deprecated) in the Dockerfile. // image, or as specified through MAINTAINER (deprecated) in the Dockerfile.
// This field is omitted if not set. // This field is omitted if not set.