浏览代码

Merge pull request #38689 from thaJeztah/add_errdefs_utils_take2

Make client return "rich" errors (take 2)
Sebastiaan van Stijn 6 年之前
父节点
当前提交
5635c2a498
共有 100 个文件被更改,包括 613 次插入109 次删除
  1. 9 0
      api/server/httputils/errors_deprecated.go
  2. 26 0
      api/server/httputils/httputils.go
  3. 1 1
      api/server/router/container/container_routes.go
  4. 2 1
      api/server/server.go
  5. 3 3
      builder/builder-next/adapters/containerimage/pull.go
  6. 4 0
      client/checkpoint_create_test.go
  7. 4 0
      client/checkpoint_delete_test.go
  8. 4 0
      client/checkpoint_list_test.go
  9. 4 0
      client/config_create_test.go
  10. 4 0
      client/config_inspect_test.go
  11. 4 0
      client/config_list_test.go
  12. 4 0
      client/config_remove_test.go
  13. 4 0
      client/config_update_test.go
  14. 4 0
      client/container_commit_test.go
  15. 2 0
      client/container_copy.go
  16. 12 0
      client/container_copy_test.go
  17. 0 4
      client/container_create.go
  18. 8 1
      client/container_create_test.go
  19. 4 1
      client/container_diff_test.go
  20. 10 0
      client/container_exec_test.go
  21. 5 0
      client/container_export_test.go
  22. 4 0
      client/container_inspect_test.go
  23. 5 0
      client/container_kill_test.go
  24. 4 0
      client/container_list_test.go
  25. 5 0
      client/container_pause_test.go
  26. 5 0
      client/container_rename_test.go
  27. 7 0
      client/container_resize_test.go
  28. 5 0
      client/container_restart_test.go
  29. 4 0
      client/container_start_test.go
  30. 5 0
      client/container_stats_test.go
  31. 5 0
      client/container_stop_test.go
  32. 4 0
      client/container_top_test.go
  33. 5 0
      client/container_unpause_test.go
  34. 4 0
      client/container_update_test.go
  35. 4 0
      client/container_wait_test.go
  36. 4 0
      client/disk_usage_test.go
  37. 17 11
      client/errors.go
  38. 4 0
      client/events_test.go
  39. 4 0
      client/image_build_test.go
  40. 4 0
      client/image_create_test.go
  41. 4 0
      client/image_history_test.go
  42. 4 0
      client/image_import_test.go
  43. 4 0
      client/image_inspect_test.go
  44. 4 0
      client/image_list_test.go
  45. 5 0
      client/image_load_test.go
  46. 2 2
      client/image_pull.go
  47. 4 0
      client/image_pull_test.go
  48. 2 2
      client/image_push.go
  49. 4 0
      client/image_push_test.go
  50. 5 0
      client/image_save_test.go
  51. 2 2
      client/image_search.go
  52. 4 0
      client/image_search_test.go
  53. 5 0
      client/image_tag_test.go
  54. 4 0
      client/info_test.go
  55. 0 4
      client/login.go
  56. 4 0
      client/network_connect_test.go
  57. 4 0
      client/network_create_test.go
  58. 4 0
      client/network_disconnect_test.go
  59. 4 0
      client/network_list_test.go
  60. 4 0
      client/network_prune_test.go
  61. 5 0
      client/network_remove_test.go
  62. 4 0
      client/node_inspect_test.go
  63. 4 0
      client/node_list_test.go
  64. 4 0
      client/node_remove_test.go
  65. 4 0
      client/node_update_test.go
  66. 9 2
      client/ping.go
  67. 4 0
      client/plugin_disable_test.go
  68. 4 0
      client/plugin_enable_test.go
  69. 4 0
      client/plugin_inspect_test.go
  70. 2 2
      client/plugin_install.go
  71. 4 0
      client/plugin_list_test.go
  72. 5 0
      client/plugin_push_test.go
  73. 4 0
      client/plugin_remove_test.go
  74. 5 0
      client/plugin_set_test.go
  75. 4 2
      client/request.go
  76. 4 0
      client/request_test.go
  77. 4 0
      client/secret_create_test.go
  78. 4 0
      client/secret_inspect_test.go
  79. 4 0
      client/secret_list_test.go
  80. 4 0
      client/secret_remove_test.go
  81. 4 0
      client/secret_update_test.go
  82. 4 0
      client/service_create_test.go
  83. 4 0
      client/service_inspect_test.go
  84. 4 0
      client/service_list_test.go
  85. 4 0
      client/service_update_test.go
  86. 4 0
      client/swarm_init_test.go
  87. 4 0
      client/swarm_inspect_test.go
  88. 4 0
      client/swarm_join_test.go
  89. 5 0
      client/swarm_leave_test.go
  90. 4 0
      client/swarm_unlock_test.go
  91. 4 0
      client/swarm_update_test.go
  92. 4 0
      client/task_inspect_test.go
  93. 4 0
      client/task_list_test.go
  94. 4 0
      client/volume_create_test.go
  95. 4 0
      client/volume_list_test.go
  96. 5 0
      client/volume_remove_test.go
  97. 51 35
      errdefs/http_helpers.go
  98. 92 0
      errdefs/http_helpers_test.go
  99. 21 22
      integration/container/create_test.go
  100. 7 14
      integration/service/create_test.go

+ 9 - 0
api/server/httputils/errors_deprecated.go

@@ -0,0 +1,9 @@
+package httputils // import "github.com/docker/docker/api/server/httputils"
+import "github.com/docker/docker/errdefs"
+
+// GetHTTPErrorStatusCode retrieves status code from error message.
+//
+// Deprecated: use errdefs.GetHTTPErrorStatusCode
+func GetHTTPErrorStatusCode(err error) int {
+	return errdefs.GetHTTPErrorStatusCode(err)
+}

+ 26 - 0
api/server/httputils/httputils.go

@@ -7,9 +7,13 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 
 
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
+	"github.com/gorilla/mux"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
+	"google.golang.org/grpc/status"
 )
 )
 
 
 // APIVersionKey is the client's requested API version.
 // APIVersionKey is the client's requested API version.
