diff --git a/api/server/server.go b/api/server/server.go index e8f66de31e..e626f269b2 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -10,6 +10,7 @@ import ( "github.com/docker/docker/api/server/middleware" "github.com/docker/docker/api/server/router" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/versions" "github.com/docker/docker/dockerversion" "github.com/docker/docker/internal/otelutil" "github.com/gorilla/mux" @@ -62,9 +63,20 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc, operation string) ht if statusCode >= 500 { log.G(ctx).Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err) } - _ = httputils.WriteJSON(w, statusCode, &types.ErrorResponse{ - Message: err.Error(), - }) + // While we no longer support API versions older 1.24 [api.MinSupportedAPIVersion], + // a client may try to connect using an older version and expect a plain-text error + // instead of a JSON error. This would result in an "API version too old" error + // formatted in JSON being printed as-is. + // + // Let's be nice, and return errors in plain-text to provide a more readable error + // to help the user understand the API version they're using is no longer supported. + if v := vars["version"]; v != "" && versions.LessThan(v, "1.24") { + http.Error(w, err.Error(), statusCode) + } else { + _ = httputils.WriteJSON(w, statusCode, &types.ErrorResponse{ + Message: err.Error(), + }) + } } }), operation).ServeHTTP } diff --git a/client/request.go b/client/request.go index 2b913aab64..4cc64350bd 100644 --- a/client/request.go +++ b/client/request.go @@ -237,7 +237,7 @@ func (cli *Client) checkResponseErr(serverResp *http.Response) (retErr error) { } var daemonErr error - if serverResp.Header.Get("Content-Type") == "application/json" && (cli.version == "" || versions.GreaterThan(cli.version, "1.23")) { + if serverResp.Header.Get("Content-Type") == "application/json" { var errorResponse types.ErrorResponse if err := json.Unmarshal(body, &errorResponse); err != nil { return errors.Wrap(err, "Error reading JSON") diff --git a/client/request_test.go b/client/request_test.go index 71a1bb6866..39b7439a34 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -132,12 +132,15 @@ func TestResponseErrors(t *testing.T) { expected: `Error response from daemon: Some error occurred`, }, { - // API versions before 1.24 did not support JSON errors, and return response as-is. + // API versions before 1.24 did not support JSON errors. Technically, + // we no longer downgrade to older API versions, but we make an + // exception for errors so that older clients would print a more + // readable error. doc: "JSON error on old API", apiVersion: "1.23", - contentType: "application/json", - response: `{"message":"Some error occurred"}`, - expected: `Error response from daemon: {"message":"Some error occurred"}`, + contentType: "text/plain; charset=utf-8", + response: `client version 1.10 is too old. Minimum supported API version is 1.24, please upgrade your client to a newer version`, + expected: `Error response from daemon: client version 1.10 is too old. Minimum supported API version is 1.24, please upgrade your client to a newer version`, }, { doc: "plain-text error", diff --git a/integration-cli/docker_api_test.go b/integration-cli/docker_api_test.go index f4ecbce09c..7afa9f0c71 100644 --- a/integration-cli/docker_api_test.go +++ b/integration-cli/docker_api_test.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "context" "fmt" "net/http" @@ -9,6 +10,7 @@ import ( "strings" "testing" + "github.com/docker/docker/api/types/versions" "github.com/docker/docker/runconfig" "github.com/docker/docker/testutil" "github.com/docker/docker/testutil/request" @@ -63,7 +65,11 @@ func (s *DockerAPISuite) TestAPIClientVersionOldNotSupported(c *testing.T) { expected := fmt.Sprintf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", version, testEnv.DaemonVersion.MinAPIVersion) b, err := request.ReadBody(body) assert.NilError(c, err) - assert.Equal(c, getErrorMessage(c, b), expected) + errMessage := string(bytes.TrimSpace(b)) + if versions.GreaterThanOrEqualTo(version, "1.24") { + errMessage = getErrorMessage(c, b) + } + assert.Equal(c, errMessage, expected) } func (s *DockerAPISuite) TestAPIErrorJSON(c *testing.T) {