Return remote API errors as JSON

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
Upstream-commit: 322e2a7d059a81617b593cf6ece2cfd9f6d4ea03
Component: engine
This commit is contained in:
Ben Firshman
2016-06-07 18:45:27 -07:00
parent 9504e2f68b
commit ecda48bde7
13 changed files with 135 additions and 59 deletions
@@ -85,8 +85,8 @@ func (s *DockerSuite) TestGetContainersWsAttachContainerNotFound(c *check.C) {
status, body, err := sockRequest("GET", "/containers/doesnotexist/attach/ws", nil)
c.Assert(status, checker.Equals, http.StatusNotFound)
c.Assert(err, checker.IsNil)
expected := "No such container: doesnotexist\n"
c.Assert(string(body), checker.Contains, expected)
expected := "No such container: doesnotexist"
c.Assert(getErrorMessage(c, body), checker.Contains, expected)
}
func (s *DockerSuite) TestPostContainersAttach(c *check.C) {
@@ -16,9 +16,10 @@ func (s *DockerSuite) TestAuthApi(c *check.C) {
Password: "no-password",
}
expected := "Get https://registry-1.docker.io/v2/: unauthorized: incorrect username or password\n"
expected := "Get https://registry-1.docker.io/v2/: unauthorized: incorrect username or password"
status, body, err := sockRequest("POST", "/auth", config)
c.Assert(err, check.IsNil)
c.Assert(status, check.Equals, http.StatusUnauthorized)
c.Assert(string(body), checker.Contains, expected, check.Commentf("Expected: %v, got: %v", expected, string(body)))
msg := getErrorMessage(c, body)
c.Assert(msg, checker.Contains, expected, check.Commentf("Expected: %v, got: %v", expected, msg))
}
@@ -480,10 +480,10 @@ func (s *DockerSuite) TestContainerApiBadPort(c *check.C) {
jsonData := bytes.NewBuffer(nil)
json.NewEncoder(jsonData).Encode(config)
status, b, err := sockRequest("POST", "/containers/create", config)
status, body, err := sockRequest("POST", "/containers/create", config)
c.Assert(err, checker.IsNil)
c.Assert(status, checker.Equals, http.StatusInternalServerError)
c.Assert(strings.TrimSpace(string(b)), checker.Equals, `Invalid port specification: "aa80"`, check.Commentf("Incorrect error msg: %s", string(b)))
c.Assert(getErrorMessage(c, body), checker.Equals, `Invalid port specification: "aa80"`, check.Commentf("Incorrect error msg: %s", body))
}
func (s *DockerSuite) TestContainerApiCreate(c *check.C) {
@@ -509,12 +509,12 @@ func (s *DockerSuite) TestContainerApiCreate(c *check.C) {
func (s *DockerSuite) TestContainerApiCreateEmptyConfig(c *check.C) {
config := map[string]interface{}{}
status, b, err := sockRequest("POST", "/containers/create", config)
status, body, err := sockRequest("POST", "/containers/create", config)
c.Assert(err, checker.IsNil)
c.Assert(status, checker.Equals, http.StatusInternalServerError)
expected := "Config cannot be empty in order to create a container\n"
c.Assert(string(b), checker.Equals, expected)
expected := "Config cannot be empty in order to create a container"
c.Assert(getErrorMessage(c, body), checker.Equals, expected)
}
func (s *DockerSuite) TestContainerApiCreateMultipleNetworksConfig(c *check.C) {
@@ -530,14 +530,15 @@ func (s *DockerSuite) TestContainerApiCreateMultipleNetworksConfig(c *check.C) {
},
}
status, b, err := sockRequest("POST", "/containers/create", config)
status, body, err := sockRequest("POST", "/containers/create", config)
c.Assert(err, checker.IsNil)
c.Assert(status, checker.Equals, http.StatusBadRequest)
msg := getErrorMessage(c, body)
// network name order in error message is not deterministic
c.Assert(string(b), checker.Contains, "Container cannot be connected to network endpoints")
c.Assert(string(b), checker.Contains, "net1")
c.Assert(string(b), checker.Contains, "net2")
c.Assert(string(b), checker.Contains, "net3")
c.Assert(msg, checker.Contains, "Container cannot be connected to network endpoints")
c.Assert(msg, checker.Contains, "net1")
c.Assert(msg, checker.Contains, "net2")
c.Assert(msg, checker.Contains, "net3")
}
func (s *DockerSuite) TestContainerApiCreateWithHostName(c *check.C) {
@@ -997,7 +998,7 @@ func (s *DockerSuite) TestContainerApiDeleteNotExist(c *check.C) {
status, body, err := sockRequest("DELETE", "/containers/doesnotexist", nil)
c.Assert(err, checker.IsNil)
c.Assert(status, checker.Equals, http.StatusNotFound)
c.Assert(string(body), checker.Matches, "No such container: doesnotexist\n")
c.Assert(getErrorMessage(c, body), checker.Matches, "No such container: doesnotexist")
}
func (s *DockerSuite) TestContainerApiDeleteForce(c *check.C) {
@@ -1247,8 +1248,8 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C)
status, body, err := sockRequest("POST", "/containers/create?name="+name, c1)
c.Assert(err, checker.IsNil)
c.Assert(status, checker.Equals, http.StatusInternalServerError)
expected := "Invalid value 1-42,, for cpuset cpus\n"
c.Assert(string(body), checker.Equals, expected)
expected := "Invalid value 1-42,, for cpuset cpus"
c.Assert(getErrorMessage(c, body), checker.Equals, expected)
c2 := struct {
Image string
@@ -1258,8 +1259,8 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C)
status, body, err = sockRequest("POST", "/containers/create?name="+name, c2)
c.Assert(err, checker.IsNil)
c.Assert(status, checker.Equals, http.StatusInternalServerError)
expected = "Invalid value 42-3,1-- for cpuset mems\n"
c.Assert(string(body), checker.Equals, expected)
expected = "Invalid value 42-3,1-- for cpuset mems"
c.Assert(getErrorMessage(c, body), checker.Equals, expected)
}
func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
@@ -1273,7 +1274,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
status, body, err := sockRequest("POST", "/containers/create", config)
c.Assert(err, check.IsNil)
c.Assert(status, check.Equals, http.StatusInternalServerError)
c.Assert(string(body), checker.Contains, "SHM size must be greater than 0")
c.Assert(getErrorMessage(c, body), checker.Contains, "SHM size must be greater than 0")
}
func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) {
@@ -1409,9 +1410,11 @@ func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *che
status, b, err := sockRequest("POST", "/containers/create?name="+name, config)
c.Assert(err, check.IsNil)
c.Assert(status, check.Equals, http.StatusInternalServerError)
expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
if !strings.Contains(string(b), expected) {
c.Fatalf("Expected output to contain %q, got %q", expected, string(b))
msg := getErrorMessage(c, b)
if !strings.Contains(msg, expected) {
c.Fatalf("Expected output to contain %q, got %q", expected, msg)
}
config = struct {
@@ -1423,8 +1426,9 @@ func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *che
c.Assert(err, check.IsNil)
c.Assert(status, check.Equals, http.StatusInternalServerError)
expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
if !strings.Contains(string(b), expected) {
c.Fatalf("Expected output to contain %q, got %q", expected, string(b))
msg = getErrorMessage(c, b)
if !strings.Contains(msg, expected) {
c.Fatalf("Expected output to contain %q, got %q", expected, msg)
}
}
@@ -2,7 +2,6 @@ package main
import (
"net/http"
"strings"
"github.com/docker/docker/pkg/integration/checker"
"github.com/go-check/check"
@@ -15,31 +14,31 @@ func (s *DockerSuite) TestApiCreateWithNotExistImage(c *check.C) {
"Volumes": map[string]struct{}{"/tmp": {}},
}
status, resp, err := sockRequest("POST", "/containers/create?name="+name, config)
status, body, err := sockRequest("POST", "/containers/create?name="+name, config)
c.Assert(err, check.IsNil)
c.Assert(status, check.Equals, http.StatusNotFound)
expected := "No such image: test456:v1"
c.Assert(strings.TrimSpace(string(resp)), checker.Contains, expected)
c.Assert(getErrorMessage(c, body), checker.Contains, expected)
config2 := map[string]interface{}{
"Image": "test456",
"Volumes": map[string]struct{}{"/tmp": {}},
}
status, resp, err = sockRequest("POST", "/containers/create?name="+name, config2)
status, body, err = sockRequest("POST", "/containers/create?name="+name, config2)
c.Assert(err, check.IsNil)
c.Assert(status, check.Equals, http.StatusNotFound)
expected = "No such image: test456:latest"
c.Assert(strings.TrimSpace(string(resp)), checker.Equals, expected)
c.Assert(getErrorMessage(c, body), checker.Equals, expected)
config3 := map[string]interface{}{
"Image": "sha256:0cb40641836c461bc97c793971d84d758371ed682042457523e4ae701efeaaaa",
}
status, resp, err = sockRequest("POST", "/containers/create?name="+name, config3)
status, body, err = sockRequest("POST", "/containers/create?name="+name, config3)
c.Assert(err, check.IsNil)
c.Assert(status, check.Equals, http.StatusNotFound)
expected = "No such image: sha256:0cb40641836c461bc97c793971d84d758371ed682042457523e4ae701efeaaaa"
c.Assert(strings.TrimSpace(string(resp)), checker.Equals, expected)
c.Assert(getErrorMessage(c, body), checker.Equals, expected)
}
@@ -24,7 +24,7 @@ func (s *DockerSuite) TestExecApiCreateNoCmd(c *check.C) {
c.Assert(status, checker.Equals, http.StatusInternalServerError)
comment := check.Commentf("Expected message when creating exec command with no Cmd specified")
c.Assert(string(body), checker.Contains, "No exec command specified", comment)
c.Assert(getErrorMessage(c, body), checker.Contains, "No exec command specified", comment)
}
func (s *DockerSuite) TestExecApiCreateNoValidContentType(c *check.C) {
@@ -44,7 +44,7 @@ func (s *DockerSuite) TestExecApiCreateNoValidContentType(c *check.C) {
c.Assert(err, checker.IsNil)
comment := check.Commentf("Expected message when creating exec command with invalid Content-Type specified")
c.Assert(string(b), checker.Contains, "Content-Type specified", comment)
c.Assert(getErrorMessage(c, b), checker.Contains, "Content-Type specified", comment)
}
func (s *DockerSuite) TestExecApiCreateContainerPaused(c *check.C) {
@@ -59,7 +59,7 @@ func (s *DockerSuite) TestExecApiCreateContainerPaused(c *check.C) {
c.Assert(status, checker.Equals, http.StatusConflict)
comment := check.Commentf("Expected message when creating exec command with Container %s is paused", name)
c.Assert(string(body), checker.Contains, "Container "+name+" is paused, unpause the container before exec", comment)
c.Assert(getErrorMessage(c, body), checker.Contains, "Container "+name+" is paused, unpause the container before exec", comment)
}
func (s *DockerSuite) TestExecApiStart(c *check.C) {
@@ -60,9 +60,7 @@ func (s *DockerSuite) TestLogsApiNoStdoutNorStderr(c *check.C) {
c.Assert(err, checker.IsNil)
expected := "Bad parameters: you must choose at least one stream"
if !bytes.Contains(body, []byte(expected)) {
c.Fatalf("Expected %s, got %s", expected, string(body[:]))
}
c.Assert(getErrorMessage(c, body), checker.Contains, expected)
}
// Regression test for #12704
@@ -40,5 +40,5 @@ func (s *DockerSuite) TestResizeApiResponseWhenContainerNotStarted(c *check.C) {
c.Assert(status, check.Equals, http.StatusInternalServerError)
c.Assert(err, check.IsNil)
c.Assert(string(body), checker.Contains, "is not running", check.Commentf("resize should fail with message 'Container is not running'"))
c.Assert(getErrorMessage(c, body), checker.Contains, "is not running", check.Commentf("resize should fail with message 'Container is not running'"))
}
@@ -60,7 +60,7 @@ func (s *DockerSuite) TestApiClientVersionNewerThanServer(c *check.C) {
c.Assert(err, checker.IsNil)
c.Assert(status, checker.Equals, http.StatusBadRequest)
expected := fmt.Sprintf("client is newer than server (client API version: %s, server API version: %s)", version, api.DefaultVersion)
c.Assert(strings.TrimSpace(string(body)), checker.Equals, expected)
c.Assert(getErrorMessage(c, body), checker.Equals, expected)
}
func (s *DockerSuite) TestApiClientVersionOldNotSupported(c *check.C) {
@@ -99,3 +99,44 @@ func (s *DockerSuite) TestApiDockerApiVersion(c *check.C) {
c.Fatalf("Out didn't have 'xxx' for the API version, had:\n%s", out)
}
}
func (s *DockerSuite) TestApiErrorJSON(c *check.C) {
httpResp, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(`{}`), "application/json")
c.Assert(err, checker.IsNil)
c.Assert(httpResp.StatusCode, checker.Equals, http.StatusInternalServerError)
c.Assert(httpResp.Header.Get("Content-Type"), checker.Equals, "application/json")
b, err := readBody(body)
c.Assert(err, checker.IsNil)
c.Assert(getErrorMessage(c, b), checker.Equals, "Config cannot be empty in order to create a container")
}
func (s *DockerSuite) TestApiErrorPlainText(c *check.C) {
httpResp, body, err := sockRequestRaw("POST", "/v1.23/containers/create", strings.NewReader(`{}`), "application/json")
c.Assert(err, checker.IsNil)
c.Assert(httpResp.StatusCode, checker.Equals, http.StatusInternalServerError)
c.Assert(httpResp.Header.Get("Content-Type"), checker.Contains, "text/plain")
b, err := readBody(body)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(string(b)), checker.Equals, "Config cannot be empty in order to create a container")
}
func (s *DockerSuite) TestApiErrorNotFoundJSON(c *check.C) {
// 404 is a different code path to normal errors, so test separately
httpResp, body, err := sockRequestRaw("GET", "/notfound", nil, "application/json")
c.Assert(err, checker.IsNil)
c.Assert(httpResp.StatusCode, checker.Equals, http.StatusNotFound)
c.Assert(httpResp.Header.Get("Content-Type"), checker.Equals, "application/json")
b, err := readBody(body)
c.Assert(err, checker.IsNil)
c.Assert(getErrorMessage(c, b), checker.Equals, "page not found")
}
func (s *DockerSuite) TestApiErrorNotFoundPlainText(c *check.C) {
httpResp, body, err := sockRequestRaw("GET", "/v1.23/notfound", nil, "application/json")
c.Assert(err, checker.IsNil)
c.Assert(httpResp.StatusCode, checker.Equals, http.StatusNotFound)
c.Assert(httpResp.Header.Get("Content-Type"), checker.Contains, "text/plain")
b, err := readBody(body)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(string(b)), checker.Equals, "page not found")
}
@@ -1507,3 +1507,10 @@ func waitForGoroutines(expected int) error {
}
}
}
// getErrorMessage returns the error message from an error API response
func getErrorMessage(c *check.C, body []byte) string {
var resp types.ErrorResponse
c.Assert(json.Unmarshal(body, &resp), check.IsNil)
return strings.TrimSpace(resp.Message)
}