mirror of https://github.com/docker/docs.git
				
				
				
			Return remote API errors as JSON
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
This commit is contained in:
		
							parent
							
								
									f6ff9acc63
								
							
						
					
					
						commit
						322e2a7d05
					
				|  | @ -5,6 +5,9 @@ import ( | |||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| 	"github.com/docker/engine-api/types" | ||||
| 	"github.com/docker/engine-api/types/versions" | ||||
| 	"github.com/gorilla/mux" | ||||
| ) | ||||
| 
 | ||||
| // httpStatusError is an interface
 | ||||
|  | @ -70,13 +73,19 @@ func GetHTTPErrorStatusCode(err error) int { | |||
| 	return statusCode | ||||
| } | ||||
| 
 | ||||
| // WriteError decodes a specific docker error and sends it in the response.
 | ||||
| func WriteError(w http.ResponseWriter, err error) { | ||||
| 	if err == nil || w == nil { | ||||
| 		logrus.WithFields(logrus.Fields{"error": err, "writer": w}).Error("unexpected HTTP error handling") | ||||
| 		return | ||||
| // MakeErrorHandler makes an HTTP handler that decodes a Docker error and
 | ||||
| // returns it in the response.
 | ||||
| func MakeErrorHandler(err error) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		statusCode := GetHTTPErrorStatusCode(err) | ||||
| 		vars := mux.Vars(r) | ||||
| 		if vars["version"] == "" || versions.GreaterThan(vars["version"], "1.23") { | ||||
| 			response := &types.ErrorResponse{ | ||||
| 				Message: err.Error(), | ||||
| 			} | ||||
| 			WriteJSON(w, statusCode, response) | ||||
| 		} else { | ||||
| 			http.Error(w, err.Error(), statusCode) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	statusCode := GetHTTPErrorStatusCode(err) | ||||
| 	http.Error(w, err.Error(), statusCode) | ||||
| } | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ package server | |||
| 
 | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | @ -10,6 +11,7 @@ import ( | |||
| 	"github.com/docker/docker/api/server/httputils" | ||||
| 	"github.com/docker/docker/api/server/middleware" | ||||
| 	"github.com/docker/docker/api/server/router" | ||||
| 	"github.com/docker/docker/errors" | ||||
| 	"github.com/gorilla/mux" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | @ -136,7 +138,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc { | |||
| 
 | ||||
| 		if err := handlerFunc(ctx, w, r, vars); err != nil { | ||||
| 			logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err) | ||||
| 			httputils.WriteError(w, err) | ||||
| 			httputils.MakeErrorHandler(err)(w, r) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -172,6 +174,11 @@ func (s *Server) createMux() *mux.Router { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	err := errors.NewRequestNotFoundError(fmt.Errorf("page not found")) | ||||
| 	notFoundHandler := httputils.MakeErrorHandler(err) | ||||
| 	m.HandleFunc(versionMatcher+"/{path:.*}", notFoundHandler) | ||||
| 	m.NotFoundHandler = notFoundHandler | ||||
| 
 | ||||
| 	return m | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -131,6 +131,7 @@ This section lists each version from latest to oldest.  Each listing includes a | |||
| * `POST /images/(name)/tag` no longer has a `force` query parameter. | ||||
| * `GET /images/search` now supports maximum returned search results `limit`. | ||||
| * `POST /containers/{name:.*}/copy` is now removed and errors out starting from this API version. | ||||
| * API errors are now returned as JSON instead of plain text. | ||||
| 
 | ||||
| ### v1.23 API changes | ||||
| 
 | ||||
|  | @ -262,4 +263,3 @@ end point now returns the new boolean fields `CpuCfsPeriod`, `CpuCfsQuota`, and | |||
| * `CgroupParent` can be passed in the host config to setup container cgroups under a specific cgroup. | ||||
| * `POST /build` closing the HTTP request cancels the build | ||||
| * `POST /containers/(id)/exec` includes `Warnings` field to response. | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,9 +22,19 @@ weight=-5 | |||
|  - When the client API version is newer than the daemon's, these calls return an HTTP | ||||
|    `400 Bad Request` error message. | ||||
| 
 | ||||
| # 2. Endpoints | ||||
| # 2. Errors | ||||
| 
 | ||||
| ## 2.1 Containers | ||||
| The Remote API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: | ||||
| 
 | ||||
|     { | ||||
|         "message": "page not found" | ||||
|     } | ||||
| 
 | ||||
| The status codes that are returned for each endpoint are specified in the endpoint documentation below. | ||||
| 
 | ||||
| # 3. Endpoints | ||||
| 
 | ||||
| ## 3.1 Containers | ||||
| 
 | ||||
| ### List containers | ||||
| 
 | ||||
|  | @ -1504,7 +1514,7 @@ Status Codes: | |||
|     - no such file or directory (**path** resource does not exist) | ||||
| - **500** – server error | ||||
| 
 | ||||
| ## 2.2 Images | ||||
| ## 3.2 Images | ||||
| 
 | ||||
| ### List Images | ||||
| 
 | ||||
|  | @ -2112,7 +2122,7 @@ Status Codes: | |||
| -   **200** – no error | ||||
| -   **500** – server error | ||||
| 
 | ||||
| ## 2.3 Misc | ||||
| ## 3.3 Misc | ||||
| 
 | ||||
| ### Check auth configuration | ||||
| 
 | ||||
|  | @ -2834,7 +2844,7 @@ Status Codes: | |||
| -   **404** – no such exec instance | ||||
| -   **500** - server error | ||||
| 
 | ||||
| ## 2.4 Volumes | ||||
| ## 3.4 Volumes | ||||
| 
 | ||||
| ### List volumes | ||||
| 
 | ||||
|  | @ -2972,7 +2982,7 @@ Status Codes | |||
| -   **409** - volume is in use and cannot be removed | ||||
| -   **500** - server error | ||||
| 
 | ||||
| ## 2.5 Networks | ||||
| ## 3.5 Networks | ||||
| 
 | ||||
| ### List networks | ||||
| 
 | ||||
|  | @ -3296,9 +3306,9 @@ Status Codes | |||
| -   **404** - no such network | ||||
| -   **500** - server error | ||||
| 
 | ||||
| # 3. Going further | ||||
| # 4. Going further | ||||
| 
 | ||||
| ## 3.1 Inside `docker run` | ||||
| ## 4.1 Inside `docker run` | ||||
| 
 | ||||
| As an example, the `docker run` command line makes the following API calls: | ||||
| 
 | ||||
|  | @ -3316,7 +3326,7 @@ As an example, the `docker run` command line makes the following API calls: | |||
| 
 | ||||
| - If in detached mode or only `stdin` is attached, display the container's id. | ||||
| 
 | ||||
| ## 3.2 Hijacking | ||||
| ## 4.2 Hijacking | ||||
| 
 | ||||
| In this version of the API, `/attach`, uses hijacking to transport `stdin`, | ||||
| `stdout`, and `stderr` on the same socket. | ||||
|  | @ -3331,7 +3341,7 @@ When Docker daemon detects the `Upgrade` header, it switches its status code | |||
| from **200 OK** to **101 UPGRADED** and resends the same headers. | ||||
| 
 | ||||
| 
 | ||||
| ## 3.3 CORS Requests | ||||
| ## 4.3 CORS Requests | ||||
| 
 | ||||
| To set cross origin requests to the remote api please give values to | ||||
| `--api-cors-header` when running Docker in daemon mode. Set * (asterisk) allows all, | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue