mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-08-05 00:15:44 +00:00
API Handlers should not error on empty request bodies
This is a Docker compatibility change discovered while working on cases it is practically speaking required because there are mandatory parameters in the body, but in those cases you do not get a JSON decode error back, you get an error about the field that needs to be set. I see no reason for us not to match this convention; it doesn't break our existing bindings, but makes using the API via curl or similar somewhat easier. Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
parent
82e7fc1440
commit
3ac6501fdd
15 changed files with 57 additions and 55 deletions
|
|
@ -3,9 +3,7 @@
|
|||
package compat
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
|
@ -22,9 +20,8 @@ import (
|
|||
|
||||
func Auth(w http.ResponseWriter, r *http.Request) {
|
||||
var authConfig registry.AuthConfig
|
||||
err := json.NewDecoder(r.Body).Decode(&authConfig)
|
||||
if err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to parse request: %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &authConfig); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -766,8 +766,8 @@ func UpdateContainer(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
options := new(container.UpdateConfig)
|
||||
if err := json.NewDecoder(r.Body).Decode(options); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decoding request body: %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &options); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// compatible configuration
|
||||
body := handlers.CreateContainerConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &body); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
package compat
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
|
@ -28,8 +27,8 @@ func ExecCreateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||
|
||||
input := new(handlers.ExecCreateConfig)
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
utils.InternalServerError(w, fmt.Errorf("decoding request body as JSON: %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &input); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -142,11 +141,11 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// TODO: We should read/support Tty from here.
|
||||
bodyParams := new(handlers.ExecStartConfig)
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&bodyParams); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to decode parameters for %s: %w", r.URL.String(), err))
|
||||
if err := utils.ReadJSONFromBody(r, &bodyParams); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Verify TTY setting against what inspect session was made with
|
||||
|
||||
sessionCtr, err := runtime.GetExecSessionContainer(sessionID)
|
||||
|
|
@ -219,9 +218,8 @@ func ExecRemoveHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sessionID := mux.Vars(r)["id"]
|
||||
|
||||
bodyParams := new(handlers.ExecRemoveConfig)
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&bodyParams); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to decode parameters for %s: %w", r.URL.String(), err))
|
||||
if err := utils.ReadJSONFromBody(r, &bodyParams); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
package compat
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
|
@ -210,8 +209,9 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
|
|||
responseWarning string
|
||||
)
|
||||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||
if err := json.NewDecoder(r.Body).Decode(&networkCreate); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err))
|
||||
|
||||
if err := utils.ReadJSONFromBody(r, &networkCreate); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -376,8 +376,8 @@ func Connect(w http.ResponseWriter, r *http.Request) {
|
|||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||
|
||||
var netConnect dockerNetwork.ConnectRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &netConnect); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -441,8 +441,8 @@ func Disconnect(w http.ResponseWriter, r *http.Request) {
|
|||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||
|
||||
var netDisconnect dockerNetwork.DisconnectRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&netDisconnect); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &netDisconnect); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ package compat
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
|
@ -123,8 +122,8 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) {
|
|||
Labels map[string]string `schema:"labels"`
|
||||
}{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&createParams); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &createParams); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package compat
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
|
@ -108,8 +107,8 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
// decode params from body
|
||||
input := client.VolumeCreateOptions{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &input); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -496,8 +496,8 @@ func UpdateContainer(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
wire := updateEntitiesWire{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&wire); err != nil {
|
||||
utils.InternalServerError(w, fmt.Errorf("decode(): %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &wire); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
package libpod
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
|
@ -62,8 +61,8 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
|
|||
},
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&wire); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &wire); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -212,8 +212,8 @@ func ManifestAddV3(w http.ResponseWriter, r *http.Request) {
|
|||
entities.ManifestAddOptions
|
||||
TLSVerify bool `schema:"tlsVerify"`
|
||||
}{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&query); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decoding AddV3 query: %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &query); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -495,8 +495,8 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
multireader = nil
|
||||
// not multipart - request is just encoded JSON, nothing else
|
||||
if err := json.NewDecoder(r.Body).Decode(body); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decoding modify request: %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &body); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
package libpod
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
|
@ -27,8 +26,8 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||
network := types.Network{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&network); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to decode request JSON payload: %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &network); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -59,8 +58,8 @@ func UpdateNetwork(w http.ResponseWriter, r *http.Request) {
|
|||
ic := abi.ContainerEngine{Libpod: runtime}
|
||||
|
||||
networkUpdateOptions := entities.NetworkUpdateOptions{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&networkUpdateOptions); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to decode request JSON payload: %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &networkUpdateOptions); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -174,8 +173,8 @@ func Connect(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||
var netConnect entities.NetworkConnectOptions
|
||||
if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to decode request JSON payload: %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &netConnect); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
name := utils.GetName(r)
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
|
|||
err error
|
||||
)
|
||||
psg := specgen.PodSpecGenerator{InfraContainerSpec: &specgen.SpecGenerator{}}
|
||||
if err = json.NewDecoder(r.Body).Decode(&psg); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("%v: %w", failedToDecodeSpecgen, err))
|
||||
if err := utils.ReadJSONFromBody(r, &psg); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
package libpod
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
|
|
@ -39,9 +38,8 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
input := entities.VolumeCreateOptions{}
|
||||
// decode params from body
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err))
|
||||
if err := utils.ReadJSONFromBody(r, &input); err != nil {
|
||||
utils.Error(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -152,6 +152,19 @@ func MarshalErrorSliceJSONIsEmpty(ptr unsafe.Pointer) bool {
|
|||
return len(*((*[]error)(ptr))) == 0
|
||||
}
|
||||
|
||||
// ReadJSONFromBody reads JSON from a request body into the given struct.
|
||||
// If the body is not provided/is empty, this returns immediately with no modification to the given struct.
|
||||
// This means that defaults will NOT be overwritten in this case.
|
||||
func ReadJSONFromBody(r *http.Request, unmarshalTo any) error {
|
||||
if r.Body == nil || r.ContentLength == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(unmarshalTo); err != nil {
|
||||
return fmt.Errorf("decoding request body as JSON: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteJSON writes an interface value encoded as JSON to w
|
||||
func WriteJSON(w http.ResponseWriter, code int, value any) {
|
||||
// FIXME: we don't need to write the header in all/some circumstances.
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@ t POST libpod/networks/create name="network4" \
|
|||
.labels.zaq=val
|
||||
|
||||
# test for empty mask
|
||||
t POST libpod/networks/create subnets='[{"subnet":"10.10.134.0"}]' 500 \
|
||||
t POST libpod/networks/create subnets='[{"subnet":"10.10.134.0"}]' 400 \
|
||||
.cause~'.*invalid CIDR address: 10.10.134.0'
|
||||
# test for invalid mask
|
||||
t POST libpod/networks/create subnets='[{"subnet":"10.10.134.0/65"}]' 500 \
|
||||
t POST libpod/networks/create subnets='[{"subnet":"10.10.134.0/65"}]' 400 \
|
||||
.cause~'.*invalid CIDR address: 10.10.134.0/65'
|
||||
|
||||
# network list
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue