api, client: produce human-readable errors for unsupported API versions (< v1.24)
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
"github.com/docker/docker/api/server/middleware" |
| 11 | 11 |
"github.com/docker/docker/api/server/router" |
| 12 | 12 |
"github.com/docker/docker/api/types" |
| 13 |
+ "github.com/docker/docker/api/types/versions" |
|
| 13 | 14 |
"github.com/docker/docker/dockerversion" |
| 14 | 15 |
"github.com/docker/docker/internal/otelutil" |
| 15 | 16 |
"github.com/gorilla/mux" |
| ... | ... |
@@ -62,9 +63,20 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc, operation string) ht |
| 62 | 62 |
if statusCode >= 500 {
|
| 63 | 63 |
log.G(ctx).Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
|
| 64 | 64 |
} |
| 65 |
- _ = httputils.WriteJSON(w, statusCode, &types.ErrorResponse{
|
|
| 66 |
- Message: err.Error(), |
|
| 67 |
- }) |
|
| 65 |
+ // While we no longer support API versions older 1.24 [api.MinSupportedAPIVersion], |
|
| 66 |
+ // a client may try to connect using an older version and expect a plain-text error |
|
| 67 |
+ // instead of a JSON error. This would result in an "API version too old" error |
|
| 68 |
+ // formatted in JSON being printed as-is. |
|
| 69 |
+ // |
|
| 70 |
+ // Let's be nice, and return errors in plain-text to provide a more readable error |
|
| 71 |
+ // to help the user understand the API version they're using is no longer supported. |
|
| 72 |
+ if v := vars["version"]; v != "" && versions.LessThan(v, "1.24") {
|
|
| 73 |
+ http.Error(w, err.Error(), statusCode) |
|
| 74 |
+ } else {
|
|
| 75 |
+ _ = httputils.WriteJSON(w, statusCode, &types.ErrorResponse{
|
|
| 76 |
+ Message: err.Error(), |
|
| 77 |
+ }) |
|
| 78 |
+ } |
|
| 68 | 79 |
} |
| 69 | 80 |
}), operation).ServeHTTP |
| 70 | 81 |
} |
| ... | ... |
@@ -237,7 +237,7 @@ func (cli *Client) checkResponseErr(serverResp *http.Response) (retErr error) {
|
| 237 | 237 |
} |
| 238 | 238 |
|
| 239 | 239 |
var daemonErr error |
| 240 |
- if serverResp.Header.Get("Content-Type") == "application/json" && (cli.version == "" || versions.GreaterThan(cli.version, "1.23")) {
|
|
| 240 |
+ if serverResp.Header.Get("Content-Type") == "application/json" {
|
|
| 241 | 241 |
var errorResponse types.ErrorResponse |
| 242 | 242 |
if err := json.Unmarshal(body, &errorResponse); err != nil {
|
| 243 | 243 |
return errors.Wrap(err, "Error reading JSON") |
| ... | ... |
@@ -132,12 +132,15 @@ func TestResponseErrors(t *testing.T) {
|
| 132 | 132 |
expected: `Error response from daemon: Some error occurred`, |
| 133 | 133 |
}, |
| 134 | 134 |
{
|
| 135 |
- // API versions before 1.24 did not support JSON errors, and return response as-is. |
|
| 135 |
+ // API versions before 1.24 did not support JSON errors. Technically, |
|
| 136 |
+ // we no longer downgrade to older API versions, but we make an |
|
| 137 |
+ // exception for errors so that older clients would print a more |
|
| 138 |
+ // readable error. |
|
| 136 | 139 |
doc: "JSON error on old API", |
| 137 | 140 |
apiVersion: "1.23", |
| 138 |
- contentType: "application/json", |
|
| 139 |
- response: `{"message":"Some error occurred"}`,
|
|
| 140 |
- expected: `Error response from daemon: {"message":"Some error occurred"}`,
|
|
| 141 |
+ contentType: "text/plain; charset=utf-8", |
|
| 142 |
+ response: `client version 1.10 is too old. Minimum supported API version is 1.24, please upgrade your client to a newer version`, |
|
| 143 |
+ 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`, |
|
| 141 | 144 |
}, |
| 142 | 145 |
{
|
| 143 | 146 |
doc: "plain-text error", |
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 | 1 |
package main |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "bytes" |
|
| 4 | 5 |
"context" |
| 5 | 6 |
"fmt" |
| 6 | 7 |
"net/http" |
| ... | ... |
@@ -9,6 +10,7 @@ import ( |
| 9 | 9 |
"strings" |
| 10 | 10 |
"testing" |
| 11 | 11 |
|
| 12 |
+ "github.com/docker/docker/api/types/versions" |
|
| 12 | 13 |
"github.com/docker/docker/runconfig" |
| 13 | 14 |
"github.com/docker/docker/testutil" |
| 14 | 15 |
"github.com/docker/docker/testutil/request" |
| ... | ... |
@@ -63,7 +65,11 @@ func (s *DockerAPISuite) TestAPIClientVersionOldNotSupported(c *testing.T) {
|
| 63 | 63 |
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)
|
| 64 | 64 |
b, err := request.ReadBody(body) |
| 65 | 65 |
assert.NilError(c, err) |
| 66 |
- assert.Equal(c, getErrorMessage(c, b), expected) |
|
| 66 |
+ errMessage := string(bytes.TrimSpace(b)) |
|
| 67 |
+ if versions.GreaterThanOrEqualTo(version, "1.24") {
|
|
| 68 |
+ errMessage = getErrorMessage(c, b) |
|
| 69 |
+ } |
|
| 70 |
+ assert.Equal(c, errMessage, expected) |
|
| 67 | 71 |
} |
| 68 | 72 |
|
| 69 | 73 |
func (s *DockerAPISuite) TestAPIErrorJSON(c *testing.T) {
|