diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 8568a93e576..78249974d81 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/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 8638f9865e8..94e782c5eed 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -418,7 +418,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/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 27a899b81bc..d4480b2efe8 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" @@ -94,7 +95,14 @@ type imageDeleteResponse struct { // swagger:response type registrySearchResponse struct { // in:body - Body []entities.ImageSearchReport + Body []registry.SearchResult +} + +// Registry Search +// swagger:response +type registrySearchResponseLibpod struct { + // in:body + Body entities.ImageSearchReport } // Inspect Image 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"` 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_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) 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")