runconfig: ContainerDecoder(): fix handling of invalid JSON

Implement similar logic as is used in httputils.ReadJSON(). Before
this patch, endpoints using the ContainerDecoder would incorrectly
return a 500 (internal server error) status.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2022-04-08 23:27:50 +02:00
parent ff5f70e55f
commit b6d58d749c
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
4 changed files with 41 additions and 21 deletions

View File

@ -17,6 +17,8 @@ func TestContainerInvalidJSON(t *testing.T) {
// POST endpoints that accept / expect a JSON body;
endpoints := []string{
"/commit",
"/containers/create",
"/containers/foobar/exec",
"/containers/foobar/update",
"/exec/foobar/start",
@ -26,7 +28,8 @@ func TestContainerInvalidJSON(t *testing.T) {
if runtime.GOOS != "windows" {
endpoints = append(
endpoints,
"/v1.23/containers/foobar/copy", // deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
"/v1.23/containers/foobar/copy", // deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
"/v1.23/containers/foobar/start", // accepts a body on API < v1.24
)
}

View File

@ -40,7 +40,7 @@ func (r ContainerDecoder) DecodeHostConfig(src io.Reader) (*container.HostConfig
// it's your business to do so
func decodeContainerConfig(src io.Reader, si *sysinfo.SysInfo) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
var w ContainerConfigWrapper
if err := json.NewDecoder(src).Decode(&w); err != nil {
if err := loadJSON(src, &w); err != nil {
return nil, nil, nil, err
}
@ -72,3 +72,18 @@ func decodeContainerConfig(src io.Reader, si *sysinfo.SysInfo) (*container.Confi
}
return w.Config, hc, w.NetworkingConfig, nil
}
// loadJSON is similar to api/server/httputils.ReadJSON()
func loadJSON(src io.Reader, out interface{}) error {
dec := json.NewDecoder(src)
if err := dec.Decode(&out); err != nil {
if err == io.EOF {
return validationError("invalid JSON: got EOF while reading request body")
}
return validationError("invalid JSON: " + err.Error())
}
if dec.More() {
return validationError("unexpected content after JSON")
}
return nil
}

View File

@ -42,27 +42,30 @@ func TestDecodeContainerConfig(t *testing.T) {
}
for _, f := range fixtures {
b, err := os.ReadFile(f.file)
if err != nil {
t.Fatal(err)
}
f := f
t.Run(f.file, func(t *testing.T) {
b, err := os.ReadFile(f.file)
if err != nil {
t.Fatal(err)
}
c, h, _, err := decodeContainerConfig(bytes.NewReader(b), sysinfo.New())
if err != nil {
t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
}
c, h, _, err := decodeContainerConfig(bytes.NewReader(b), sysinfo.New())
if err != nil {
t.Fatal(err)
}
if c.Image != image {
t.Fatalf("Expected %s image, found %s\n", image, c.Image)
}
if c.Image != image {
t.Fatalf("Expected %s image, found %s", image, c.Image)
}
if len(c.Entrypoint) != len(f.entrypoint) {
t.Fatalf("Expected %v, found %v\n", f.entrypoint, c.Entrypoint)
}
if len(c.Entrypoint) != len(f.entrypoint) {
t.Fatalf("Expected %v, found %v", f.entrypoint, c.Entrypoint)
}
if h != nil && h.Memory != 1000 {
t.Fatalf("Expected memory to be 1000, found %d\n", h.Memory)
}
if h != nil && h.Memory != 1000 {
t.Fatalf("Expected memory to be 1000, found %d", h.Memory)
}
})
}
}

View File

@ -1,7 +1,6 @@
package runconfig // import "github.com/docker/docker/runconfig"
import (
"encoding/json"
"io"
"strings"
@ -12,7 +11,7 @@ import (
// It assumes the content of the reader will be JSON, and decodes it.
func decodeHostConfig(src io.Reader) (*container.HostConfig, error) {
var w ContainerConfigWrapper
if err := json.NewDecoder(src).Decode(&w); err != nil {
if err := loadJSON(src, &w); err != nil {
return nil, err
}
return w.getHostConfig(), nil