From 4cb2d48ca42ee697c6dda3b00752059cc655e0a8 Mon Sep 17 00:00:00 2001 From: Matej Vasek Date: Thu, 10 Aug 2023 23:31:54 +0200 Subject: [PATCH 1/2] Revert "compat,build: pull must accept string" This reverts commit 5b148a0a68360892f57603b77e73c69e32fb7b7e. Reverting to treating the `pull` query parameter as a boolean. Because of deceiving Docker API documentation it was assumed that the parameter is pull-policy, however that is not true. Docker does treat `pull` as a boolean. What is interesting is that Docker indeed accepts strings like `always` or `never` however Docekr both of these strings treat as `true`, not as pull-policy. As matter of the fact it seems there is no such a thing as pull-policy in Docker. More context https://github.com/containers/podman/issues/17778#issuecomment-1673931925 Signed-off-by: Matej Vasek --- pkg/api/handlers/compat/images_build.go | 15 ++------------- pkg/api/server/register_images.go | 8 ++++---- test/apiv2/10-images.at | 5 ----- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 984660c32c..3e18bbc0ee 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -121,7 +121,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { OSVersion string `schema:"osversion"` OutputFormat string `schema:"outputformat"` Platform []string `schema:"platform"` - Pull string `schema:"pull"` + Pull bool `schema:"pull"` PullPolicy string `schema:"pullpolicy"` Quiet bool `schema:"q"` Registry string `schema:"registry"` @@ -580,19 +580,8 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { pullPolicy = buildahDefine.PolicyMap[query.PullPolicy] } else { if _, found := r.URL.Query()["pull"]; found { - switch strings.ToLower(query.Pull) { - case "false": - pullPolicy = buildahDefine.PullIfMissing - case "true": + if query.Pull { pullPolicy = buildahDefine.PullAlways - default: - policyFromMap, foundPolicy := buildahDefine.PolicyMap[query.Pull] - if foundPolicy { - pullPolicy = policyFromMap - } else { - utils.BadRequest(w, "pull", query.Pull, fmt.Errorf("invalid pull policy: %q", query.Pull)) - return - } } } } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index fb9f0d753f..805af97f82 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -546,8 +546,8 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // (As of version 1.xx) // - in: query // name: pull - // type: string - // default: + // type: boolean + // default: false // description: | // Attempt to pull the image even if an older image exists locally // (As of version 1.xx) @@ -1453,8 +1453,8 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // (As of version 1.xx) // - in: query // name: pull - // type: string - // default: + // type: boolean + // default: false // description: | // Attempt to pull the image even if an older image exists locally // (As of version 1.xx) diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index 94a8bee480..79b2039695 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -234,11 +234,6 @@ t POST "build?dockerfile=containerfile" $CONTAINERFILE_TAR application/json 200 response_headers=$(cat "$WORKDIR/curl.headers.out") like "$response_headers" ".*application/json.*" "header does not contain application/json" -# Build api response header must contain Content-type: application/json -t POST "build?dockerfile=containerfile&pull=never" $CONTAINERFILE_TAR application/json 200 -response_headers=$(cat "$WORKDIR/curl.headers.out") -like "$response_headers" ".*application/json.*" "header does not contain application/json" - # PR #12091: output from compat API must now include {"aux":{"ID":"sha..."}} t POST "build?dockerfile=containerfile" $CONTAINERFILE_TAR 200 \ '.aux|select(has("ID")).ID~^sha256:[0-9a-f]\{64\}$' From f33b01b7312f86cb4b2d0428e10e4b9318193c4d Mon Sep 17 00:00:00 2001 From: Matej Vasek Date: Thu, 10 Aug 2023 23:47:09 +0200 Subject: [PATCH 2/2] fix: Docker API compatible bool deserialization In Docker anything but "", "0", "no", "false", "none" (ignoring case) is considered to be true. Signed-off-by: Matej Vasek --- pkg/api/handlers/compat/changes.go | 3 +-- pkg/api/handlers/compat/containers.go | 11 +++++----- pkg/api/handlers/compat/containers_archive.go | 2 +- pkg/api/handlers/compat/containers_attach.go | 3 +-- pkg/api/handlers/compat/containers_create.go | 3 +-- pkg/api/handlers/compat/containers_logs.go | 3 +-- pkg/api/handlers/compat/containers_restart.go | 3 +-- pkg/api/handlers/compat/containers_start.go | 3 +-- .../handlers/compat/containers_stats_linux.go | 3 +-- pkg/api/handlers/compat/containers_stop.go | 3 +-- pkg/api/handlers/compat/containers_top.go | 3 +-- pkg/api/handlers/compat/events.go | 3 +-- pkg/api/handlers/compat/images.go | 13 ++++++------ pkg/api/handlers/compat/images_build.go | 3 +-- pkg/api/handlers/compat/images_push.go | 3 +-- pkg/api/handlers/compat/images_remove.go | 3 +-- pkg/api/handlers/compat/images_search.go | 3 +-- pkg/api/handlers/compat/networks.go | 5 ++--- pkg/api/handlers/compat/resize.go | 3 +-- pkg/api/handlers/compat/secrets.go | 3 +-- pkg/api/handlers/compat/volumes.go | 5 ++--- pkg/api/handlers/decoder.go | 13 ++++++++++++ pkg/api/handlers/utils/handler.go | 10 ++++++++++ pkg/api/server/server.go | 1 + pkg/api/types/types.go | 1 + test/apiv2/10-images.at | 5 +++++ test/python/docker/compat/test_containers.py | 20 +++++++++++++++++++ 27 files changed, 82 insertions(+), 52 deletions(-) diff --git a/pkg/api/handlers/compat/changes.go b/pkg/api/handlers/compat/changes.go index c3f612f046..e848591eb3 100644 --- a/pkg/api/handlers/compat/changes.go +++ b/pkg/api/handlers/compat/changes.go @@ -8,11 +8,10 @@ import ( "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" - "github.com/gorilla/schema" ) func Changes(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 997b60d61a..c24a3eb644 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -27,12 +27,11 @@ import ( "github.com/docker/docker/api/types/network" "github.com/docker/go-connections/nat" "github.com/docker/go-units" - "github.com/gorilla/schema" "github.com/sirupsen/logrus" ) func RemoveContainer(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) query := struct { Force bool `schema:"force"` Ignore bool `schema:"ignore"` @@ -99,7 +98,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { func ListContainers(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) query := struct { All bool `schema:"all"` Limit int `schema:"limit"` @@ -179,7 +178,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { func GetContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) query := struct { Size bool `schema:"size"` }{ @@ -208,7 +207,7 @@ func GetContainer(w http.ResponseWriter, r *http.Request) { func KillContainer(w http.ResponseWriter, r *http.Request) { // /{version}/containers/(name)/kill runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) query := struct { Signal string `schema:"signal"` }{ @@ -627,7 +626,7 @@ func formatCapabilities(slice []string) { func RenameContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) name := utils.GetName(r) query := struct { diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go index e319f3f6fd..2250572444 100644 --- a/pkg/api/handlers/compat/containers_archive.go +++ b/pkg/api/handlers/compat/containers_archive.go @@ -21,7 +21,7 @@ import ( ) func Archive(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) switch r.Method { diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go index c37dc09af4..f87857319d 100644 --- a/pkg/api/handlers/compat/containers_attach.go +++ b/pkg/api/handlers/compat/containers_attach.go @@ -10,13 +10,12 @@ import ( "github.com/containers/podman/v4/pkg/api/handlers/utils" "github.com/containers/podman/v4/pkg/api/server/idle" api "github.com/containers/podman/v4/pkg/api/types" - "github.com/gorilla/schema" "github.com/sirupsen/logrus" ) func AttachContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) query := struct { DetachKeys string `schema:"detachKeys"` diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 141ed46f81..19a541b4ec 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -28,12 +28,11 @@ import ( "github.com/containers/podman/v4/pkg/specgenutil" "github.com/containers/storage" "github.com/docker/docker/api/types/mount" - "github.com/gorilla/schema" ) func CreateContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) query := struct { Name string `schema:"name"` Platform string `schema:"platform"` diff --git a/pkg/api/handlers/compat/containers_logs.go b/pkg/api/handlers/compat/containers_logs.go index 77c4fdd2a7..eef2f8cdf8 100644 --- a/pkg/api/handlers/compat/containers_logs.go +++ b/pkg/api/handlers/compat/containers_logs.go @@ -15,12 +15,11 @@ import ( "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" "github.com/containers/podman/v4/pkg/util" - "github.com/gorilla/schema" log "github.com/sirupsen/logrus" ) func LogsFromContainer(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { diff --git a/pkg/api/handlers/compat/containers_restart.go b/pkg/api/handlers/compat/containers_restart.go index d805b95c28..054ff06776 100644 --- a/pkg/api/handlers/compat/containers_restart.go +++ b/pkg/api/handlers/compat/containers_restart.go @@ -11,12 +11,11 @@ import ( api "github.com/containers/podman/v4/pkg/api/types" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/infra/abi" - "github.com/gorilla/schema" ) func RestartContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) // Now use the ABI implementation to prevent us from having duplicate // code. containerEngine := abi.ContainerEngine{Libpod: runtime} diff --git a/pkg/api/handlers/compat/containers_start.go b/pkg/api/handlers/compat/containers_start.go index 0b7f5160a8..67490b90c3 100644 --- a/pkg/api/handlers/compat/containers_start.go +++ b/pkg/api/handlers/compat/containers_start.go @@ -9,11 +9,10 @@ import ( "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/api/handlers/utils" - "github.com/gorilla/schema" ) func StartContainer(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) query := struct { DetachKeys string `schema:"detachKeys"` }{ diff --git a/pkg/api/handlers/compat/containers_stats_linux.go b/pkg/api/handlers/compat/containers_stats_linux.go index f997464331..a8ab35363a 100644 --- a/pkg/api/handlers/compat/containers_stats_linux.go +++ b/pkg/api/handlers/compat/containers_stats_linux.go @@ -13,7 +13,6 @@ import ( api "github.com/containers/podman/v4/pkg/api/types" "github.com/containers/storage/pkg/system" docker "github.com/docker/docker/api/types" - "github.com/gorilla/schema" runccgroups "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/sirupsen/logrus" ) @@ -22,7 +21,7 @@ const DefaultStatsPeriod = 5 * time.Second func StatsContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) query := struct { Stream bool `schema:"stream"` diff --git a/pkg/api/handlers/compat/containers_stop.go b/pkg/api/handlers/compat/containers_stop.go index eeb530001b..52b6f3da1e 100644 --- a/pkg/api/handlers/compat/containers_stop.go +++ b/pkg/api/handlers/compat/containers_stop.go @@ -12,12 +12,11 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/podman/v4/pkg/util" - "github.com/gorilla/schema" ) func StopContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) // Now use the ABI implementation to prevent us from having duplicate // code. containerEngine := abi.ContainerEngine{Libpod: runtime} diff --git a/pkg/api/handlers/compat/containers_top.go b/pkg/api/handlers/compat/containers_top.go index 8da18ae446..b6edd51b41 100644 --- a/pkg/api/handlers/compat/containers_top.go +++ b/pkg/api/handlers/compat/containers_top.go @@ -11,13 +11,12 @@ import ( "github.com/containers/podman/v4/pkg/api/handlers" "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" - "github.com/gorilla/schema" "github.com/sirupsen/logrus" ) func TopContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) psArgs := "-ef" if utils.IsLibpodRequest(r) { diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index 105404a0d0..486ac54656 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -10,7 +10,6 @@ import ( api "github.com/containers/podman/v4/pkg/api/types" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/util" - "github.com/gorilla/schema" jsoniter "github.com/json-iterator/go" "github.com/sirupsen/logrus" ) @@ -19,7 +18,7 @@ import ( func GetEvents(w http.ResponseWriter, r *http.Request) { var ( fromStart bool - decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder = utils.GetDecoder(r) runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) json = jsoniter.ConfigCompatibleWithStandardLibrary // FIXME: this should happen on the package level ) diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index c3f4a0373f..2997eb2169 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -25,7 +25,6 @@ import ( "github.com/containers/storage" "github.com/docker/distribution/registry/api/errcode" "github.com/docker/docker/pkg/jsonmessage" - "github.com/gorilla/schema" "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" ) @@ -95,7 +94,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { } func CommitContainer(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { @@ -174,7 +173,7 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { // 200 no error // 404 repo does not exist or no read access // 500 internal - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { @@ -258,7 +257,7 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { // 200 no error // 404 repo does not exist or no read access // 500 internal - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { @@ -428,7 +427,7 @@ func GetImage(w http.ResponseWriter, r *http.Request) { } func GetImages(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { All bool @@ -489,7 +488,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) { } func LoadImages(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { @@ -547,7 +546,7 @@ func LoadImages(w http.ResponseWriter, r *http.Request) { func ExportImages(w http.ResponseWriter, r *http.Request) { // 200 OK // 500 Error - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 3e18bbc0ee..8c492985a1 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -27,7 +27,6 @@ import ( "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage/pkg/archive" "github.com/docker/docker/pkg/jsonmessage" - "github.com/gorilla/schema" "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" ) @@ -151,7 +150,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { SkipUnusedStages: true, } - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, err) return diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index 8d236db761..f20f22e2c7 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -16,13 +16,12 @@ import ( "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/storage" "github.com/docker/docker/pkg/jsonmessage" - "github.com/gorilla/schema" "github.com/sirupsen/logrus" ) // PushImage is the handler for the compat http endpoint for pushing images. func PushImage(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) // Now use the ABI implementation to prevent us from having duplicate diff --git a/pkg/api/handlers/compat/images_remove.go b/pkg/api/handlers/compat/images_remove.go index 58bdf5f2a0..1a51640a8d 100644 --- a/pkg/api/handlers/compat/images_remove.go +++ b/pkg/api/handlers/compat/images_remove.go @@ -11,11 +11,10 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/storage" - "github.com/gorilla/schema" ) func RemoveImage(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go index efd06fbf0d..02cc7e3521 100644 --- a/pkg/api/handlers/compat/images_search.go +++ b/pkg/api/handlers/compat/images_search.go @@ -12,12 +12,11 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/storage" - "github.com/gorilla/schema" ) func SearchImages(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) query := struct { Term string `json:"term"` Limit int `json:"limit"` diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index 22d2b4bcc9..67d15ad204 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -19,7 +19,6 @@ import ( "github.com/docker/docker/api/types" dockerNetwork "github.com/docker/docker/api/types/network" - "github.com/gorilla/schema" "github.com/sirupsen/logrus" ) @@ -41,7 +40,7 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { }{ scope: "local", } - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return @@ -315,7 +314,7 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) { // This is where you can override the golang default value for one of fields } - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return diff --git a/pkg/api/handlers/compat/resize.go b/pkg/api/handlers/compat/resize.go index a2caf6e350..6139b9e996 100644 --- a/pkg/api/handlers/compat/resize.go +++ b/pkg/api/handlers/compat/resize.go @@ -12,12 +12,11 @@ import ( "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" "github.com/gorilla/mux" - "github.com/gorilla/schema" ) func ResizeTTY(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) // /containers/{id}/resize query := struct { diff --git a/pkg/api/handlers/compat/secrets.go b/pkg/api/handlers/compat/secrets.go index f550143591..1d78c18973 100644 --- a/pkg/api/handlers/compat/secrets.go +++ b/pkg/api/handlers/compat/secrets.go @@ -15,7 +15,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/podman/v4/pkg/util" - "github.com/gorilla/schema" ) func ListSecrets(w http.ResponseWriter, r *http.Request) { @@ -52,7 +51,7 @@ func ListSecrets(w http.ResponseWriter, r *http.Request) { } func InspectSecret(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) names := []string{name} diff --git a/pkg/api/handlers/compat/volumes.go b/pkg/api/handlers/compat/volumes.go index 4ba6f22418..9494933abd 100644 --- a/pkg/api/handlers/compat/volumes.go +++ b/pkg/api/handlers/compat/volumes.go @@ -19,7 +19,6 @@ import ( "github.com/containers/podman/v4/pkg/util" docker_api_types "github.com/docker/docker/api/types" docker_api_types_volume "github.com/docker/docker/api/types/volume" - "github.com/gorilla/schema" ) func ListVolumes(w http.ResponseWriter, r *http.Request) { @@ -85,7 +84,7 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { var ( volumeOptions []libpod.VolumeCreateOption runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder = utils.GetDecoder(r) ) /* No query string data*/ query := struct{}{} @@ -213,7 +212,7 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { func RemoveVolume(w http.ResponseWriter, r *http.Request) { var ( runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) + decoder = utils.GetDecoder(r) ) query := struct { Force bool `schema:"force"` diff --git a/pkg/api/handlers/decoder.go b/pkg/api/handlers/decoder.go index 0696920142..4a636bc817 100644 --- a/pkg/api/handlers/decoder.go +++ b/pkg/api/handlers/decoder.go @@ -3,6 +3,7 @@ package handlers import ( "encoding/json" "reflect" + "strings" "syscall" "time" @@ -28,6 +29,18 @@ func NewAPIDecoder() *schema.Decoder { return d } +func NewCompatAPIDecoder() *schema.Decoder { + dec := NewAPIDecoder() + + // mimic behaviour of github.com/docker/docker/api/server/httputils.BoolValue() + dec.RegisterConverter(true, func(s string) reflect.Value { + s = strings.ToLower(strings.TrimSpace(s)) + return reflect.ValueOf(!(s == "" || s == "0" || s == "no" || s == "false" || s == "none")) + }) + + return dec +} + // On client: // // v := map[string][]string{ diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index f2f8ab1dc8..1e5aa2c12d 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -13,8 +13,11 @@ import ( "github.com/blang/semver/v4" "github.com/containers/podman/v4/version" "github.com/gorilla/mux" + "github.com/gorilla/schema" jsoniter "github.com/json-iterator/go" "github.com/sirupsen/logrus" + + api "github.com/containers/podman/v4/pkg/api/types" ) var ( @@ -188,3 +191,10 @@ func GetVar(r *http.Request, k string) string { func GetName(r *http.Request) string { return GetVar(r, "name") } + +func GetDecoder(r *http.Request) *schema.Decoder { + if IsLibpodRequest(r) { + return r.Context().Value(api.DecoderKey).(*schema.Decoder) + } + return r.Context().Value(api.CompatDecoderKey).(*schema.Decoder) +} diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 14446e6b50..844863a428 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -90,6 +90,7 @@ func newServer(runtime *libpod.Runtime, listener net.Listener, opts entities.Ser server.BaseContext = func(l net.Listener) context.Context { ctx := context.WithValue(context.Background(), types.DecoderKey, handlers.NewAPIDecoder()) + ctx = context.WithValue(ctx, types.CompatDecoderKey, handlers.NewCompatAPIDecoder()) ctx = context.WithValue(ctx, types.RuntimeKey, runtime) ctx = context.WithValue(ctx, types.IdleTrackerKey, tracker) return ctx diff --git a/pkg/api/types/types.go b/pkg/api/types/types.go index 034bae6d4a..a01ded0111 100644 --- a/pkg/api/types/types.go +++ b/pkg/api/types/types.go @@ -7,4 +7,5 @@ const ( RuntimeKey IdleTrackerKey ConnKey + CompatDecoderKey ) diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index 79b2039695..be742b01b9 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -234,6 +234,11 @@ t POST "build?dockerfile=containerfile" $CONTAINERFILE_TAR application/json 200 response_headers=$(cat "$WORKDIR/curl.headers.out") like "$response_headers" ".*application/json.*" "header does not contain application/json" +# Build api response header must contain Content-type: application/json +t POST "build?dockerfile=containerfile&pull=1" $CONTAINERFILE_TAR application/json 200 +response_headers=$(cat "$WORKDIR/curl.headers.out") +like "$response_headers" ".*application/json.*" "header does not contain application/json" + # PR #12091: output from compat API must now include {"aux":{"ID":"sha..."}} t POST "build?dockerfile=containerfile" $CONTAINERFILE_TAR 200 \ '.aux|select(has("ID")).ID~^sha256:[0-9a-f]\{64\}$' diff --git a/test/python/docker/compat/test_containers.py b/test/python/docker/compat/test_containers.py index fab8013443..0595b29123 100644 --- a/test/python/docker/compat/test_containers.py +++ b/test/python/docker/compat/test_containers.py @@ -239,6 +239,26 @@ class TestContainers(common.DockerTestCase): ret, _ = ctr.exec_run(["stat", "/workspace/scratch/test"]) self.assertEqual(ret, 0, "Working directory created if it doesn't exist") + def test_build_pull(self): + dockerfile = ( + b"FROM quay.io/libpod/alpine:latest\n" + b"USER 1000:1000\n" + ) + img: Image + img, logs = self.docker.images.build(fileobj=io.BytesIO(dockerfile), quiet=False, pull=True) + has_tried_pull = False + for e in logs: + if "stream" in e and "trying to pull" in e["stream"].lower(): + has_tried_pull = True + self.assertTrue(has_tried_pull, "the build process has not tried to pull the base image") + + img, logs = self.docker.images.build(fileobj=io.BytesIO(dockerfile), quiet=False, pull=False) + has_tried_pull = False + for e in logs: + if "stream" in e and "trying to pull" in e["stream"].lower(): + has_tried_pull = True + self.assertFalse(has_tried_pull, "the build process has tried tried to pull the base image") + def test_mount_rw_by_default(self): ctr: Optional[Container] = None vol: Optional[Volume] = None