Browse code

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]: https://github.com/moby/moby/commit/bd8a99b4004b0b766db7f5f79dfc2dad553b9426
[`client.ImageInspect`]: https://github.com/moby/moby/blob/f739c61c69a7155362993c2a2a624e838b3893bc/client/image_inspect.go#L14-L64
[`docker image inspect`]: https://github.com/docker/cli/blob/74e3520724d77e63ef75987cbd0c0cde507ab971/cli/command/image/inspect.go#L59-L81

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2025/10/06 00:07:04
Showing 7 changed files
... ...
@@ -42,15 +42,6 @@ type InspectResponse struct {
42 42
 	// the manifest is generated and its digest calculated.
43 43
 	RepoDigests []string
44 44
 
45
-	// Parent is the ID of the parent image.
46
-	//
47
-	// Depending on how the image was created, this field may be empty and
48
-	// is only set for images that were built/created locally. This field
49
-	// is omitted if the image was pulled from an image registry.
50
-	//
51
-	// Deprecated: this field is deprecated, and will be removed in the next release.
52
-	Parent string `json:",omitempty"`
53
-
54 45
 	// Comment is an optional message that can be set when committing or
55 46
 	// importing the image. This field is omitted if not set.
56 47
 	Comment string `json:",omitempty"`
... ...
@@ -62,13 +53,6 @@ type InspectResponse struct {
62 62
 	// and omitted otherwise.
63 63
 	Created string `json:",omitempty"`
64 64
 
65
-	// DockerVersion is the version of Docker that was used to build the image.
66
-	//
67
-	// Depending on how the image was created, this field may be omitted.
68
-	//
69
-	// Deprecated: this field is deprecated, and will be removed in the next release.
70
-	DockerVersion string `json:",omitempty"`
71
-
72 65
 	// Author is the name of the author that was specified when committing the
73 66
 	// image, or as specified through MAINTAINER (deprecated) in the Dockerfile.
74 67
 	// This field is omitted if not set.
... ...
@@ -91,13 +91,13 @@ func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, opts im
91 91
 			RepoTags:    repoTags,
92 92
 			Descriptor:  &target,
93 93
 			RepoDigests: repoDigests,
94
-			Parent:      parent, //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present.
95 94
 			Size:        size,
96 95
 			Manifests:   manifests,
97 96
 			Metadata: imagetypes.Metadata{
98 97
 				LastTagTime: lastUpdated,
99 98
 			},
100 99
 		},
100
+		Parent: parent, // field is deprecated with the legacy builder, but returned by the API if present.
101 101
 	}
102 102
 
103 103
 	if multi.Best != nil {
... ...
@@ -56,20 +56,18 @@ func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, opts im
56 56
 	imgConfig := containerConfigToDockerOCIImageConfig(img.Config)
57 57
 	return &imagebackend.InspectData{
58 58
 		InspectResponse: imagetypes.InspectResponse{
59
-			ID:            img.ID().String(),
60
-			RepoTags:      repoTags,
61
-			RepoDigests:   repoDigests,
62
-			Parent:        img.Parent.String(), //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present (built with legacy builder).
63
-			Comment:       comment,
64
-			Created:       created,
65
-			DockerVersion: img.DockerVersion, //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present.
66
-			Author:        img.Author,
67
-			Config:        &imgConfig,
68
-			Architecture:  img.Architecture,
69
-			Variant:       img.Variant,
70
-			Os:            img.OperatingSystem(),
71
-			OsVersion:     img.OSVersion,
72
-			Size:          size,
59
+			ID:           img.ID().String(),
60
+			RepoTags:     repoTags,
61
+			RepoDigests:  repoDigests,
62
+			Comment:      comment,
63
+			Created:      created,
64
+			Author:       img.Author,
65
+			Config:       &imgConfig,
66
+			Architecture: img.Architecture,
67
+			Variant:      img.Variant,
68
+			Os:           img.OperatingSystem(),
69
+			OsVersion:    img.OSVersion,
70
+			Size:         size,
73 71
 			GraphDriver: &storage.DriverData{
74 72
 				Name: i.layerStore.DriverName(),
75 73
 				Data: layerMetadata,
... ...
@@ -82,6 +80,8 @@ func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, opts im
82 82
 				LastTagTime: lastUpdated,
83 83
 			},
84 84
 		},
85
+		Parent:          img.Parent.String(),  // field is deprecated with the legacy builder, but still included in response when present (built with legacy builder).
86
+		DockerVersion:   img.DockerVersion,    // field is deprecated with the legacy builder, but still included in response when present.
85 87
 		Container:       img.Container,        // field is deprecated, but still set on API < v1.45.
86 88
 		ContainerConfig: &img.ContainerConfig, // field is deprecated, but still set on API < v1.45.
87 89
 	}, nil
... ...
@@ -61,6 +61,22 @@ type ImageInspectOpts struct {
61 61
 type InspectData struct {
62 62
 	imagetypes.InspectResponse
63 63
 
64
+	// Parent is the ID of the parent image.
65
+	//
66
+	// Depending on how the image was created, this field may be empty and
67
+	// is only set for images that were built/created locally. This field
68
+	// is omitted if the image was pulled from an image registry.
69
+	//
70
+	// This field is deprecated with the legacy builder, but returned by the API if present.
71
+	Parent string `json:",omitempty"`
72
+
73
+	// DockerVersion is the version of Docker that was used to build the image.
74
+	//
75
+	// Depending on how the image was created, this field may be omitted.
76
+	//
77
+	// This field is deprecated with the legacy builder, but returned by the API if present.
78
+	DockerVersion string `json:",omitempty"`
79
+
64 80
 	// Container is the ID of the container that was used to create the image.
65 81
 	//
66 82
 	// Depending on how the image was created, this field may be empty.
... ...
@@ -423,9 +423,9 @@ func (ir *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWrite
423 423
 		// These fields have "omitempty" on API v1.52 and higher,
424 424
 		// but older API versions returned them unconditionally.
425 425
 		legacyOptions = append(legacyOptions, compat.WithExtraFields(map[string]any{
426
-			"Parent":        inspectData.Parent, //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present (built with legacy builder).
426
+			"Parent":        inspectData.Parent, // field is deprecated, but still included in response when present (built with legacy builder).
427 427
 			"Comment":       inspectData.Comment,
428
-			"DockerVersion": inspectData.DockerVersion, //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present.
428
+			"DockerVersion": inspectData.DockerVersion, // field is deprecated, but still included in response when present.
429 429
 			"Author":        inspectData.Author,
430 430
 		}))
431 431
 
... ...
@@ -440,6 +440,15 @@ func (ir *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWrite
440 440
 				"Config": legacyConfigFields["v1.50-v1.51"],
441 441
 			}))
442 442
 		}
443
+	} else {
444
+		if inspectData.Parent != "" {
445
+			// field is deprecated, but still included in response when present (built with legacy builder).
446
+			legacyOptions = append(legacyOptions, compat.WithExtraFields(map[string]any{"Parent": inspectData.Parent}))
447
+		}
448
+		if inspectData.DockerVersion != "" {
449
+			// field is deprecated, but still included in response when present.
450
+			legacyOptions = append(legacyOptions, compat.WithExtraFields(map[string]any{"DockerVersion": inspectData.DockerVersion}))
451
+		}
443 452
 	}
