From 0525ae2aed2b8681eacacf258e17f8d58821a3f7 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 5 Sep 2025 18:23:02 +0200 Subject: [PATCH] api: image inspect: remove temporary backfill for Config fields Commit 4dc961d0e922381b8cd5e05223f172f3145c7494 (API v1.50) fixed the type used for Config to point to the correct type, which is the Config struct from the [Docker image spec] (which embeds the [OCI Image Specification] type); however, those types use an omitempty, which wasn't documented as part of the API changes, so f85394dd5d7ea27d5189f22a53eebd69e2acc7f3 added a temporary backfill for empty fields. This removes that backfill for API v1.52 so that empty image config fields are now omitted. [OCI Image Specification]: https://github.com/opencontainers/image-spec/blob/v1.1.1/specs-go/v1/config.go#L23-L62 [Docker image spec]: https://github.com/moby/docker-image-spec/blob/v1.3.1/specs-go/v1/image.go#L19-L32 Signed-off-by: Sebastiaan van Stijn --- api/docs/CHANGELOG.md | 3 +++ daemon/server/router/image/image_routes.go | 15 +++++++++------ daemon/server/router/image/inspect_response.go | 4 ++-- .../server/router/image/inspect_response_test.go | 4 ++-- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/api/docs/CHANGELOG.md b/api/docs/CHANGELOG.md index b8e896ed9e..3be1399630 100644 --- a/api/docs/CHANGELOG.md +++ b/api/docs/CHANGELOG.md @@ -26,6 +26,9 @@ keywords: "API, Docker, rcli, REST, documentation" a PortBinding with an empty HostIP and HostPort when calling `POST /containers/{id}/start`. This behavior is now deprecated, and a warning is returned by `POST /containers/create`. The next API version will drop empty `PortBindings` list altogether. +* `GET /images/{name}/json` now omits the following `Config` fields when + not set, to closer align with the implementation of the [OCI Image Specification](https://github.com/opencontainers/image-spec/blob/v1.1.1/specs-go/v1/config.go#L23-L62) + `Cmd`, `Entrypoint`, `Env`, `Labels`, `OnBuild`, `User`, `Volumes`, and `WorkingDir`. ## v1.51 API changes diff --git a/daemon/server/router/image/image_routes.go b/daemon/server/router/image/image_routes.go index 7bfb5097ba..1af0e13fd2 100644 --- a/daemon/server/router/image/image_routes.go +++ b/daemon/server/router/image/image_routes.go @@ -372,12 +372,8 @@ func (ir *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWrite return err } - // inspectResponse preserves fields in the response that have an - // "omitempty" in the OCI spec, but didn't omit such fields in - // legacy responses before API v1.50. imageInspect := &inspectCompatResponse{ InspectResponse: resp, - legacyConfig: legacyConfigFields["current"], } // Make sure we output empty arrays instead of nil. While Go nil slice is functionally equivalent to an empty slice, @@ -406,8 +402,15 @@ func (ir *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWrite if versions.LessThan(version, "1.48") { imageInspect.Descriptor = nil } - if versions.LessThan(version, "1.50") { - imageInspect.legacyConfig = legacyConfigFields["v1.49"] + if versions.LessThan(version, "1.52") { + if versions.LessThan(version, "1.50") { + imageInspect.legacyConfig = legacyConfigFields["v1.49"] + } else { + // inspectResponse preserves fields in the response that have an + // "omitempty" in the OCI spec, but didn't omit such fields in + // legacy responses before API v1.50. + imageInspect.legacyConfig = legacyConfigFields["v1.50-v1.51"] + } } return httputils.WriteJSON(w, http.StatusOK, imageInspect) diff --git a/daemon/server/router/image/inspect_response.go b/daemon/server/router/image/inspect_response.go index c393d353de..691292b5d0 100644 --- a/daemon/server/router/image/inspect_response.go +++ b/daemon/server/router/image/inspect_response.go @@ -31,10 +31,10 @@ var legacyConfigFields = map[string]map[string]any{ "Volumes": nil, "WorkingDir": "", }, - // Legacy fields for current API versions (v1.50 and up). These fields + // Legacy fields for current API versions (v1.50 and v1.52). These fields // did not have an "omitempty" and were always included in the response, // even if not set; see https://github.com/moby/moby/issues/50134 - "current": { + "v1.50-v1.51": { "Cmd": nil, "Entrypoint": nil, "Env": nil, diff --git a/daemon/server/router/image/inspect_response_test.go b/daemon/server/router/image/inspect_response_test.go index 7e2905b76f..e0ec55a25a 100644 --- a/daemon/server/router/image/inspect_response_test.go +++ b/daemon/server/router/image/inspect_response_test.go @@ -40,12 +40,12 @@ func TestInspectResponse(t *testing.T) { expected: `{"AttachStderr":false,"AttachStdin":false,"AttachStdout":false,"Cmd":["/bin/sh"],"Domainname":"","Entrypoint":null,"Env":null,"Hostname":"","Image":"","Labels":null,"OnBuild":null,"OpenStdin":false,"StdinOnce":false,"StopSignal":"SIGQUIT","Tty":false,"User":"","Volumes":null,"WorkingDir":""}`, }, { - doc: "api >= v1.50", + doc: "api v1.50 - v1.51", cfg: &ocispec.ImageConfig{ Cmd: []string{"/bin/sh"}, StopSignal: "SIGQUIT", }, - legacyConfig: legacyConfigFields["current"], + legacyConfig: legacyConfigFields["v1.50-v1.51"], expected: `{"Cmd":["/bin/sh"],"Entrypoint":null,"Env":null,"Labels":null,"OnBuild":null,"StopSignal":"SIGQUIT","User":"","Volumes":null,"WorkingDir":""}`, }, }