Browse code

Merge pull request #49666 from thaJeztah/legacy_errors

api, client: produce human-readable errors for unsupported API versions (< v1.24)

Sebastiaan van Stijn authored on 2025/04/10 19:08:56
Showing 4 changed files
... ...
@@ -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) {