444 453
 
445 454
 	if len(legacyOptions) > 0 {
... ...
@@ -319,10 +319,22 @@ func (s *DockerAPISuite) TestBuildOnBuildCache(c *testing.T) {
319 319
 	// check parentID is correct
320 320
 	// Parent is graphdriver-only
321 321
 	if !testEnv.UsingSnapshotter() {
322
-		image, err := apiClient.ImageInspect(ctx, childID)
322
+		var buf bytes.Buffer
323
+		_, err := apiClient.ImageInspect(ctx, childID, client.ImageInspectWithRawResponse(&buf))
323 324
 		assert.NilError(c, err)
324 325
 
325
-		assert.Check(c, is.Equal(parentID, image.Parent)) //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present.
326
+		var image struct {
327
+			// Parent is the ID of the parent image.
328
+			//
329
+			// Depending on how the image was created, this field may be empty and
330
+			// is only set for images that were built/created locally. This field
331
+			// is omitted if the image was pulled from an image registry.
332
+			Parent string `json:",omitempty"`
333
+		}
334
+		rawResponse := buf.Bytes()
335
+		err = json.Unmarshal(rawResponse, &image)
336
+		assert.NilError(c, err, string(rawResponse))
337
+		assert.Check(c, is.Equal(parentID, image.Parent), string(rawResponse))
326 338
 	}
327 339
 }
328 340
 
... ...
@@ -42,15 +42,6 @@ type InspectResponse struct {
42 42
 	// the manifest is generated and its digest calculated.
43 43
 	RepoDigests []string
44 44
 
45
-	// Parent is the ID of the parent image.
46
-	//
47
-	// Depending on how the image was created, this field may be empty and
48
-	// is only set for images that were built/created locally. This field
49
-	// is omitted if the image was pulled from an image registry.
50
-	//
51
-	// Deprecated: this field is deprecated, and will be removed in the next release.
52
-	Parent string `json:",omitempty"`
53
-
54 45
 	// Comment is an optional message that can be set when committing or
55 46
 	// importing the image. This field is omitted if not set.
56 47
 	Comment string `json:",omitempty"`
... ...
@@ -62,13 +53,6 @@ type InspectResponse struct {
62 62
 	// and omitted otherwise.
63 63
 	Created string `json:",omitempty"`
64 64
 
65
-	// DockerVersion is the version of Docker that was used to build the image.
66
-	//
67
-	// Depending on how the image was created, this field may be omitted.
68
-	//
69
-	// Deprecated: this field is deprecated, and will be removed in the next release.
70
-	DockerVersion string `json:",omitempty"`
71
-
72 65
 	// Author is the name of the author that was specified when committing the
73 66
 	// image, or as specified through MAINTAINER (deprecated) in the Dockerfile.
74 67
 	// This field is omitted if not set.