@@ -88,6 +92,28 @@ func VersionFromContext(ctx context.Context) string {
 	return ""
 	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 := errdefs.GetHTTPErrorStatusCode(err)
+		vars := mux.Vars(r)
+		if apiVersionSupportsJSONErrors(vars["version"]) {
+			response := &types.ErrorResponse{
+				Message: err.Error(),
+			}
+			WriteJSON(w, statusCode, response)
+		} else {
+			http.Error(w, status.Convert(err).Message(), statusCode)
+		}
+	}
+}
+
+func apiVersionSupportsJSONErrors(version string) bool {
+	const firstAPIVersionWithJSONErrors = "1.23"
+	return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
+}
+
 // matchesContentType validates the content type against the expected one
 // matchesContentType validates the content type against the expected one
 func matchesContentType(contentType, expectedType string) bool {
 func matchesContentType(contentType, expectedType string) bool {
 	mimetype, _, err := mime.ParseMediaType(contentType)
 	mimetype, _, err := mime.ParseMediaType(contentType)

+ 1 - 1
api/server/router/container/container_routes.go

@@ -589,7 +589,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
 		// Remember to close stream if error happens
 		// Remember to close stream if error happens
 		conn, _, errHijack := hijacker.Hijack()
 		conn, _, errHijack := hijacker.Hijack()
 		if errHijack == nil {
 		if errHijack == nil {
-			statusCode := httputils.GetHTTPErrorStatusCode(err)
+			statusCode := errdefs.GetHTTPErrorStatusCode(err)
 			statusText := http.StatusText(statusCode)
 			statusText := http.StatusText(statusCode)
 			fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n%s\r\n", statusCode, statusText, err.Error())
 			fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n%s\r\n", statusCode, statusText, err.Error())
 			httputils.CloseStreams(conn)
 			httputils.CloseStreams(conn)

+ 2 - 1
api/server/server.go

@@ -12,6 +12,7 @@ import (
 	"github.com/docker/docker/api/server/router"
 	"github.com/docker/docker/api/server/router"
 	"github.com/docker/docker/api/server/router/debug"
 	"github.com/docker/docker/api/server/router/debug"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/dockerversion"
+	"github.com/docker/docker/errdefs"
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
 )
 )
@@ -139,7 +140,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
 		}
 		}
 
 
 		if err := handlerFunc(ctx, w, r, vars); err != nil {
 		if err := handlerFunc(ctx, w, r, vars); err != nil {
-			statusCode := httputils.GetHTTPErrorStatusCode(err)
+			statusCode := errdefs.GetHTTPErrorStatusCode(err)
 			if statusCode >= 500 {
 			if statusCode >= 500 {
 				logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
 				logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
 			}
 			}

+ 3 - 3
builder/builder-next/adapters/containerimage/pull.go

@@ -11,7 +11,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/containerd/containerd/content"
 	"github.com/containerd/containerd/content"
-	"github.com/containerd/containerd/errdefs"
+	containerderrors "github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/images"
 	"github.com/containerd/containerd/images"
 	"github.com/containerd/containerd/platforms"
 	"github.com/containerd/containerd/platforms"
 	ctdreference "github.com/containerd/containerd/reference"
 	ctdreference "github.com/containerd/containerd/reference"
@@ -36,7 +36,7 @@ import (
 	"github.com/moby/buildkit/util/progress"
 	"github.com/moby/buildkit/util/progress"
 	"github.com/moby/buildkit/util/resolver"
 	"github.com/moby/buildkit/util/resolver"
 	"github.com/moby/buildkit/util/tracing"
 	"github.com/moby/buildkit/util/tracing"
-	digest "github.com/opencontainers/go-digest"
+	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/image-spec/identity"
 	"github.com/opencontainers/image-spec/identity"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
@@ -654,7 +654,7 @@ func showProgress(ctx context.Context, ongoing *jobs, cs content.Store, pw progr
 			if !j.done {
 			if !j.done {
 				info, err := cs.Info(context.TODO(), j.Digest)
 				info, err := cs.Info(context.TODO(), j.Digest)
 				if err != nil {
 				if err != nil {
-					if errdefs.IsNotFound(err) {
+					if containerderrors.IsNotFound(err) {
 						// pw.Write(j.Digest.String(), progress.Status{
 						// pw.Write(j.Digest.String(), progress.Status{
 						// 	Action: "waiting",
 						// 	Action: "waiting",
 						// })
 						// })

+ 4 - 0
client/checkpoint_create_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestCheckpointCreateError(t *testing.T) {
 func TestCheckpointCreateError(t *testing.T) {
@@ -25,6 +26,9 @@ func TestCheckpointCreateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestCheckpointCreate(t *testing.T) {
 func TestCheckpointCreate(t *testing.T) {

+ 4 - 0
client/checkpoint_delete_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestCheckpointDeleteError(t *testing.T) {
 func TestCheckpointDeleteError(t *testing.T) {
@@ -24,6 +25,9 @@ func TestCheckpointDeleteError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestCheckpointDelete(t *testing.T) {
 func TestCheckpointDelete(t *testing.T) {

+ 4 - 0
client/checkpoint_list_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestCheckpointListError(t *testing.T) {
 func TestCheckpointListError(t *testing.T) {
@@ -22,6 +23,9 @@ func TestCheckpointListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestCheckpointList(t *testing.T) {
 func TestCheckpointList(t *testing.T) {

+ 4 - 0
client/config_create_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 )
 )
@@ -34,6 +35,9 @@ func TestConfigCreateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestConfigCreate(t *testing.T) {
 func TestConfigCreate(t *testing.T) {

+ 4 - 0
client/config_inspect_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
@@ -58,6 +59,9 @@ func TestConfigInspectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestConfigInspectConfigNotFound(t *testing.T) {
 func TestConfigInspectConfigNotFound(t *testing.T) {

+ 4 - 0
client/config_list_test.go

@@ -13,6 +13,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 )
 )
@@ -36,6 +37,9 @@ func TestConfigListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestConfigList(t *testing.T) {
 func TestConfigList(t *testing.T) {

+ 4 - 0
client/config_remove_test.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 
 
+	"github.com/docker/docker/errdefs"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 )
 )
@@ -32,6 +33,9 @@ func TestConfigRemoveError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestConfigRemove(t *testing.T) {
 func TestConfigRemove(t *testing.T) {

+ 4 - 0
client/config_update_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 )
 )
@@ -33,6 +34,9 @@ func TestConfigUpdateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestConfigUpdate(t *testing.T) {
 func TestConfigUpdate(t *testing.T) {

+ 4 - 0
client/container_commit_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerCommitError(t *testing.T) {
 func TestContainerCommitError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestContainerCommitError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerCommit(t *testing.T) {
 func TestContainerCommit(t *testing.T) {

+ 2 - 0
client/container_copy.go

@@ -50,6 +50,7 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath str
 	}
 	}
 	defer ensureReaderClosed(response)
 	defer ensureReaderClosed(response)
 
 
+	// TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior
 	if response.statusCode != http.StatusOK {
 	if response.statusCode != http.StatusOK {
 		return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
 		return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
 	}
 	}
@@ -69,6 +70,7 @@ func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath s
 		return nil, types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+srcPath)
 		return nil, types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+srcPath)
 	}
 	}
 
 
+	// TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior
 	if response.statusCode != http.StatusOK {
 	if response.statusCode != http.StatusOK {
 		return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
 		return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
 	}
 	}

+ 12 - 0
client/container_copy_test.go

@@ -12,6 +12,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerStatPathError(t *testing.T) {
 func TestContainerStatPathError(t *testing.T) {
@@ -22,6 +23,9 @@ func TestContainerStatPathError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server error, got %v", err)
 		t.Fatalf("expected a Server error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerStatPathNotFoundError(t *testing.T) {
 func TestContainerStatPathNotFoundError(t *testing.T) {
@@ -102,6 +106,9 @@ func TestCopyToContainerError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server error, got %v", err)
 		t.Fatalf("expected a Server error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestCopyToContainerNotFoundError(t *testing.T) {
 func TestCopyToContainerNotFoundError(t *testing.T) {
@@ -114,6 +121,7 @@ func TestCopyToContainerNotFoundError(t *testing.T) {
 	}
 	}
 }
 }
 
 
+// TODO TestCopyToContainerNotStatusOKError expects a non-error status-code ("204 No Content") to produce an error; verify if this is the desired behavior
 func TestCopyToContainerNotStatusOKError(t *testing.T) {
 func TestCopyToContainerNotStatusOKError(t *testing.T) {
 	client := &Client{
 	client := &Client{
 		client: newMockClient(errorMock(http.StatusNoContent, "No content")),
 		client: newMockClient(errorMock(http.StatusNoContent, "No content")),
@@ -178,6 +186,9 @@ func TestCopyFromContainerError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server error, got %v", err)
 		t.Fatalf("expected a Server error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestCopyFromContainerNotFoundError(t *testing.T) {
 func TestCopyFromContainerNotFoundError(t *testing.T) {
@@ -190,6 +201,7 @@ func TestCopyFromContainerNotFoundError(t *testing.T) {
 	}
 	}
 }
 }
 
 
+// TODO TestCopyFromContainerNotStatusOKError expects a non-error status-code ("204 No Content") to produce an error; verify if this is the desired behavior
 func TestCopyFromContainerNotStatusOKError(t *testing.T) {
 func TestCopyFromContainerNotStatusOKError(t *testing.T) {
 	client := &Client{
 	client := &Client{
 		client: newMockClient(errorMock(http.StatusNoContent, "No content")),
 		client: newMockClient(errorMock(http.StatusNoContent, "No content")),

+ 0 - 4
client/container_create.go

@@ -4,7 +4,6 @@ import (
 	"context"
 	"context"
 	"encoding/json"
 	"encoding/json"
 	"net/url"
 	"net/url"
-	"strings"
 
 
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/network"
@@ -44,9 +43,6 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
 
 
 	serverResp, err := cli.post(ctx, "/containers/create", query, body, nil)
 	serverResp, err := cli.post(ctx, "/containers/create", query, body, nil)
 	if err != nil {
 	if err != nil {
-		if serverResp.statusCode == 404 && strings.Contains(err.Error(), "No such image") {
-			return response, objectNotFoundError{object: "image", id: config.Image}
-		}
 		return response, err
 		return response, err
 	}
 	}
 
 

+ 8 - 1
client/container_create_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerCreateError(t *testing.T) {
 func TestContainerCreateError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestContainerCreateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error while testing StatusInternalServerError, got %v", err)
 		t.Fatalf("expected a Server Error while testing StatusInternalServerError, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error while testing StatusInternalServerError, got %T", err)
+	}
 
 
 	// 404 doesn't automatically means an unknown image
 	// 404 doesn't automatically means an unknown image
 	client = &Client{
 	client = &Client{
@@ -30,6 +34,9 @@ func TestContainerCreateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error while testing StatusNotFound, got %v", err)
 		t.Fatalf("expected a Server Error while testing StatusNotFound, got %v", err)
 	}
 	}
+	if err == nil || !IsErrNotFound(err) {
+		t.Fatalf("expected a Server Error while testing StatusNotFound, got %T", err)
+	}
 }
 }
 
 
 func TestContainerCreateImageNotFound(t *testing.T) {
 func TestContainerCreateImageNotFound(t *testing.T) {
@@ -38,7 +45,7 @@ func TestContainerCreateImageNotFound(t *testing.T) {
 	}
 	}
 	_, err := client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, "unknown")
 	_, err := client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, "unknown")
 	if err == nil || !IsErrNotFound(err) {
 	if err == nil || !IsErrNotFound(err) {
-		t.Fatalf("expected an imageNotFound error, got %v", err)
+		t.Fatalf("expected an imageNotFound error, got %v, %T", err, err)
 	}
 	}
 }
 }
 
 

+ 4 - 1
client/container_diff_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerDiffError(t *testing.T) {
 func TestContainerDiffError(t *testing.T) {
@@ -21,7 +22,9 @@ func TestContainerDiffError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
-
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerDiff(t *testing.T) {
 func TestContainerDiff(t *testing.T) {

+ 10 - 0
client/container_exec_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerExecCreateError(t *testing.T) {
 func TestContainerExecCreateError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestContainerExecCreateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerExecCreate(t *testing.T) {
 func TestContainerExecCreate(t *testing.T) {
@@ -76,6 +80,9 @@ func TestContainerExecStartError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerExecStart(t *testing.T) {
 func TestContainerExecStart(t *testing.T) {
@@ -120,6 +127,9 @@ func TestContainerExecInspectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerExecInspect(t *testing.T) {
 func TestContainerExecInspect(t *testing.T) {

+ 5 - 0
client/container_export_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerExportError(t *testing.T) {
 func TestContainerExportError(t *testing.T) {
@@ -18,6 +20,9 @@ func TestContainerExportError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerExport(t *testing.T) {
 func TestContainerExport(t *testing.T) {

+ 4 - 0
client/container_inspect_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -23,6 +24,9 @@ func TestContainerInspectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerInspectContainerNotFound(t *testing.T) {
 func TestContainerInspectContainerNotFound(t *testing.T) {

+ 5 - 0
client/container_kill_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerKillError(t *testing.T) {
 func TestContainerKillError(t *testing.T) {
@@ -18,6 +20,9 @@ func TestContainerKillError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerKill(t *testing.T) {
 func TestContainerKill(t *testing.T) {

+ 4 - 0
client/container_list_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerListError(t *testing.T) {
 func TestContainerListError(t *testing.T) {
@@ -22,6 +23,9 @@ func TestContainerListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerList(t *testing.T) {
 func TestContainerList(t *testing.T) {

+ 5 - 0
client/container_pause_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerPauseError(t *testing.T) {
 func TestContainerPauseError(t *testing.T) {
@@ -18,6 +20,9 @@ func TestContainerPauseError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerPause(t *testing.T) {
 func TestContainerPause(t *testing.T) {

+ 5 - 0
client/container_rename_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerRenameError(t *testing.T) {
 func TestContainerRenameError(t *testing.T) {
@@ -18,6 +20,9 @@ func TestContainerRenameError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerRename(t *testing.T) {
 func TestContainerRename(t *testing.T) {

+ 7 - 0
client/container_resize_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerResizeError(t *testing.T) {
 func TestContainerResizeError(t *testing.T) {
@@ -20,6 +21,9 @@ func TestContainerResizeError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerExecResizeError(t *testing.T) {
 func TestContainerExecResizeError(t *testing.T) {
@@ -30,6 +34,9 @@ func TestContainerExecResizeError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerResize(t *testing.T) {
 func TestContainerResize(t *testing.T) {

+ 5 - 0
client/container_restart_test.go

@@ -9,6 +9,8 @@ import (
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 	"time"
 	"time"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerRestartError(t *testing.T) {
 func TestContainerRestartError(t *testing.T) {
@@ -20,6 +22,9 @@ func TestContainerRestartError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerRestart(t *testing.T) {
 func TestContainerRestart(t *testing.T) {

+ 4 - 0
client/container_start_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerStartError(t *testing.T) {
 func TestContainerStartError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestContainerStartError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerStart(t *testing.T) {
 func TestContainerStart(t *testing.T) {

+ 5 - 0
client/container_stats_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerStatsError(t *testing.T) {
 func TestContainerStatsError(t *testing.T) {
@@ -18,6 +20,9 @@ func TestContainerStatsError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerStats(t *testing.T) {
 func TestContainerStats(t *testing.T) {

+ 5 - 0
client/container_stop_test.go

@@ -9,6 +9,8 @@ import (
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 	"time"
 	"time"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerStopError(t *testing.T) {
 func TestContainerStopError(t *testing.T) {
@@ -20,6 +22,9 @@ func TestContainerStopError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerStop(t *testing.T) {
 func TestContainerStop(t *testing.T) {

+ 4 - 0
client/container_top_test.go

@@ -12,6 +12,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerTopError(t *testing.T) {
 func TestContainerTopError(t *testing.T) {
@@ -22,6 +23,9 @@ func TestContainerTopError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerTop(t *testing.T) {
 func TestContainerTop(t *testing.T) {

+ 5 - 0
client/container_unpause_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerUnpauseError(t *testing.T) {
 func TestContainerUnpauseError(t *testing.T) {
@@ -18,6 +20,9 @@ func TestContainerUnpauseError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerUnpause(t *testing.T) {
 func TestContainerUnpause(t *testing.T) {

+ 4 - 0
client/container_update_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerUpdateError(t *testing.T) {
 func TestContainerUpdateError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestContainerUpdateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestContainerUpdate(t *testing.T) {
 func TestContainerUpdate(t *testing.T) {

+ 4 - 0
client/container_wait_test.go

@@ -13,6 +13,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestContainerWaitError(t *testing.T) {
 func TestContainerWaitError(t *testing.T) {
@@ -27,6 +28,9 @@ func TestContainerWaitError(t *testing.T) {
 		if err.Error() != "Error response from daemon: Server error" {
 		if err.Error() != "Error response from daemon: Server error" {
 			t.Fatalf("expected a Server Error, got %v", err)
 			t.Fatalf("expected a Server Error, got %v", err)
 		}
 		}
+		if !errdefs.IsSystem(err) {
+			t.Fatalf("expected a Server Error, got %T", err)
+		}
 	}
 	}
 }
 }
 
 

+ 4 - 0
client/disk_usage_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestDiskUsageError(t *testing.T) {
 func TestDiskUsageError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestDiskUsageError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestDiskUsage(t *testing.T) {
 func TestDiskUsage(t *testing.T) {

+ 17 - 11
client/errors.go

@@ -5,6 +5,7 @@ import (
 	"net/http"
 	"net/http"
 
 
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -32,16 +33,19 @@ func ErrorConnectionFailed(host string) error {
 	return errConnectionFailed{host: host}
 	return errConnectionFailed{host: host}
 }
 }
 
 
+// Deprecated: use the errdefs.NotFound() interface instead. Kept for backward compatibility
 type notFound interface {
 type notFound interface {
 	error
 	error
-	NotFound() bool // Is the error a NotFound error
+	NotFound() bool
 }
 }
 
 
 // IsErrNotFound returns true if the error is a NotFound error, which is returned
 // IsErrNotFound returns true if the error is a NotFound error, which is returned
 // by the API when some object is not found.
 // by the API when some object is not found.
 func IsErrNotFound(err error) bool {
 func IsErrNotFound(err error) bool {
-	te, ok := err.(notFound)
-	return ok && te.NotFound()
+	if _, ok := err.(notFound); ok {
+		return ok
+	}
+	return errdefs.IsNotFound(err)
 }
 }
 
 
 type objectNotFoundError struct {
 type objectNotFoundError struct {
@@ -49,9 +53,7 @@ type objectNotFoundError struct {
 	id     string
 	id     string
 }
 }
 
 
-func (e objectNotFoundError) NotFound() bool {
-	return true
-}
+func (e objectNotFoundError) NotFound() {}
 
 
 func (e objectNotFoundError) Error() string {
 func (e objectNotFoundError) Error() string {
 	return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
 	return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
@@ -64,7 +66,7 @@ func wrapResponseError(err error, resp serverResponse, object, id string) error
 	case resp.statusCode == http.StatusNotFound:
 	case resp.statusCode == http.StatusNotFound:
 		return objectNotFoundError{object: object, id: id}
 		return objectNotFoundError{object: object, id: id}
 	case resp.statusCode == http.StatusNotImplemented:
 	case resp.statusCode == http.StatusNotImplemented:
-		return notImplementedError{message: err.Error()}
+		return errdefs.NotImplemented(err)
 	default:
 	default:
 		return err
 		return err
 	}
 	}
@@ -83,8 +85,10 @@ func (u unauthorizedError) Error() string {
 // IsErrUnauthorized returns true if the error is caused
 // IsErrUnauthorized returns true if the error is caused
 // when a remote registry authentication fails
 // when a remote registry authentication fails
 func IsErrUnauthorized(err error) bool {
 func IsErrUnauthorized(err error) bool {
-	_, ok := err.(unauthorizedError)
-	return ok
+	if _, ok := err.(unauthorizedError); ok {
+		return ok
+	}
+	return errdefs.IsUnauthorized(err)
 }
 }
 
 
 type pluginPermissionDenied struct {
 type pluginPermissionDenied struct {
@@ -118,8 +122,10 @@ func (e notImplementedError) NotImplemented() bool {
 // This is returned by the API when a requested feature has not been
 // This is returned by the API when a requested feature has not been
 // implemented.
 // implemented.
 func IsErrNotImplemented(err error) bool {
 func IsErrNotImplemented(err error) bool {
-	te, ok := err.(notImplementedError)
-	return ok && te.NotImplemented()
+	if _, ok := err.(notImplementedError); ok {
+		return ok
+	}
+	return errdefs.IsNotImplemented(err)
 }
 }
 
 
 // NewVersionError returns an error if the APIVersion required
 // NewVersionError returns an error if the APIVersion required

+ 4 - 0
client/events_test.go

@@ -14,6 +14,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestEventsErrorInOptions(t *testing.T) {
 func TestEventsErrorInOptions(t *testing.T) {
@@ -55,6 +56,9 @@ func TestEventsErrorFromServer(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestEvents(t *testing.T) {
 func TestEvents(t *testing.T) {

+ 4 - 0
client/image_build_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/errdefs"
 	"github.com/docker/go-units"
 	"github.com/docker/go-units"
 )
 )
 
 
@@ -23,6 +24,9 @@ func TestImageBuildError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImageBuild(t *testing.T) {
 func TestImageBuild(t *testing.T) {

+ 4 - 0
client/image_create_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestImageCreateError(t *testing.T) {
 func TestImageCreateError(t *testing.T) {
@@ -20,6 +21,9 @@ func TestImageCreateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server error, got %v", err)
 		t.Fatalf("expected a Server error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImageCreate(t *testing.T) {
 func TestImageCreate(t *testing.T) {

+ 4 - 0
client/image_history_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/api/types/image"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestImageHistoryError(t *testing.T) {
 func TestImageHistoryError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestImageHistoryError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server error, got %v", err)
 		t.Fatalf("expected a Server error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImageHistory(t *testing.T) {
 func TestImageHistory(t *testing.T) {

+ 4 - 0
client/image_import_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestImageImportError(t *testing.T) {
 func TestImageImportError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestImageImportError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server error, got %v", err)
 		t.Fatalf("expected a Server error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImageImport(t *testing.T) {
 func TestImageImport(t *testing.T) {

+ 4 - 0
client/image_inspect_test.go

@@ -12,6 +12,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -24,6 +25,9 @@ func TestImageInspectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImageInspectImageNotFound(t *testing.T) {
 func TestImageInspectImageNotFound(t *testing.T) {

+ 4 - 0
client/image_list_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestImageListError(t *testing.T) {
 func TestImageListError(t *testing.T) {
@@ -23,6 +24,9 @@ func TestImageListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImageList(t *testing.T) {
 func TestImageList(t *testing.T) {

+ 5 - 0
client/image_load_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestImageLoadError(t *testing.T) {
 func TestImageLoadError(t *testing.T) {
@@ -19,6 +21,9 @@ func TestImageLoadError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImageLoad(t *testing.T) {
 func TestImageLoad(t *testing.T) {

+ 2 - 2
client/image_pull.go

@@ -3,12 +3,12 @@ package client // import "github.com/docker/docker/client"
 import (
 import (
 	"context"
 	"context"
 	"io"
 	"io"
-	"net/http"
 	"net/url"
 	"net/url"
 	"strings"
 	"strings"
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 // ImagePull requests the docker host to pull an image from a remote registry.
 // ImagePull requests the docker host to pull an image from a remote registry.
@@ -35,7 +35,7 @@ func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.I
 	}
 	}
 
 
 	resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
 	resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
-	if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
+	if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		if privilegeErr != nil {
 		if privilegeErr != nil {
 			return nil, privilegeErr
 			return nil, privilegeErr

+ 4 - 0
client/image_pull_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestImagePullReferenceParseError(t *testing.T) {
 func TestImagePullReferenceParseError(t *testing.T) {
@@ -33,6 +34,9 @@ func TestImagePullAnyError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImagePullStatusUnauthorizedError(t *testing.T) {
 func TestImagePullStatusUnauthorizedError(t *testing.T) {

+ 2 - 2
client/image_push.go

@@ -4,11 +4,11 @@ import (
 	"context"
 	"context"
 	"errors"
 	"errors"
 	"io"
 	"io"
-	"net/http"
 	"net/url"
 	"net/url"
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 // ImagePush requests the docker host to push an image to a remote registry.
 // ImagePush requests the docker host to push an image to a remote registry.
@@ -36,7 +36,7 @@ func (cli *Client) ImagePush(ctx context.Context, image string, options types.Im
 	query.Set("tag", tag)
 	query.Set("tag", tag)
 
 
 	resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth)
 	resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth)
-	if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
+	if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		if privilegeErr != nil {
 		if privilegeErr != nil {
 			return nil, privilegeErr
 			return nil, privilegeErr

+ 4 - 0
client/image_push_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestImagePushReferenceError(t *testing.T) {
 func TestImagePushReferenceError(t *testing.T) {
@@ -38,6 +39,9 @@ func TestImagePushAnyError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImagePushStatusUnauthorizedError(t *testing.T) {
 func TestImagePushStatusUnauthorizedError(t *testing.T) {

+ 5 - 0
client/image_save_test.go

@@ -9,6 +9,8 @@ import (
 	"reflect"
 	"reflect"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestImageSaveError(t *testing.T) {
 func TestImageSaveError(t *testing.T) {
@@ -19,6 +21,9 @@ func TestImageSaveError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server error, got %v", err)
 		t.Fatalf("expected a Server error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImageSave(t *testing.T) {
 func TestImageSave(t *testing.T) {

+ 2 - 2
client/image_search.go

@@ -4,12 +4,12 @@ import (
 	"context"
 	"context"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"net/http"
 	"net/url"
 	"net/url"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/api/types/registry"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 // ImageSearch makes the docker host to search by a term in a remote registry.
 // ImageSearch makes the docker host to search by a term in a remote registry.
@@ -29,7 +29,7 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options types.I
 	}
 	}
 
 
 	resp, err := cli.tryImageSearch(ctx, query, options.RegistryAuth)
 	resp, err := cli.tryImageSearch(ctx, query, options.RegistryAuth)
-	if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
+	if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		if privilegeErr != nil {
 		if privilegeErr != nil {
 			return results, privilegeErr
 			return results, privilegeErr

+ 4 - 0
client/image_search_test.go

@@ -13,6 +13,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/api/types/registry"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestImageSearchAnyError(t *testing.T) {
 func TestImageSearchAnyError(t *testing.T) {
@@ -23,6 +24,9 @@ func TestImageSearchAnyError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestImageSearchStatusUnauthorizedError(t *testing.T) {
 func TestImageSearchStatusUnauthorizedError(t *testing.T) {

+ 5 - 0
client/image_tag_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestImageTagError(t *testing.T) {
 func TestImageTagError(t *testing.T) {
@@ -19,6 +21,9 @@ func TestImageTagError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 // Note: this is not testing all the InvalidReference as it's the responsibility
 // Note: this is not testing all the InvalidReference as it's the responsibility

+ 4 - 0
client/info_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestInfoServerError(t *testing.T) {
 func TestInfoServerError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestInfoServerError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestInfoInvalidResponseJSONError(t *testing.T) {
 func TestInfoInvalidResponseJSONError(t *testing.T) {

+ 0 - 4
client/login.go

@@ -3,7 +3,6 @@ package client // import "github.com/docker/docker/client"
 import (
 import (
 	"context"
 	"context"
 	"encoding/json"
 	"encoding/json"
-	"net/http"
 	"net/url"
 	"net/url"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
@@ -15,9 +14,6 @@ import (
 func (cli *Client) RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error) {
 func (cli *Client) RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error) {
 	resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil)
 	resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil)
 
 
-	if resp.statusCode == http.StatusUnauthorized {
-		return registry.AuthenticateOKBody{}, unauthorizedError{err}
-	}
 	if err != nil {
 	if err != nil {
 		return registry.AuthenticateOKBody{}, err
 		return registry.AuthenticateOKBody{}, err
 	}
 	}

+ 4 - 0
client/network_connect_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/network"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestNetworkConnectError(t *testing.T) {
 func TestNetworkConnectError(t *testing.T) {
@@ -23,6 +24,9 @@ func TestNetworkConnectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestNetworkConnectEmptyNilEndpointSettings(t *testing.T) {
 func TestNetworkConnectEmptyNilEndpointSettings(t *testing.T) {

+ 4 - 0
client/network_create_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestNetworkCreateError(t *testing.T) {
 func TestNetworkCreateError(t *testing.T) {
@@ -22,6 +23,9 @@ func TestNetworkCreateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestNetworkCreate(t *testing.T) {
 func TestNetworkCreate(t *testing.T) {

+ 4 - 0
client/network_disconnect_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestNetworkDisconnectError(t *testing.T) {
 func TestNetworkDisconnectError(t *testing.T) {
@@ -22,6 +23,9 @@ func TestNetworkDisconnectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestNetworkDisconnect(t *testing.T) {
 func TestNetworkDisconnect(t *testing.T) {

+ 4 - 0
client/network_list_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestNetworkListError(t *testing.T) {
 func TestNetworkListError(t *testing.T) {
@@ -25,6 +26,9 @@ func TestNetworkListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestNetworkList(t *testing.T) {
 func TestNetworkList(t *testing.T) {

+ 4 - 0
client/network_prune_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/errdefs"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 )
 )
@@ -28,6 +29,9 @@ func TestNetworksPruneError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestNetworksPrune(t *testing.T) {
 func TestNetworksPrune(t *testing.T) {

+ 5 - 0
client/network_remove_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestNetworkRemoveError(t *testing.T) {
 func TestNetworkRemoveError(t *testing.T) {
@@ -19,6 +21,9 @@ func TestNetworkRemoveError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestNetworkRemove(t *testing.T) {
 func TestNetworkRemove(t *testing.T) {

+ 4 - 0
client/node_inspect_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -23,6 +24,9 @@ func TestNodeInspectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestNodeInspectNodeNotFound(t *testing.T) {
 func TestNodeInspectNodeNotFound(t *testing.T) {

+ 4 - 0
client/node_list_test.go

@@ -13,6 +13,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestNodeListError(t *testing.T) {
 func TestNodeListError(t *testing.T) {
@@ -24,6 +25,9 @@ func TestNodeListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestNodeList(t *testing.T) {
 func TestNodeList(t *testing.T) {

+ 4 - 0
client/node_remove_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestNodeRemoveError(t *testing.T) {
 func TestNodeRemoveError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestNodeRemoveError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestNodeRemove(t *testing.T) {
 func TestNodeRemove(t *testing.T) {

+ 4 - 0
client/node_update_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestNodeUpdateError(t *testing.T) {
 func TestNodeUpdateError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestNodeUpdateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestNodeUpdate(t *testing.T) {
 func TestNodeUpdate(t *testing.T) {

+ 9 - 2
client/ping.go

@@ -6,6 +6,7 @@ import (
 	"path"
 	"path"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 // Ping pings the server and returns the value of the "Docker-Experimental",
 // Ping pings the server and returns the value of the "Docker-Experimental",
@@ -14,6 +15,10 @@ import (
 // by the daemon.
 // by the daemon.
 func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
 func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
 	var ping types.Ping
 	var ping types.Ping
+
+	// Using cli.buildRequest() + cli.doRequest() instead of cli.sendRequest()
+	// because ping requests are used during  API version negotiation, so we want
+	// to hit the non-versioned /_ping endpoint, not /v1.xx/_ping
 	req, err := cli.buildRequest("HEAD", path.Join(cli.basePath, "/_ping"), nil, nil)
 	req, err := cli.buildRequest("HEAD", path.Join(cli.basePath, "/_ping"), nil, nil)
 	if err != nil {
 	if err != nil {
 		return ping, err
 		return ping, err
@@ -43,7 +48,8 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
 func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) {
 func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) {
 	var ping types.Ping
 	var ping types.Ping
 	if resp.header == nil {
 	if resp.header == nil {
-		return ping, cli.checkResponseErr(resp)
+		err := cli.checkResponseErr(resp)
+		return ping, errdefs.FromStatusCode(err, resp.statusCode)
 	}
 	}
 	ping.APIVersion = resp.header.Get("API-Version")
 	ping.APIVersion = resp.header.Get("API-Version")
 	ping.OSType = resp.header.Get("OSType")
 	ping.OSType = resp.header.Get("OSType")
@@ -53,5 +59,6 @@ func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) {
 	if bv := resp.header.Get("Builder-Version"); bv != "" {
 	if bv := resp.header.Get("Builder-Version"); bv != "" {
 		ping.BuilderVersion = types.BuilderVersion(bv)
 		ping.BuilderVersion = types.BuilderVersion(bv)
 	}
 	}
-	return ping, cli.checkResponseErr(resp)
+	err := cli.checkResponseErr(resp)
+	return ping, errdefs.FromStatusCode(err, resp.statusCode)
 }
 }

+ 4 - 0
client/plugin_disable_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestPluginDisableError(t *testing.T) {
 func TestPluginDisableError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestPluginDisableError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestPluginDisable(t *testing.T) {
 func TestPluginDisable(t *testing.T) {

+ 4 - 0
client/plugin_enable_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestPluginEnableError(t *testing.T) {
 func TestPluginEnableError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestPluginEnableError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestPluginEnable(t *testing.T) {
 func TestPluginEnable(t *testing.T) {

+ 4 - 0
client/plugin_inspect_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -23,6 +24,9 @@ func TestPluginInspectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestPluginInspectWithEmptyID(t *testing.T) {
 func TestPluginInspectWithEmptyID(t *testing.T) {

+ 2 - 2
client/plugin_install.go

@@ -4,11 +4,11 @@ import (
 	"context"
 	"context"
 	"encoding/json"
 	"encoding/json"
 	"io"
 	"io"
-	"net/http"
 	"net/url"
 	"net/url"
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -78,7 +78,7 @@ func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileg
 
 
 func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values, options types.PluginInstallOptions) (types.PluginPrivileges, error) {
 func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values, options types.PluginInstallOptions) (types.PluginPrivileges, error) {
 	resp, err := cli.tryPluginPrivileges(ctx, query, options.RegistryAuth)
 	resp, err := cli.tryPluginPrivileges(ctx, query, options.RegistryAuth)
-	if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
+	if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
 		// todo: do inspect before to check existing name before checking privileges
 		// todo: do inspect before to check existing name before checking privileges
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		if privilegeErr != nil {
 		if privilegeErr != nil {

+ 4 - 0
client/plugin_list_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestPluginListError(t *testing.T) {
 func TestPluginListError(t *testing.T) {
@@ -23,6 +24,9 @@ func TestPluginListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestPluginList(t *testing.T) {
 func TestPluginList(t *testing.T) {

+ 5 - 0
client/plugin_push_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestPluginPushError(t *testing.T) {
 func TestPluginPushError(t *testing.T) {
@@ -19,6 +21,9 @@ func TestPluginPushError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestPluginPush(t *testing.T) {
 func TestPluginPush(t *testing.T) {

+ 4 - 0
client/plugin_remove_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestPluginRemoveError(t *testing.T) {
 func TestPluginRemoveError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestPluginRemoveError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestPluginRemove(t *testing.T) {
 func TestPluginRemove(t *testing.T) {

+ 5 - 0
client/plugin_set_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestPluginSetError(t *testing.T) {
 func TestPluginSetError(t *testing.T) {
@@ -19,6 +21,9 @@ func TestPluginSetError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestPluginSet(t *testing.T) {
 func TestPluginSet(t *testing.T) {

+ 4 - 2
client/request.go

@@ -15,6 +15,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -120,9 +121,10 @@ func (cli *Client) sendRequest(ctx context.Context, method, path string, query u
 	}
 	}
 	resp, err := cli.doRequest(ctx, req)
 	resp, err := cli.doRequest(ctx, req)
 	if err != nil {
 	if err != nil {
-		return resp, err
+		return resp, errdefs.FromStatusCode(err, resp.statusCode)
 	}
 	}
-	return resp, cli.checkResponseErr(resp)
+	err = cli.checkResponseErr(resp)
+	return resp, errdefs.FromStatusCode(err, resp.statusCode)
 }
 }
 
 
 func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResponse, error) {
 func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResponse, error) {

+ 4 - 0
client/request_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/errdefs"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 )
 )
@@ -88,6 +89,9 @@ func TestPlainTextError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestInfiniteError(t *testing.T) {
 func TestInfiniteError(t *testing.T) {

+ 4 - 0
client/secret_create_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 )
 )
@@ -34,6 +35,9 @@ func TestSecretCreateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSecretCreate(t *testing.T) {
 func TestSecretCreate(t *testing.T) {

+ 4 - 0
client/secret_inspect_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
@@ -35,6 +36,9 @@ func TestSecretInspectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSecretInspectSecretNotFound(t *testing.T) {
 func TestSecretInspectSecretNotFound(t *testing.T) {

+ 4 - 0
client/secret_list_test.go

@@ -13,6 +13,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 )
 )
@@ -36,6 +37,9 @@ func TestSecretListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSecretList(t *testing.T) {
 func TestSecretList(t *testing.T) {

+ 4 - 0
client/secret_remove_test.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 
 
+	"github.com/docker/docker/errdefs"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 )
 )
@@ -32,6 +33,9 @@ func TestSecretRemoveError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSecretRemove(t *testing.T) {
 func TestSecretRemove(t *testing.T) {

+ 4 - 0
client/secret_update_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 )
 )
@@ -33,6 +34,9 @@ func TestSecretUpdateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSecretUpdate(t *testing.T) {
 func TestSecretUpdate(t *testing.T) {

+ 4 - 0
client/service_create_test.go

@@ -13,6 +13,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/opencontainers/image-spec/specs-go/v1"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
@@ -27,6 +28,9 @@ func TestServiceCreateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestServiceCreate(t *testing.T) {
 func TestServiceCreate(t *testing.T) {

+ 4 - 0
client/service_inspect_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -24,6 +25,9 @@ func TestServiceInspectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestServiceInspectServiceNotFound(t *testing.T) {
 func TestServiceInspectServiceNotFound(t *testing.T) {

+ 4 - 0
client/service_list_test.go

@@ -13,6 +13,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestServiceListError(t *testing.T) {
 func TestServiceListError(t *testing.T) {
@@ -24,6 +25,9 @@ func TestServiceListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestServiceList(t *testing.T) {
 func TestServiceList(t *testing.T) {

+ 4 - 0
client/service_update_test.go

@@ -11,6 +11,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestServiceUpdateError(t *testing.T) {
 func TestServiceUpdateError(t *testing.T) {
@@ -22,6 +23,9 @@ func TestServiceUpdateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestServiceUpdate(t *testing.T) {
 func TestServiceUpdate(t *testing.T) {

+ 4 - 0
client/swarm_init_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestSwarmInitError(t *testing.T) {
 func TestSwarmInitError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestSwarmInitError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSwarmInit(t *testing.T) {
 func TestSwarmInit(t *testing.T) {

+ 4 - 0
client/swarm_inspect_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestSwarmInspectError(t *testing.T) {
 func TestSwarmInspectError(t *testing.T) {
@@ -22,6 +23,9 @@ func TestSwarmInspectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSwarmInspect(t *testing.T) {
 func TestSwarmInspect(t *testing.T) {

+ 4 - 0
client/swarm_join_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestSwarmJoinError(t *testing.T) {
 func TestSwarmJoinError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestSwarmJoinError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSwarmJoin(t *testing.T) {
 func TestSwarmJoin(t *testing.T) {

+ 5 - 0
client/swarm_leave_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestSwarmLeaveError(t *testing.T) {
 func TestSwarmLeaveError(t *testing.T) {
@@ -19,6 +21,9 @@ func TestSwarmLeaveError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSwarmLeave(t *testing.T) {
 func TestSwarmLeave(t *testing.T) {

+ 4 - 0
client/swarm_unlock_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestSwarmUnlockError(t *testing.T) {
 func TestSwarmUnlockError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestSwarmUnlockError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSwarmUnlock(t *testing.T) {
 func TestSwarmUnlock(t *testing.T) {

+ 4 - 0
client/swarm_update_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestSwarmUpdateError(t *testing.T) {
 func TestSwarmUpdateError(t *testing.T) {
@@ -21,6 +22,9 @@ func TestSwarmUpdateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestSwarmUpdate(t *testing.T) {
 func TestSwarmUpdate(t *testing.T) {

+ 4 - 0
client/task_inspect_test.go

@@ -11,6 +11,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -23,6 +24,9 @@ func TestTaskInspectError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestTaskInspectWithEmptyID(t *testing.T) {
 func TestTaskInspectWithEmptyID(t *testing.T) {

+ 4 - 0
client/task_list_test.go

@@ -13,6 +13,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestTaskListError(t *testing.T) {
 func TestTaskListError(t *testing.T) {
@@ -24,6 +25,9 @@ func TestTaskListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestTaskList(t *testing.T) {
 func TestTaskList(t *testing.T) {

+ 4 - 0
client/volume_create_test.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	volumetypes "github.com/docker/docker/api/types/volume"
 	volumetypes "github.com/docker/docker/api/types/volume"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestVolumeCreateError(t *testing.T) {
 func TestVolumeCreateError(t *testing.T) {
@@ -23,6 +24,9 @@ func TestVolumeCreateError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestVolumeCreate(t *testing.T) {
 func TestVolumeCreate(t *testing.T) {

+ 4 - 0
client/volume_list_test.go

@@ -13,6 +13,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	volumetypes "github.com/docker/docker/api/types/volume"
 	volumetypes "github.com/docker/docker/api/types/volume"
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestVolumeListError(t *testing.T) {
 func TestVolumeListError(t *testing.T) {
@@ -24,6 +25,9 @@ func TestVolumeListError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestVolumeList(t *testing.T) {
 func TestVolumeList(t *testing.T) {

+ 5 - 0
client/volume_remove_test.go

@@ -8,6 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/errdefs"
 )
 )
 
 
 func TestVolumeRemoveError(t *testing.T) {
 func TestVolumeRemoveError(t *testing.T) {
@@ -19,6 +21,9 @@ func TestVolumeRemoveError(t *testing.T) {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
+	if !errdefs.IsSystem(err) {
+		t.Fatalf("expected a Server Error, got %T", err)
+	}
 }
 }
 
 
 func TestVolumeRemove(t *testing.T) {
 func TestVolumeRemove(t *testing.T) {

+ 51 - 35
api/server/httputils/errors.go → errdefs/http_helpers.go

@@ -1,23 +1,15 @@
-package httputils // import "github.com/docker/docker/api/server/httputils"
+package errdefs // import "github.com/docker/docker/errdefs"
 
 
 import (
 import (
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
 
 
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/distribution/registry/api/errcode"
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/versions"
-	"github.com/docker/docker/errdefs"
-	"github.com/gorilla/mux"
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	"google.golang.org/grpc/status"
 )
 )
 
 
-type causer interface {
-	Cause() error
-}
-
 // GetHTTPErrorStatusCode retrieves status code from error message.
 // GetHTTPErrorStatusCode retrieves status code from error message.
 func GetHTTPErrorStatusCode(err error) int {
 func GetHTTPErrorStatusCode(err error) int {
 	if err == nil {
 	if err == nil {
@@ -32,23 +24,23 @@ func GetHTTPErrorStatusCode(err error) int {
 
 
 	// Note that the below functions are already checking the error causal chain for matches.
 	// Note that the below functions are already checking the error causal chain for matches.
 	switch {
 	switch {
-	case errdefs.IsNotFound(err):
+	case IsNotFound(err):
 		statusCode = http.StatusNotFound
 		statusCode = http.StatusNotFound
-	case errdefs.IsInvalidParameter(err):
+	case IsInvalidParameter(err):
 		statusCode = http.StatusBadRequest
 		statusCode = http.StatusBadRequest
-	case errdefs.IsConflict(err) || errdefs.IsAlreadyExists(err):
+	case IsConflict(err) || IsAlreadyExists(err):
 		statusCode = http.StatusConflict
 		statusCode = http.StatusConflict
-	case errdefs.IsUnauthorized(err):
+	case IsUnauthorized(err):
 		statusCode = http.StatusUnauthorized
 		statusCode = http.StatusUnauthorized
-	case errdefs.IsUnavailable(err):
+	case IsUnavailable(err):
 		statusCode = http.StatusServiceUnavailable
 		statusCode = http.StatusServiceUnavailable
-	case errdefs.IsForbidden(err):
+	case IsForbidden(err):
 		statusCode = http.StatusForbidden
 		statusCode = http.StatusForbidden
-	case errdefs.IsNotModified(err):
+	case IsNotModified(err):
 		statusCode = http.StatusNotModified
 		statusCode = http.StatusNotModified
-	case errdefs.IsNotImplemented(err):
+	case IsNotImplemented(err):
 		statusCode = http.StatusNotImplemented
 		statusCode = http.StatusNotImplemented
-	case errdefs.IsSystem(err) || errdefs.IsUnknown(err) || errdefs.IsDataLoss(err) || errdefs.IsDeadline(err) || errdefs.IsCancelled(err):
+	case IsSystem(err) || IsUnknown(err) || IsDataLoss(err) || IsDeadline(err) || IsCancelled(err):
 		statusCode = http.StatusInternalServerError
 		statusCode = http.StatusInternalServerError
 	default:
 	default:
 		statusCode = statusCodeFromGRPCError(err)
 		statusCode = statusCodeFromGRPCError(err)
@@ -76,26 +68,50 @@ func GetHTTPErrorStatusCode(err error) int {
 	return statusCode
 	return statusCode
 }
 }
 
 
-func apiVersionSupportsJSONErrors(version string) bool {
-	const firstAPIVersionWithJSONErrors = "1.23"
-	return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
-}
+// FromStatusCode creates an errdef error, based on the provided HTTP status-code
+func FromStatusCode(err error, statusCode int) error {
+	if err == nil {
+		return err
+	}
+	switch statusCode {
+	case http.StatusNotFound:
+		err = NotFound(err)
+	case http.StatusBadRequest:
+		err = InvalidParameter(err)
+	case http.StatusConflict:
+		err = Conflict(err)
+	case http.StatusUnauthorized:
+		err = Unauthorized(err)
+	case http.StatusServiceUnavailable:
+		err = Unavailable(err)
+	case http.StatusForbidden:
+		err = Forbidden(err)
+	case http.StatusNotModified:
+		err = NotModified(err)
+	case http.StatusNotImplemented:
+		err = NotImplemented(err)
+	case http.StatusInternalServerError:
+		if !IsSystem(err) && !IsUnknown(err) && !IsDataLoss(err) && !IsDeadline(err) && !IsCancelled(err) {
+			err = System(err)
+		}
+	default:
+		logrus.WithFields(logrus.Fields{
+			"module":      "api",
+			"status_code": fmt.Sprintf("%d", statusCode),
+		}).Debugf("FIXME: Got an status-code for which error does not match any expected type!!!: %d", statusCode)
 
 
-// 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 apiVersionSupportsJSONErrors(vars["version"]) {
-			response := &types.ErrorResponse{
-				Message: err.Error(),
-			}
-			WriteJSON(w, statusCode, response)
-		} else {
-			http.Error(w, status.Convert(err).Message(), statusCode)
+		switch {
+		case statusCode >= 200 && statusCode < 400:
+			// it's a client error
+		case statusCode >= 400 && statusCode < 500:
+			err = InvalidParameter(err)
+		case statusCode >= 500 && statusCode < 600:
+			err = System(err)
+		default:
+			err = Unknown(err)
 		}
 		}
 	}
 	}
+	return err
 }
 }
 
 
 // statusCodeFromGRPCError returns status code according to gRPC error
 // statusCodeFromGRPCError returns status code according to gRPC error

+ 92 - 0
errdefs/http_helpers_test.go

@@ -0,0 +1,92 @@
+package errdefs
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"gotest.tools/assert"
+)
+
+func TestFromStatusCode(t *testing.T) {
+	testErr := fmt.Errorf("some error occurred")
+
+	testCases := []struct {
+		err    error
+		status int
+		check  func(error) bool
+	}{
+		{
+			err:    testErr,
+			status: http.StatusNotFound,
+			check:  IsNotFound,
+		},
+		{
+			err:    testErr,
+			status: http.StatusBadRequest,
+			check:  IsInvalidParameter,
+		},
+		{
+			err:    testErr,
+			status: http.StatusConflict,
+			check:  IsConflict,
+		},
+		{
+			err:    testErr,
+			status: http.StatusUnauthorized,
+			check:  IsUnauthorized,
+		},
+		{
+			err:    testErr,
+			status: http.StatusServiceUnavailable,
+			check:  IsUnavailable,
+		},
+		{
+			err:    testErr,
+			status: http.StatusForbidden,
+			check:  IsForbidden,
+		},
+		{
+			err:    testErr,
+			status: http.StatusNotModified,
+			check:  IsNotModified,
+		},
+		{
+			err:    testErr,
+			status: http.StatusNotImplemented,
+			check:  IsNotImplemented,
+		},
+		{
+			err:    testErr,
+			status: http.StatusInternalServerError,
+			check:  IsSystem,
+		},
+		{
+			err:    Unknown(testErr),
+			status: http.StatusInternalServerError,
+			check:  IsUnknown,
+		},
+		{
+			err:    DataLoss(testErr),
+			status: http.StatusInternalServerError,
+			check:  IsDataLoss,
+		},
+		{
+			err:    Deadline(testErr),
+			status: http.StatusInternalServerError,
+			check:  IsDeadline,
+		},
+		{
+			err:    Cancelled(testErr),
+			status: http.StatusInternalServerError,
+			check:  IsCancelled,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(http.StatusText(tc.status), func(t *testing.T) {
+			err := FromStatusCode(tc.err, tc.status)
+			assert.Check(t, tc.check(err), "unexpected error-type %T", err)
+		})
+	}
+}

+ 21 - 22
integration/container/create_test.go

@@ -4,7 +4,6 @@ import (
 	"context"
 	"context"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"net/http"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
 	"time"
 	"time"
@@ -14,6 +13,7 @@ import (
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/client"
+	"github.com/docker/docker/errdefs"
 	ctr "github.com/docker/docker/integration/internal/container"
 	ctr "github.com/docker/docker/integration/internal/container"
 	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/oci"
 	"github.com/docker/docker/oci"
@@ -60,6 +60,7 @@ func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) {
 				"",
 				"",
 			)
 			)
 			assert.Check(t, is.ErrorContains(err, tc.expectedError))
 			assert.Check(t, is.ErrorContains(err, tc.expectedError))
+			assert.Check(t, errdefs.IsNotFound(err))
 		})
 		})
 	}
 	}
 }
 }
@@ -100,6 +101,7 @@ func TestCreateWithInvalidEnv(t *testing.T) {
 				"",
 				"",
 			)
 			)
 			assert.Check(t, is.ErrorContains(err, tc.expectedError))
 			assert.Check(t, is.ErrorContains(err, tc.expectedError))
+			assert.Check(t, errdefs.IsInvalidParameter(err))
 		})
 		})
 	}
 	}
 }
 }
@@ -145,6 +147,7 @@ func TestCreateTmpfsMountsTarget(t *testing.T) {
 			"",
 			"",
 		)
 		)
 		assert.Check(t, is.ErrorContains(err, tc.expectedError))
 		assert.Check(t, is.ErrorContains(err, tc.expectedError))
+		assert.Check(t, errdefs.IsInvalidParameter(err))
 	}
 	}
 }
 }
 func TestCreateWithCustomMaskedPaths(t *testing.T) {
 func TestCreateWithCustomMaskedPaths(t *testing.T) {
@@ -346,6 +349,7 @@ func TestCreateWithCapabilities(t *testing.T) {
 				assert.DeepEqual(t, tc.expected, ci.HostConfig.Capabilities)
 				assert.DeepEqual(t, tc.expected, ci.HostConfig.Capabilities)
 			} else {
 			} else {
 				assert.ErrorContains(t, err, tc.expectedError)
 				assert.ErrorContains(t, err, tc.expectedError)
+				assert.Check(t, errdefs.IsInvalidParameter(err))
 			}
 			}
 		})
 		})
 	}
 	}
@@ -432,6 +436,8 @@ func TestCreateWithCustomReadonlyPaths(t *testing.T) {
 
 
 func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
 func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
 	defer setupTest(t)()
 	defer setupTest(t)()
+	client := testEnv.APIClient()
+	ctx := context.Background()
 
 
 	testCases := []struct {
 	testCases := []struct {
 		doc         string
 		doc         string
@@ -479,38 +485,31 @@ func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
 		},
 		},
 	}
 	}
 
 
-	for i, tc := range testCases {
-		i := i
+	for _, tc := range testCases {
 		tc := tc
 		tc := tc
 		t.Run(tc.doc, func(t *testing.T) {
 		t.Run(tc.doc, func(t *testing.T) {
 			t.Parallel()
 			t.Parallel()
-			healthCheck := map[string]interface{}{
-				"Interval": tc.interval,
-				"Timeout":  tc.timeout,
-				"Retries":  tc.retries,
+			cfg := container.Config{
+				Image: "busybox",
+				Healthcheck: &container.HealthConfig{
+					Interval: tc.interval,
+					Timeout:  tc.timeout,
+					Retries:  tc.retries,
+				},
 			}
 			}
 			if tc.startPeriod != 0 {
 			if tc.startPeriod != 0 {
-				healthCheck["StartPeriod"] = tc.startPeriod
-			}
-
-			config := map[string]interface{}{
-				"Image":       "busybox",
-				"Healthcheck": healthCheck,
+				cfg.Healthcheck.StartPeriod = tc.startPeriod
 			}
 			}
 
 
-			res, body, err := request.Post("/containers/create?name="+fmt.Sprintf("test_%d_", i)+t.Name(), request.JSONBody(config))
-			assert.NilError(t, err)
+			resp, err := client.ContainerCreate(ctx, &cfg, &container.HostConfig{}, nil, "")
+			assert.Check(t, is.Equal(len(resp.Warnings), 0))
 
 
 			if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
 			if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
-				assert.Check(t, is.Equal(http.StatusInternalServerError, res.StatusCode))
+				assert.Check(t, errdefs.IsSystem(err))
 			} else {
 			} else {
-				assert.Check(t, is.Equal(http.StatusBadRequest, res.StatusCode))
+				assert.Check(t, errdefs.IsInvalidParameter(err))
 			}
 			}
-
-			buf, err := request.ReadBody(body)
-			assert.NilError(t, err)
-
-			assert.Check(t, is.Contains(string(buf), tc.expectedErr))
+			assert.ErrorContains(t, err, tc.expectedErr)
 		})
 		})
 	}
 	}
 }
 }

+ 7 - 14
integration/service/create_test.go

@@ -4,7 +4,6 @@ import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
 	"io/ioutil"
 	"io/ioutil"
-	"net/http"
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
@@ -13,10 +12,10 @@ import (
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/client"
+	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/integration/internal/network"
 	"github.com/docker/docker/integration/internal/network"
 	"github.com/docker/docker/integration/internal/swarm"
 	"github.com/docker/docker/integration/internal/swarm"
 	"github.com/docker/docker/internal/test/daemon"
 	"github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/request"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
 	"gotest.tools/poll"
 	"gotest.tools/poll"
@@ -129,6 +128,9 @@ func TestCreateServiceConflict(t *testing.T) {
 	defer setupTest(t)()
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
 	defer d.Stop(t)
+	c := d.NewClientT(t)
+	defer c.Close()
+	ctx := context.Background()
 
 
 	serviceName := "TestService_" + t.Name()
 	serviceName := "TestService_" + t.Name()
 	serviceSpec := []swarm.ServiceSpecOpt{
 	serviceSpec := []swarm.ServiceSpecOpt{
@@ -138,18 +140,9 @@ func TestCreateServiceConflict(t *testing.T) {
 	swarm.CreateService(t, d, serviceSpec...)
 	swarm.CreateService(t, d, serviceSpec...)
 
 
 	spec := swarm.CreateServiceSpec(t, serviceSpec...)
 	spec := swarm.CreateServiceSpec(t, serviceSpec...)
-	res, body, err := request.Post(
-		"/services/create",
-		request.Host(d.Sock()),
-		request.JSONBody(spec),
-		request.JSON,
-	)
-	assert.NilError(t, err)
-	assert.Equal(t, res.StatusCode, http.StatusConflict)
-
-	buf, err := request.ReadBody(body)
-	assert.NilError(t, err)
-	assert.Check(t, is.Contains(string(buf), "service "+serviceName+" already exists"))
+	_, err := c.ServiceCreate(ctx, spec, types.ServiceCreateOptions{})
+	assert.Check(t, errdefs.IsConflict(err))
+	assert.ErrorContains(t, err, "service "+serviceName+" already exists")
 }
 }
 
 
 func TestCreateServiceMaxReplicas(t *testing.T) {
 func TestCreateServiceMaxReplicas(t *testing.T) {