From 9bcfee6b44852ffd624bf191eb466ad00ca7ff8f Mon Sep 17 00:00:00 2001 From: Marek Simek Date: Mon, 25 May 2026 11:50:15 +0200 Subject: [PATCH 1/3] api: Deprecate is-automated filter and field in GET /images/search endpoint The Docker API in version 1.44 deprecates the is_automated field for the GET /images/search endpoint. The is_automated field has been deprecated by Docker Hub's search API. Consequently, searching for is-automated=true will yield no results. The Docker API in version 1.44 deprecates the is_automated field in the GET /images/search response and will always be set to false in the future because Docker Hub is deprecating the is_automated field in its search API. Return struct moby/api for the compat endpoint that matches the Docker API response format and deprecates is_automated. Update test_v2_0_0_image.py::ImageTestCase::test_search_compat to verify returned format and fix subtests not being asserted (remove mp). Fixes: https://redhat.atlassian.net/browse/RUN-3323 Signed-off-by: Marek Simek --- pkg/api/handlers/compat/images_search.go | 20 ++++++++ pkg/api/handlers/swagger/responses.go | 8 +++ pkg/api/server/register_images.go | 2 +- .../python/rest_api/test_v2_0_0_image.py | 51 +++++++++---------- 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go index d7453f73587..ebbafd05b70 100644 --- a/pkg/api/handlers/compat/images_search.go +++ b/pkg/api/handlers/compat/images_search.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" + "github.com/moby/moby/api/types/registry" "go.podman.io/image/v5/types" "go.podman.io/podman/v6/libpod" "go.podman.io/podman/v6/pkg/api/handlers/utils" @@ -77,7 +78,26 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { utils.ImageNotFound(w, query.Term, storage.ErrImageUnknown) return } + compatResults := make([]registry.SearchResult, len(reports)) + for i, r := range reports { + compatResults[i] = registry.SearchResult{ + Name: r.Name, + Description: r.Description, + StarCount: r.Stars, + IsOfficial: isOK(r.Official), + IsAutomated: isOK(r.Automated), + } + } + utils.WriteResponse(w, http.StatusOK, compatResults) + return } utils.WriteResponse(w, http.StatusOK, reports) } + +// isOK converts the libimage format of +// string flags to bool to be compatible with +// the Docker API. +func isOK(s string) bool { + return s == "[OK]" +} diff --git a/pkg/api/handlers/swagger/responses.go b/pkg/api/handlers/swagger/responses.go index 65c11dbb48d..237ee77b6df 100644 --- a/pkg/api/handlers/swagger/responses.go +++ b/pkg/api/handlers/swagger/responses.go @@ -7,6 +7,7 @@ import ( "github.com/moby/moby/api/types/container" dockerImage "github.com/moby/moby/api/types/image" "github.com/moby/moby/api/types/network" + "github.com/moby/moby/api/types/registry" "github.com/moby/moby/api/types/volume" "go.podman.io/common/libnetwork/types" "go.podman.io/image/v5/manifest" @@ -93,6 +94,13 @@ type imageDeleteResponse struct { // Registry Search // swagger:response type registrySearchResponse struct { + // in:body + Body []registry.SearchResult +} + +// Registry Search +// swagger:response +type registrySearchResponseLibpod struct { // in:body Body struct { // Index is the image index diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 2c41e149a1f..d973e2f323d 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -1287,7 +1287,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - application/json // responses: // 200: - // $ref: "#/responses/registrySearchResponse" + // $ref: "#/responses/registrySearchResponseLibpod" // 500: // $ref: '#/responses/internalError' r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(compat.SearchImages)).Methods(http.MethodGet) diff --git a/test/apiv2/python/rest_api/test_v2_0_0_image.py b/test/apiv2/python/rest_api/test_v2_0_0_image.py index 8bba369579d..81f923c304d 100644 --- a/test/apiv2/python/rest_api/test_v2_0_0_image.py +++ b/test/apiv2/python/rest_api/test_v2_0_0_image.py @@ -1,7 +1,5 @@ import json import unittest -import multiprocessing as mp - import requests from dateutil.parser import parse from .fixtures import APITestCase @@ -164,18 +162,33 @@ def test_create(self): self.assertEqual(r.status_code, 200, r.text) def test_search_compat(self): - url = self.podman_url + "/v1.40/images/search" + url = self.podman_url + "/v1.44/images/search" # Had issues with this test hanging when repositories not happy def do_search1(): + required_keys = ( + "description", + "is_automated", # Deprecated: always false. + "is_official", + "name", + "star_count", + ) payload = {"term": "alpine"} - r = requests.get(url, params=payload, timeout=5) + r = requests.get(url, params=payload, timeout=30) self.assertEqual(r.status_code, 200, f"#1: {r.text}") - self.assertIsInstance(r.json(), list) + + results = r.json() + self.assertIsInstance(results, list) + for item in results: + for k in required_keys: + self.assertIn(k, item) def do_search2(): - payload = {"term": "alpine", "limit": 1} - r = requests.get(url, params=payload, timeout=5) + # The containers.conf uses: + # compat_api_enforce_docker_hub=false + # and full name needs to be used here. + payload = {"term": "docker.io/library/alpine", "limit": 1} + r = requests.get(url, params=payload, timeout=30) self.assertEqual(r.status_code, 200, f"#2: {r.text}") results = r.json() @@ -186,7 +199,7 @@ def do_search3(): # FIXME: Research if quay.io supports is-official and which image is "official" return payload = {"term": "thanos", "filters": '{"is-official":["true"]}'} - r = requests.get(url, params=payload, timeout=5) + r = requests.get(url, params=payload, timeout=30) self.assertEqual(r.status_code, 200, f"#3: {r.text}") results = r.json() @@ -198,32 +211,18 @@ def do_search3(): def do_search4(): headers = {"X-Registry-Auth": "null"} payload = {"term": "alpine"} - r = requests.get(url, params=payload, headers=headers, timeout=5) + r = requests.get(url, params=payload, headers=headers, timeout=30) self.assertEqual(r.status_code, 200, f"#4: {r.text}") def do_search5(): headers = {"X-Registry-Auth": "invalid value"} payload = {"term": "alpine"} - r = requests.get(url, params=payload, headers=headers, timeout=5) + r = requests.get(url, params=payload, headers=headers, timeout=30) self.assertEqual(r.status_code, 400, f"#5: {r.text}") - i = 1 - # Need to explicitly set start method - # # https://docs.python.org/dev/library/multiprocessing.html#contexts-and-start-methods - mp.set_start_method('fork') - for fn in [do_search1, do_search2, do_search3, do_search4, do_search5]: + for i, t in enumerate([do_search1, do_search2, do_search3, do_search4, do_search5], start=1): with self.subTest(i=i): - search = mp.Process(target=fn) - search.start() - search.join(timeout=10) - self.assertFalse(search.is_alive(), f"#{i} /images/search took too long") - - # search_methods = [do_search1, do_search2, do_search3, do_search4, do_search5] - # for search_method in search_methods: - # search = Process(target=search_method) - # search.start() - # search.join(timeout=10) - # self.assertFalse(search.is_alive(), "/images/search took too long") + t() def test_history(self): r = requests.get(self.podman_url + "/v1.40/images/alpine/history") From b56648305016697d88799ea85a6f26253c451de7 Mon Sep 17 00:00:00 2001 From: Marek Simek Date: Mon, 25 May 2026 12:25:28 +0200 Subject: [PATCH 2/3] api: Deprecate response fields from GET /containers/{id}/json The Docker API 1.44 deprecates the fields HairpinMode, LinkLocalIPv6Address, LinkLocalIPv6PrefixLen, SecondaryIPAddresses, SecondaryIPv6Addresses available in NetworkSettings when calling GET /containers/{id}/json and will be removed in a future release. You should instead look for the default network in NetworkSettings.Networks. The fields are removed in 1.52. Version gate SecondaryIPAddresses, SecondaryIPv6Addresses in the handler and update test. HairpinMode, LinkLocalIPv6Address, LinkLocalIPv6PrefixLen are not returned by the compat endpoint as the response is serialized to the moby/moby/api structure missing these fields. Fixes: https://redhat.atlassian.net/browse/RUN-3323 Signed-off-by: Marek Simek --- pkg/api/handlers/compat/containers.go | 6 ++++++ test/apiv2/python/rest_api/test_v2_0_0_container.py | 13 +++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index ecebdda375e..453eea5e957 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -212,6 +212,12 @@ func GetContainer(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } + if _, err := utils.SupportedVersion(r, ">=1.52.0"); err == nil || errors.Is(err, apiutil.ErrVersionNotGiven) { + if api.NetworkSettings != nil { + api.NetworkSettings.SecondaryIPAddresses = nil + api.NetworkSettings.SecondaryIPv6Addresses = nil + } + } utils.WriteResponse(w, http.StatusOK, api) } diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py index 3c24298f4c8..868fd264a3a 100644 --- a/test/apiv2/python/rest_api/test_v2_0_0_container.py +++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py @@ -535,13 +535,22 @@ def test_inspect_network(self): r = requests.post(self.uri(self.resolve_container("/containers/{}/start"))) self.assertIn(r.status_code, (204, 304), r.text) - r = requests.get(self.compat_uri(self.resolve_container("/containers/{}/json"))) + container_uri = self.resolve_container("/containers/{}/json") + + # SecondaryIPAddresses present for API < v1.52 + r = requests.get(self.podman_url + "/v1.44/" + container_uri) self.assertEqual(r.status_code, 200, r.text) self.assertId(r.content) out = r.json() - self.assertEqual("10.0.2.0", out["NetworkSettings"]["SecondaryIPAddresses"][0]["Addr"]) self.assertEqual(24, out["NetworkSettings"]["SecondaryIPAddresses"][0]["PrefixLen"]) + + # SecondaryIPAddresses removed for API >= v1.52 + r = requests.get(self.podman_url + "/v1.52/" + container_uri) + self.assertEqual(r.status_code, 200, r.text) + self.assertId(r.content) + out = r.json() + self.assertIsNone(out["NetworkSettings"].get("SecondaryIPAddresses")) finally: delete_named_network_ns(network_ns_name) From f9dccd1ff23abb302729462bce9dfc9c742235da Mon Sep 17 00:00:00 2001 From: Marek Simek Date: Mon, 25 May 2026 13:52:31 +0200 Subject: [PATCH 3/3] api: Deprecate fields Container and ContainerConfig from GET /images/{name}/json The Docker API deprecates Container and ContainerConfig fields in the GET /images/{name}/json response are deprecated and they will no longer be included in API v1.45. Fixes: https://redhat.atlassian.net/browse/RUN-3323 Signed-off-by: Marek Simek --- pkg/api/handlers/compat/images.go | 2 +- pkg/api/handlers/types.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index eb879a522a0..aa34b436fbf 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -419,7 +419,7 @@ func imageDataToImageInspect(ctx context.Context, l *libimage.Image, r *http.Req } if _, err := apiutil.SupportedVersion(r, "<1.45.0"); err == nil { - imageInspect.ContainerConfig = cc + imageInspect.ContainerConfig = cc //nolint:staticcheck // Deprecated field } return &imageInspect, nil diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index b70c4d156d9..68166b90e95 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -25,7 +25,12 @@ type ImageInspect struct { // If a field in the outer struct has the same name as a field in the embedded struct, // the outer struct's field will shadow or override the embedded one allowing for a clean way to // hide fields from the swagger spec that still exist in the libraries struct. - Container string `json:"Container,omitempty"` + // + // Deprecated: since API v1.44, will no longer be included in API v1.45. + // https://docs.docker.com/reference/api/engine/version-history/#v144-api-changes + Container string `json:"Container,omitempty"` + // Deprecated: since API v1.44, will no longer be included in API v1.45. + // https://docs.docker.com/reference/api/engine/version-history/#v144-api-changes ContainerConfig *dockerContainer.Config `json:"ContainerConfig,omitempty"` VirtualSize int64 `json:"VirtualSize,omitempty"` Parent string `json:"Parent"`