Kaynağa Gözat

Add helpers to create errdef errors

Instead of having to create a bunch of custom error types that are doing
nothing but wrapping another error in sub-packages, use a common helper
to create errors of the requested type.

e.g. instead of re-implementing this over and over:

```go
type notFoundError struct {
  cause error
}

func(e notFoundError) Error() string {
  return e.cause.Error()
}

func(e notFoundError) NotFound() {}

func(e notFoundError) Cause() error {
  return e.cause
}
```

Packages can instead just do:

```
  errdefs.NotFound(err)
```

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
Brian Goff 7 yıl önce
ebeveyn
işleme
87a12421a9
65 değiştirilmiş dosya ile 682 ekleme ve 661 silme
  1. 20 0
      api/errdefs/defs.go
  2. 240 0
      api/errdefs/helpers.go
  3. 132 0
      api/errdefs/helpers_test.go
  4. 28 0
      api/errdefs/is.go
  5. 2 2
      api/server/httputils/errors.go
  6. 3 16
      api/server/httputils/httputils.go
  7. 7 16
      api/server/router/build/build_routes.go
  8. 0 14
      api/server/router/container/container.go
  9. 6 6
      api/server/router/container/container_routes.go
  10. 3 2
      api/server/router/container/exec.go
  11. 2 15
      api/server/router/image/image_routes.go
  12. 5 19
      api/server/router/network/network_routes.go
  13. 2 15
      api/server/router/session/session_routes.go
  14. 13 26
      api/server/router/swarm/cluster_routes.go
  15. 6 5
      builder/dockerfile/builder.go
  16. 2 1
      builder/dockerfile/dispatchers.go
  17. 0 15
      builder/dockerfile/errors.go
  18. 3 2
      builder/dockerfile/evaluator.go
  19. 0 75
      builder/remotecontext/errors.go
  20. 11 10
      builder/remotecontext/remote.go
  21. 9 8
      daemon/archive.go
  22. 4 3
      daemon/attach.go
  23. 0 56
      daemon/cluster/errors.go
  24. 12 11
      daemon/cluster/helpers.go
  25. 2 1
      daemon/cluster/networks.go
  26. 2 1
      daemon/cluster/nodes.go
  27. 3 2
      daemon/cluster/services.go
  28. 6 5
      daemon/cluster/swarm.go
  29. 3 2
      daemon/commit.go
  30. 3 2
      daemon/container.go
  31. 2 1
      daemon/container_linux.go
  32. 2 1
      daemon/container_operations.go
  33. 2 1
      daemon/container_operations_unix.go
  34. 8 7
      daemon/create.go
  35. 4 3
      daemon/delete.go
  36. 6 75
      daemon/errors.go
  37. 4 3
      daemon/exec.go
  38. 3 2
      daemon/export.go
  39. 2 1
      daemon/image.go
  40. 2 1
      daemon/image_delete.go
  41. 4 3
      daemon/image_pull.go
  42. 5 4
      daemon/import.go
  43. 3 2
      daemon/inspect.go
  44. 2 1
      daemon/list.go
  45. 3 2
      daemon/logs.go
  46. 2 1
      daemon/names.go
  47. 11 6
      daemon/network.go
  48. 4 3
      daemon/rename.go
  49. 14 13
      daemon/start.go
  50. 2 1
      daemon/start_unix.go
  51. 2 1
      daemon/stop.go
  52. 3 2
      daemon/update.go
  53. 2 1
      daemon/volumes.go
  54. 2 15
      distribution/errors.go
  55. 11 10
      libcontainerd/client_daemon.go
  56. 7 40
      libcontainerd/errors.go
  57. 7 6
      plugin/backend_linux.go
  58. 0 28
      plugin/errors.go
  59. 5 4
      plugin/manager_linux.go
  60. 3 2
      plugin/store.go
  61. 8 7
      registry/auth.go
  62. 2 57
      registry/errors.go
  63. 3 2
      registry/service.go
  64. 4 3
      registry/session.go
  65. 9 22
      volume/local/local.go

+ 20 - 0
api/errdefs/defs.go

@@ -43,6 +43,11 @@ type ErrNotModified interface {
 	NotModified()
 }
 
+// ErrAlreadyExists is a special case of ErrConflict which signals that the desired object already exists
+type ErrAlreadyExists interface {
+	AlreadyExists()
+}
+
 // ErrNotImplemented signals that the requested action/feature is not implemented on the system as configured.
 type ErrNotImplemented interface {
 	NotImplemented()
@@ -52,3 +57,18 @@ type ErrNotImplemented interface {
 type ErrUnknown interface {
 	Unknown()
 }
+
+// ErrCancelled signals that the action was cancelled.
+type ErrCancelled interface {
+	Cancelled()
+}
+
+// ErrDeadline signals that the deadline was reached before the action completed.
+type ErrDeadline interface {
+	DeadlineExceeded()
+}
+
+// ErrDataLoss indicates that data was lost or there is data corruption.
+type ErrDataLoss interface {
+	DataLoss()
+}

+ 240 - 0
api/errdefs/helpers.go

@@ -0,0 +1,240 @@
+package errdefs
+
+import "context"
+
+type errNotFound struct{ error }
+
+func (errNotFound) NotFound() {}
+
+func (e errNotFound) Cause() error {
+	return e.error
+}
+
+// NotFound is a helper to create an error of the class with the same name from any error type
+func NotFound(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errNotFound{err}
+}
+
+type errInvalidParameter struct{ error }
+
+func (errInvalidParameter) InvalidParameter() {}
+
+func (e errInvalidParameter) Cause() error {
+	return e.error
+}
+
+// InvalidParameter is a helper to create an error of the class with the same name from any error type
+func InvalidParameter(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errInvalidParameter{err}
+}
+
+type errConflict struct{ error }
+
+func (errConflict) Conflict() {}
+
+func (e errConflict) Cause() error {
+	return e.error
+}
+
+// Conflict is a helper to create an error of the class with the same name from any error type
+func Conflict(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errConflict{err}
+}
+
+type errUnauthorized struct{ error }
+
+func (errUnauthorized) Unauthorized() {}
+
+func (e errUnauthorized) Cause() error {
+	return e.error
+}
+
+// Unauthorized is a helper to create an error of the class with the same name from any error type
+func Unauthorized(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errUnauthorized{err}
+}
+
+type errUnavailable struct{ error }
+
+func (errUnavailable) Unavailable() {}
+
+func (e errUnavailable) Cause() error {
+	return e.error
+}
+
+// Unavailable is a helper to create an error of the class with the same name from any error type
+func Unavailable(err error) error {
+	return errUnavailable{err}
+}
+
+type errForbidden struct{ error }
+
+func (errForbidden) Forbidden() {}
+
+func (e errForbidden) Cause() error {
+	return e.error
+}
+
+// Forbidden is a helper to create an error of the class with the same name from any error type
+func Forbidden(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errForbidden{err}
+}
+
+type errSystem struct{ error }
+
+func (errSystem) System() {}
+
+func (e errSystem) Cause() error {
+	return e.error
+}
+
+// System is a helper to create an error of the class with the same name from any error type
+func System(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errSystem{err}
+}
+
+type errNotModified struct{ error }
+
+func (errNotModified) NotModified() {}
+
+func (e errNotModified) Cause() error {
+	return e.error
+}
+
+// NotModified is a helper to create an error of the class with the same name from any error type
+func NotModified(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errNotModified{err}
+}
+
+type errAlreadyExists struct{ error }
+
+func (errAlreadyExists) AlreadyExists() {}
+
+func (e errAlreadyExists) Cause() error {
+	return e.error
+}
+
+// AlreadyExists is a helper to create an error of the class with the same name from any error type
+func AlreadyExists(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errAlreadyExists{err}
+}
+
+type errNotImplemented struct{ error }
+
+func (errNotImplemented) NotImplemented() {}
+
+func (e errNotImplemented) Cause() error {
+	return e.error
+}
+
+// NotImplemented is a helper to create an error of the class with the same name from any error type
+func NotImplemented(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errNotImplemented{err}
+}
+
+type errUnknown struct{ error }
+
+func (errUnknown) Unknown() {}
+
+func (e errUnknown) Cause() error {
+	return e.error
+}
+
+// Unknown is a helper to create an error of the class with the same name from any error type
+func Unknown(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errUnknown{err}
+}
+
+type errCancelled struct{ error }
+
+func (errCancelled) Cancelled() {}
+
+func (e errCancelled) Cause() error {
+	return e.error
+}
+
+// Cancelled is a helper to create an error of the class with the same name from any error type
+func Cancelled(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errCancelled{err}
+}
+
+type errDeadline struct{ error }
+
+func (errDeadline) DeadlineExceeded() {}
+
+func (e errDeadline) Cause() error {
+	return e.error
+}
+
+// Deadline is a helper to create an error of the class with the same name from any error type
+func Deadline(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errDeadline{err}
+}
+
+type errDataLoss struct{ error }
+
+func (errDataLoss) DataLoss() {}
+
+func (e errDataLoss) Cause() error {
+	return e.error
+}
+
+// DataLoss is a helper to create an error of the class with the same name from any error type
+func DataLoss(err error) error {
+	if err == nil {
+		return nil
+	}
+	return errDataLoss{err}
+}
+
+// FromContext returns the error class from the passed in context
+func FromContext(ctx context.Context) error {
+	e := ctx.Err()
+	if e == nil {
+		return nil
+	}
+
+	if e == context.Canceled {
+		return Cancelled(e)
+	}
+	if e == context.DeadlineExceeded {
+		return Deadline(e)
+	}
+	return Unknown(e)
+}

+ 132 - 0
api/errdefs/helpers_test.go

@@ -0,0 +1,132 @@
+package errdefs
+
+import (
+	"errors"
+	"testing"
+)
+
+var errTest = errors.New("this is a test")
+
+type causal interface {
+	Cause() error
+}
+
+func TestNotFound(t *testing.T) {
+	e := NotFound(errTest)
+	if !IsNotFound(e) {
+		t.Fatalf("expected not found error, got: %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestConflict(t *testing.T) {
+	e := Conflict(errTest)
+	if !IsConflict(e) {
+		t.Fatalf("expected conflcit error, got: %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestForbidden(t *testing.T) {
+	e := Forbidden(errTest)
+	if !IsForbidden(e) {
+		t.Fatalf("expected forbidden error, got: %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestInvalidParameter(t *testing.T) {
+	e := InvalidParameter(errTest)
+	if !IsInvalidParameter(e) {
+		t.Fatalf("expected invalid argument error, got %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestNotImplemented(t *testing.T) {
+	e := NotImplemented(errTest)
+	if !IsNotImplemented(e) {
+		t.Fatalf("expected not implemented error, got %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestNotModified(t *testing.T) {
+	e := NotModified(errTest)
+	if !IsNotModified(e) {
+		t.Fatalf("expected not modified error, got %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestAlreadyExists(t *testing.T) {
+	e := AlreadyExists(errTest)
+	if !IsAlreadyExists(e) {
+		t.Fatalf("expected already exists error, got %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestUnauthorized(t *testing.T) {
+	e := Unauthorized(errTest)
+	if !IsUnauthorized(e) {
+		t.Fatalf("expected unauthorized error, got %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestUnknown(t *testing.T) {
+	e := Unknown(errTest)
+	if !IsUnknown(e) {
+		t.Fatalf("expected unknown error, got %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestCancelled(t *testing.T) {
+	e := Cancelled(errTest)
+	if !IsCancelled(e) {
+		t.Fatalf("expected canclled error, got %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestDeadline(t *testing.T) {
+	e := Deadline(errTest)
+	if !IsDeadline(e) {
+		t.Fatalf("expected deadline error, got %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}
+
+func TestIsDataLoss(t *testing.T) {
+	e := DataLoss(errTest)
+	if !IsDataLoss(e) {
+		t.Fatalf("expected data loss error, got %T", e)
+	}
+	if cause := e.(causal).Cause(); cause != errTest {
+		t.Fatalf("causual should be errTest, got: %v", cause)
+	}
+}

+ 28 - 0
api/errdefs/is.go

@@ -15,7 +15,11 @@ func getImplementer(err error) error {
 		ErrForbidden,
 		ErrSystem,
 		ErrNotModified,
+		ErrAlreadyExists,
 		ErrNotImplemented,
+		ErrCancelled,
+		ErrDeadline,
+		ErrDataLoss,
 		ErrUnknown:
 		return e
 	case causer:
@@ -73,6 +77,12 @@ func IsNotModified(err error) bool {
 	return ok
 }
 
+// IsAlreadyExists returns if the passed in error is a AlreadyExists error
+func IsAlreadyExists(err error) bool {
+	_, ok := getImplementer(err).(ErrAlreadyExists)
+	return ok
+}
+
 // IsNotImplemented returns if the passed in error is an ErrNotImplemented
 func IsNotImplemented(err error) bool {
 	_, ok := getImplementer(err).(ErrNotImplemented)
@@ -84,3 +94,21 @@ func IsUnknown(err error) bool {
 	_, ok := getImplementer(err).(ErrUnknown)
 	return ok
 }
+
+// IsCancelled returns if the passed in error is an ErrCancelled
+func IsCancelled(err error) bool {
+	_, ok := getImplementer(err).(ErrCancelled)
+	return ok
+}
+
+// IsDeadline returns if the passed in error is an ErrDeadline
+func IsDeadline(err error) bool {
+	_, ok := getImplementer(err).(ErrDeadline)
+	return ok
+}
+
+// IsDataLoss returns if the passed in error is an ErrDataLoss
+func IsDataLoss(err error) bool {
+	_, ok := getImplementer(err).(ErrDataLoss)
+	return ok
+}

+ 2 - 2
api/server/httputils/errors.go

@@ -35,7 +35,7 @@ func GetHTTPErrorStatusCode(err error) int {
 		statusCode = http.StatusNotFound
 	case errdefs.IsInvalidParameter(err):
 		statusCode = http.StatusBadRequest
-	case errdefs.IsConflict(err):
+	case errdefs.IsConflict(err) || errdefs.IsAlreadyExists(err):
 		statusCode = http.StatusConflict
 	case errdefs.IsUnauthorized(err):
 		statusCode = http.StatusUnauthorized
@@ -47,7 +47,7 @@ func GetHTTPErrorStatusCode(err error) int {
 		statusCode = http.StatusNotModified
 	case errdefs.IsNotImplemented(err):
 		statusCode = http.StatusNotImplemented
-	case errdefs.IsSystem(err) || errdefs.IsUnknown(err):
+	case errdefs.IsSystem(err) || errdefs.IsUnknown(err) || errdefs.IsDataLoss(err) || errdefs.IsDeadline(err) || errdefs.IsCancelled(err):
 		statusCode = http.StatusInternalServerError
 	default:
 		statusCode = statusCodeFromGRPCError(err)

+ 3 - 16
api/server/httputils/httputils.go

@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"strings"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/net/context"
@@ -43,20 +44,6 @@ func CloseStreams(streams ...interface{}) {
 	}
 }
 
-type validationError struct {
-	cause error
-}
-
-func (e validationError) Error() string {
-	return e.cause.Error()
-}
-
-func (e validationError) Cause() error {
-	return e.cause
-}
-
-func (e validationError) InvalidParameter() {}
-
 // CheckForJSON makes sure that the request's Content-Type is application/json.
 func CheckForJSON(r *http.Request) error {
 	ct := r.Header.Get("Content-Type")
@@ -72,7 +59,7 @@ func CheckForJSON(r *http.Request) error {
 	if matchesContentType(ct, "application/json") {
 		return nil
 	}
-	return validationError{errors.Errorf("Content-Type specified (%s) must be 'application/json'", ct)}
+	return errdefs.InvalidParameter(errors.Errorf("Content-Type specified (%s) must be 'application/json'", ct))
 }
 
 // ParseForm ensures the request form is parsed even with invalid content types.
@@ -82,7 +69,7 @@ func ParseForm(r *http.Request) error {
 		return nil
 	}
 	if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 	return nil
 }

+ 7 - 16
api/server/router/build/build_routes.go

@@ -13,6 +13,7 @@ import (
 	"strings"
 	"sync"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
@@ -83,7 +84,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
 		}
 		p := system.ParsePlatform(apiPlatform)
 		if err := system.ValidatePlatform(p); err != nil {
-			return nil, validationError{fmt.Errorf("invalid platform: %s", err)}
+			return nil, errdefs.InvalidParameter(errors.Errorf("invalid platform: %s", err))
 		}
 		options.Platform = p.OS
 	}
@@ -104,14 +105,14 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
 	}
 
 	if runtime.GOOS != "windows" && options.SecurityOpt != nil {
-		return nil, validationError{fmt.Errorf("The daemon on this platform does not support setting security options on build")}
+		return nil, errdefs.InvalidParameter(errors.New("The daemon on this platform does not support setting security options on build"))
 	}
 
 	var buildUlimits = []*units.Ulimit{}
 	ulimitsJSON := r.FormValue("ulimits")
 	if ulimitsJSON != "" {
 		if err := json.Unmarshal([]byte(ulimitsJSON), &buildUlimits); err != nil {
-			return nil, errors.Wrap(validationError{err}, "error reading ulimit settings")
+			return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading ulimit settings")
 		}
 		options.Ulimits = buildUlimits
 	}
@@ -132,7 +133,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
 	if buildArgsJSON != "" {
 		var buildArgs = map[string]*string{}
 		if err := json.Unmarshal([]byte(buildArgsJSON), &buildArgs); err != nil {
-			return nil, errors.Wrap(validationError{err}, "error reading build args")
+			return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading build args")
 		}
 		options.BuildArgs = buildArgs
 	}
@@ -141,7 +142,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
 	if labelsJSON != "" {
 		var labels = map[string]string{}
 		if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil {
-			return nil, errors.Wrap(validationError{err}, "error reading labels")
+			return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading labels")
 		}
 		options.Labels = labels
 	}
@@ -167,16 +168,6 @@ func (br *buildRouter) postPrune(ctx context.Context, w http.ResponseWriter, r *
 	return httputils.WriteJSON(w, http.StatusOK, report)
 }
 
-type validationError struct {
-	cause error
-}
-
-func (e validationError) Error() string {
-	return e.cause.Error()
-}
-
-func (e validationError) InvalidParameter() {}
-
 func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	var (
 		notVerboseBuffer = bytes.NewBuffer(nil)
@@ -210,7 +201,7 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
 	buildOptions.AuthConfigs = getAuthConfigs(r.Header)
 
 	if buildOptions.Squash && !br.daemon.HasExperimental() {
-		return validationError{errors.New("squash is only supported with experimental mode")}
+		return errdefs.InvalidParameter(errors.New("squash is only supported with experimental mode"))
 	}
 
 	out := io.Writer(output)

+ 0 - 14
api/server/router/container/container.go

@@ -5,20 +5,6 @@ import (
 	"github.com/docker/docker/api/server/router"
 )
 
-type validationError struct {
-	cause error
-}
-
-func (e validationError) Error() string {
-	return e.cause.Error()
-}
-
-func (e validationError) Cause() error {
-	return e.cause
-}
-
-func (e validationError) InvalidParameter() {}
-
 // containerRouter is a router to talk with the container controller
 type containerRouter struct {
 	backend Backend

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

@@ -88,7 +88,7 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
 	// with the appropriate status code.
 	stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr")
 	if !(stdout || stderr) {
-		return validationError{errors.New("Bad parameters: you must choose at least one stream")}
+		return errdefs.InvalidParameter(errors.New("Bad parameters: you must choose at least one stream"))
 	}
 
 	containerName := vars["name"]
@@ -203,7 +203,7 @@ func (s *containerRouter) postContainersKill(ctx context.Context, w http.Respons
 	if sigStr := r.Form.Get("signal"); sigStr != "" {
 		var err error
 		if sig, err = signal.ParseSignal(sigStr); err != nil {
-			return validationError{err}
+			return errdefs.InvalidParameter(err)
 		}
 	}
 
@@ -468,11 +468,11 @@ func (s *containerRouter) postContainersResize(ctx context.Context, w http.Respo
 
 	height, err := strconv.Atoi(r.Form.Get("h"))
 	if err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 	width, err := strconv.Atoi(r.Form.Get("w"))
 	if err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	return s.backend.ContainerResize(vars["name"], height, width)
@@ -490,7 +490,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
 
 	hijacker, ok := w.(http.Hijacker)
 	if !ok {
-		return validationError{errors.Errorf("error attaching to container %s, hijack connection missing", containerName)}
+		return errdefs.InvalidParameter(errors.Errorf("error attaching to container %s, hijack connection missing", containerName))
 	}
 
 	setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
@@ -611,7 +611,7 @@ func (s *containerRouter) postContainersPrune(ctx context.Context, w http.Respon
 
 	pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
 	if err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	pruneReport, err := s.backend.ContainersPrune(ctx, pruneFilters)

+ 3 - 2
api/server/router/container/exec.go

@@ -7,6 +7,7 @@ import (
 	"net/http"
 	"strconv"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/versions"
@@ -137,11 +138,11 @@ func (s *containerRouter) postContainerExecResize(ctx context.Context, w http.Re
 	}
 	height, err := strconv.Atoi(r.Form.Get("h"))
 	if err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 	width, err := strconv.Atoi(r.Form.Get("w"))
 	if err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	return s.backend.ContainerExecResize(vars["name"], height, width)

+ 2 - 15
api/server/router/image/image_routes.go

@@ -10,6 +10,7 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
@@ -144,20 +145,6 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
 	return nil
 }
 
-type validationError struct {
-	cause error
-}
-
-func (e validationError) Error() string {
-	return e.cause.Error()
-}
-
-func (e validationError) Cause() error {
-	return e.cause
-}
-
-func (validationError) InvalidParameter() {}
-
 func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	metaHeaders := map[string][]string{}
 	for k, v := range r.Header {
@@ -181,7 +168,7 @@ func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter,
 	} else {
 		// the old format is supported for compatibility if there was no authConfig header
 		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
-			return errors.Wrap(validationError{err}, "Bad parameters and missing X-Registry-Auth")
+			return errors.Wrap(errdefs.InvalidParameter(err), "Bad parameters and missing X-Registry-Auth")
 		}
 	}
 

+ 5 - 19
api/server/router/network/network_routes.go

@@ -2,13 +2,13 @@ package network
 
 import (
 	"encoding/json"
-	"fmt"
 	"net/http"
 	"strconv"
 	"strings"
 
 	"golang.org/x/net/context"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
@@ -101,22 +101,8 @@ func (e ambigousResultsError) Error() string {
 
 func (ambigousResultsError) InvalidParameter() {}
 
-type conflictError struct {
-	cause error
-}
-
-func (e conflictError) Error() string {
-	return e.cause.Error()
-}
-
-func (e conflictError) Cause() error {
-	return e.cause
-}
-
-func (e conflictError) Conflict() {}
-
 func nameConflict(name string) error {
-	return conflictError{libnetwork.NetworkNameError(name)}
+	return errdefs.Conflict(libnetwork.NetworkNameError(name))
 }
 
 func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
@@ -589,7 +575,7 @@ func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, e
 		}
 	}
 	if len(listByFullName) > 1 {
-		return types.NetworkResource{}, fmt.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))
+		return types.NetworkResource{}, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName)))
 	}
 
 	// Find based on partial ID, returns true only if no duplicates
@@ -599,8 +585,8 @@ func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, e
 		}
 	}
 	if len(listByPartialID) > 1 {
-		return types.NetworkResource{}, fmt.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))
+		return types.NetworkResource{}, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID)))
 	}
 
-	return types.NetworkResource{}, libnetwork.ErrNoSuchNetwork(term)
+	return types.NetworkResource{}, errdefs.NotFound(libnetwork.ErrNoSuchNetwork(term))
 }

+ 2 - 15
api/server/router/session/session_routes.go

@@ -3,27 +3,14 @@ package session
 import (
 	"net/http"
 
+	"github.com/docker/docker/api/errdefs"
 	"golang.org/x/net/context"
 )
 
-type invalidRequest struct {
-	cause error
-}
-
-func (e invalidRequest) Error() string {
-	return e.cause.Error()
-}
-
-func (e invalidRequest) Cause() error {
-	return e.cause
-}
-
-func (e invalidRequest) InvalidParameter() {}
-
 func (sr *sessionRouter) startSession(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	err := sr.backend.HandleHTTPRequest(ctx, w, r)
 	if err != nil {
-		return invalidRequest{err}
+		return errdefs.InvalidParameter(err)
 	}
 	return nil
 }

+ 13 - 26
api/server/router/swarm/cluster_routes.go

@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"strconv"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/server/httputils"
 	basictypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
@@ -57,20 +58,6 @@ func (sr *swarmRouter) inspectCluster(ctx context.Context, w http.ResponseWriter
 	return httputils.WriteJSON(w, http.StatusOK, swarm)
 }
 
-type invalidRequestError struct {
-	err error
-}
-
-func (e invalidRequestError) Error() string {
-	return e.err.Error()
-}
-
-func (e invalidRequestError) Cause() error {
-	return e.err
-}
-
-func (e invalidRequestError) InvalidParameter() {}
-
 func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	var swarm types.Spec
 	if err := json.NewDecoder(r.Body).Decode(&swarm); err != nil {
@@ -81,7 +68,7 @@ func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter,
 	version, err := strconv.ParseUint(rawVersion, 10, 64)
 	if err != nil {
 		err := fmt.Errorf("invalid swarm version '%s': %v", rawVersion, err)
-		return invalidRequestError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	var flags types.UpdateFlags
@@ -90,7 +77,7 @@ func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter,
 		rot, err := strconv.ParseBool(value)
 		if err != nil {
 			err := fmt.Errorf("invalid value for rotateWorkerToken: %s", value)
-			return invalidRequestError{err}
+			return errdefs.InvalidParameter(err)
 		}
 
 		flags.RotateWorkerToken = rot
@@ -100,7 +87,7 @@ func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter,
 		rot, err := strconv.ParseBool(value)
 		if err != nil {
 			err := fmt.Errorf("invalid value for rotateManagerToken: %s", value)
-			return invalidRequestError{err}
+			return errdefs.InvalidParameter(err)
 		}
 
 		flags.RotateManagerToken = rot
@@ -109,7 +96,7 @@ func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter,
 	if value := r.URL.Query().Get("rotateManagerUnlockKey"); value != "" {
 		rot, err := strconv.ParseBool(value)
 		if err != nil {
-			return invalidRequestError{fmt.Errorf("invalid value for rotateManagerUnlockKey: %s", value)}
+			return errdefs.InvalidParameter(fmt.Errorf("invalid value for rotateManagerUnlockKey: %s", value))
 		}
 
 		flags.RotateManagerUnlockKey = rot
@@ -153,7 +140,7 @@ func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r
 	}
 	filter, err := filters.FromJSON(r.Form.Get("filters"))
 	if err != nil {
-		return invalidRequestError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	services, err := sr.backend.GetServices(basictypes.ServiceListOptions{Filters: filter})
@@ -172,7 +159,7 @@ func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r
 		insertDefaults, err = strconv.ParseBool(value)
 		if err != nil {
 			err := fmt.Errorf("invalid value for insertDefaults: %s", value)
-			return errors.Wrapf(invalidRequestError{err}, "invalid value for insertDefaults: %s", value)
+			return errors.Wrapf(errdefs.InvalidParameter(err), "invalid value for insertDefaults: %s", value)
 		}
 	}
 
@@ -218,7 +205,7 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter,
 	version, err := strconv.ParseUint(rawVersion, 10, 64)
 	if err != nil {
 		err := fmt.Errorf("invalid service version '%s': %v", rawVersion, err)
-		return invalidRequestError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	var flags basictypes.ServiceUpdateOptions
@@ -311,7 +298,7 @@ func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r
 	version, err := strconv.ParseUint(rawVersion, 10, 64)
 	if err != nil {
 		err := fmt.Errorf("invalid node version '%s': %v", rawVersion, err)
-		return invalidRequestError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	if err := sr.backend.UpdateNode(vars["id"], version, node); err != nil {
@@ -417,13 +404,13 @@ func (sr *swarmRouter) getSecret(ctx context.Context, w http.ResponseWriter, r *
 func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	var secret types.SecretSpec
 	if err := json.NewDecoder(r.Body).Decode(&secret); err != nil {
-		return invalidRequestError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	rawVersion := r.URL.Query().Get("version")
 	version, err := strconv.ParseUint(rawVersion, 10, 64)
 	if err != nil {
-		return invalidRequestError{fmt.Errorf("invalid secret version")}
+		return errdefs.InvalidParameter(fmt.Errorf("invalid secret version"))
 	}
 
 	id := vars["id"]
@@ -484,13 +471,13 @@ func (sr *swarmRouter) getConfig(ctx context.Context, w http.ResponseWriter, r *
 func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	var config types.ConfigSpec
 	if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
-		return invalidRequestError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	rawVersion := r.URL.Query().Get("version")
 	version, err := strconv.ParseUint(rawVersion, 10, 64)
 	if err != nil {
-		return invalidRequestError{fmt.Errorf("invalid config version")}
+		return errdefs.InvalidParameter(fmt.Errorf("invalid config version"))
 	}
 
 	id := vars["id"]

+ 6 - 5
builder/dockerfile/builder.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/container"
@@ -225,7 +226,7 @@ func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*buil
 		if instructions.IsUnknownInstruction(err) {
 			buildsFailed.WithValues(metricsUnknownInstructionError).Inc()
 		}
-		return nil, validationError{err}
+		return nil, errdefs.InvalidParameter(err)
 	}
 	if b.options.Target != "" {
 		targetIx, found := instructions.HasStage(stages, b.options.Target)
@@ -363,7 +364,7 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
 
 	dockerfile, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
 	if err != nil {
-		return nil, validationError{err}
+		return nil, errdefs.InvalidParameter(err)
 	}
 
 	os := runtime.GOOS
@@ -378,7 +379,7 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
 	// ensure that the commands are valid
 	for _, n := range dockerfile.AST.Children {
 		if !validCommitCommands[n.Value] {
-			return nil, validationError{errors.Errorf("%s is not a valid change command", n.Value)}
+			return nil, errdefs.InvalidParameter(errors.Errorf("%s is not a valid change command", n.Value))
 		}
 	}
 
@@ -390,7 +391,7 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
 	for _, n := range dockerfile.AST.Children {
 		cmd, err := instructions.ParseCommand(n)
 		if err != nil {
-			return nil, validationError{err}
+			return nil, errdefs.InvalidParameter(err)
 		}
 		commands = append(commands, cmd)
 	}
@@ -402,7 +403,7 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
 	for _, cmd := range commands {
 		err := dispatch(dispatchRequest, cmd)
 		if err != nil {
-			return nil, validationError{err}
+			return nil, errdefs.InvalidParameter(err)
 		}
 		dispatchRequest.state.updateRunConfig()
 	}

+ 2 - 1
builder/dockerfile/dispatchers.go

@@ -15,6 +15,7 @@ import (
 	"strings"
 
 	"github.com/docker/docker/api"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/builder"
@@ -510,7 +511,7 @@ func dispatchStopSignal(d dispatchRequest, c *instructions.StopSignalCommand) er
 
 	_, err := signal.ParseSignal(c.Signal)
 	if err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 	d.state.runConfig.StopSignal = c.Signal
 	return d.builder.commit(d.state, fmt.Sprintf("STOPSIGNAL %v", c.Signal))

+ 0 - 15
builder/dockerfile/errors.go

@@ -1,15 +0,0 @@
-package dockerfile
-
-type validationError struct {
-	err error
-}
-
-func (e validationError) Error() string {
-	return e.err.Error()
-}
-
-func (e validationError) InvalidParameter() {}
-
-func (e validationError) Cause() error {
-	return e.err
-}

+ 3 - 2
builder/dockerfile/evaluator.go

@@ -24,6 +24,7 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder/dockerfile/instructions"
@@ -37,7 +38,7 @@ func dispatch(d dispatchRequest, cmd instructions.Command) (err error) {
 		optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
 		err := c.CheckPlatform(optionsOS)
 		if err != nil {
-			return validationError{err}
+			return errdefs.InvalidParameter(err)
 		}
 	}
 	runConfigEnv := d.state.runConfig.Env
@@ -48,7 +49,7 @@ func dispatch(d dispatchRequest, cmd instructions.Command) (err error) {
 			return d.shlex.ProcessWord(word, envs)
 		})
 		if err != nil {
-			return validationError{err}
+			return errdefs.InvalidParameter(err)
 		}
 	}
 

+ 0 - 75
builder/remotecontext/errors.go

@@ -1,75 +0,0 @@
-package remotecontext
-
-type notFoundError string
-
-func (e notFoundError) Error() string {
-	return string(e)
-}
-
-func (notFoundError) NotFound() {}
-
-type requestError string
-
-func (e requestError) Error() string {
-	return string(e)
-}
-
-func (e requestError) InvalidParameter() {}
-
-type unauthorizedError string
-
-func (e unauthorizedError) Error() string {
-	return string(e)
-}
-
-func (unauthorizedError) Unauthorized() {}
-
-type forbiddenError string
-
-func (e forbiddenError) Error() string {
-	return string(e)
-}
-
-func (forbiddenError) Forbidden() {}
-
-type dnsError struct {
-	cause error
-}
-
-func (e dnsError) Error() string {
-	return e.cause.Error()
-}
-
-func (e dnsError) NotFound() {}
-
-func (e dnsError) Cause() error {
-	return e.cause
-}
-
-type systemError struct {
-	cause error
-}
-
-func (e systemError) Error() string {
-	return e.cause.Error()
-}
-
-func (e systemError) SystemError() {}
-
-func (e systemError) Cause() error {
-	return e.cause
-}
-
-type unknownError struct {
-	cause error
-}
-
-func (e unknownError) Error() string {
-	return e.cause.Error()
-}
-
-func (unknownError) Unknown() {}
-
-func (e unknownError) Cause() error {
-	return e.cause
-}

+ 11 - 10
builder/remotecontext/remote.go

@@ -10,6 +10,7 @@ import (
 	"net/url"
 	"regexp"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/pkg/errors"
 )
@@ -26,7 +27,7 @@ var mimeRe = regexp.MustCompile(acceptableRemoteMIME)
 func downloadRemote(remoteURL string) (string, io.ReadCloser, error) {
 	response, err := GetWithStatusError(remoteURL)
 	if err != nil {
-		return "", nil, fmt.Errorf("error downloading remote context %s: %v", remoteURL, err)
+		return "", nil, errors.Wrapf(err, "error downloading remote context %s", remoteURL)
 	}
 
 	contentType, contextReader, err := inspectResponse(
@@ -35,7 +36,7 @@ func downloadRemote(remoteURL string) (string, io.ReadCloser, error) {
 		response.ContentLength)
 	if err != nil {
 		response.Body.Close()
-		return "", nil, fmt.Errorf("error detecting content type for remote %s: %v", remoteURL, err)
+		return "", nil, errors.Wrapf(err, "error detecting content type for remote %s", remoteURL)
 	}
 
 	return contentType, ioutils.NewReadCloserWrapper(contextReader, response.Body.Close), nil
@@ -47,10 +48,10 @@ func GetWithStatusError(address string) (resp *http.Response, err error) {
 	if resp, err = http.Get(address); err != nil {
 		if uerr, ok := err.(*url.Error); ok {
 			if derr, ok := uerr.Err.(*net.DNSError); ok && !derr.IsTimeout {
-				return nil, dnsError{err}
+				return nil, errdefs.NotFound(err)
 			}
 		}
-		return nil, systemError{err}
+		return nil, errdefs.System(err)
 	}
 	if resp.StatusCode < 400 {
 		return resp, nil
@@ -59,21 +60,21 @@ func GetWithStatusError(address string) (resp *http.Response, err error) {
 	body, err := ioutil.ReadAll(resp.Body)
 	resp.Body.Close()
 	if err != nil {
-		return nil, errors.Wrap(systemError{err}, msg+": error reading body")
+		return nil, errdefs.System(errors.New(msg + ": error reading body"))
 	}
 
 	msg += ": " + string(bytes.TrimSpace(body))
 	switch resp.StatusCode {
 	case http.StatusNotFound:
-		return nil, notFoundError(msg)
+		return nil, errdefs.NotFound(errors.New(msg))
 	case http.StatusBadRequest:
-		return nil, requestError(msg)
+		return nil, errdefs.InvalidParameter(errors.New(msg))
 	case http.StatusUnauthorized:
-		return nil, unauthorizedError(msg)
+		return nil, errdefs.Unauthorized(errors.New(msg))
 	case http.StatusForbidden:
-		return nil, forbiddenError(msg)
+		return nil, errdefs.Forbidden(errors.New(msg))
 	}
-	return nil, unknownError{errors.New(msg)}
+	return nil, errdefs.Unknown(errors.New(msg))
 }
 
 // inspectResponse looks into the http response data at r to determine whether its

+ 9 - 8
daemon/archive.go

@@ -5,6 +5,7 @@ import (
 	"os"
 	"strings"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/pkg/archive"
@@ -54,7 +55,7 @@ func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, err
 
 	// Make sure an online file-system operation is permitted.
 	if err := daemon.isOnlineFSOperationPermitted(container); err != nil {
-		return nil, systemError{err}
+		return nil, errdefs.System(err)
 	}
 
 	data, err := daemon.containerCopy(container, res)
@@ -65,7 +66,7 @@ func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, err
 	if os.IsNotExist(err) {
 		return nil, containerFileNotFound{res, name}
 	}
-	return nil, systemError{err}
+	return nil, errdefs.System(err)
 }
 
 // ContainerStatPath stats the filesystem resource at the specified path in the
@@ -78,7 +79,7 @@ func (daemon *Daemon) ContainerStatPath(name string, path string) (stat *types.C
 
 	// Make sure an online file-system operation is permitted.
 	if err := daemon.isOnlineFSOperationPermitted(container); err != nil {
-		return nil, systemError{err}
+		return nil, errdefs.System(err)
 	}
 
 	stat, err = daemon.containerStatPath(container, path)
@@ -89,7 +90,7 @@ func (daemon *Daemon) ContainerStatPath(name string, path string) (stat *types.C
 	if os.IsNotExist(err) {
 		return nil, containerFileNotFound{path, name}
 	}
-	return nil, systemError{err}
+	return nil, errdefs.System(err)
 }
 
 // ContainerArchivePath creates an archive of the filesystem resource at the
@@ -103,7 +104,7 @@ func (daemon *Daemon) ContainerArchivePath(name string, path string) (content io
 
 	// Make sure an online file-system operation is permitted.
 	if err := daemon.isOnlineFSOperationPermitted(container); err != nil {
-		return nil, nil, systemError{err}
+		return nil, nil, errdefs.System(err)
 	}
 
 	content, stat, err = daemon.containerArchivePath(container, path)
@@ -114,7 +115,7 @@ func (daemon *Daemon) ContainerArchivePath(name string, path string) (content io
 	if os.IsNotExist(err) {
 		return nil, nil, containerFileNotFound{path, name}
 	}
-	return nil, nil, systemError{err}
+	return nil, nil, errdefs.System(err)
 }
 
 // ContainerExtractToDir extracts the given archive to the specified location
@@ -131,7 +132,7 @@ func (daemon *Daemon) ContainerExtractToDir(name, path string, copyUIDGID, noOve
 
 	// Make sure an online file-system operation is permitted.
 	if err := daemon.isOnlineFSOperationPermitted(container); err != nil {
-		return systemError{err}
+		return errdefs.System(err)
 	}
 
 	err = daemon.containerExtractToDir(container, path, copyUIDGID, noOverwriteDirNonDir, content)
@@ -142,7 +143,7 @@ func (daemon *Daemon) ContainerExtractToDir(name, path string, copyUIDGID, noOve
 	if os.IsNotExist(err) {
 		return containerFileNotFound{path, name}
 	}
-	return systemError{err}
+	return errdefs.System(err)
 }
 
 // containerStatPath stats the filesystem resource at the specified path in this

+ 4 - 3
daemon/attach.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"io"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container/stream"
@@ -22,7 +23,7 @@ func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerA
 	if c.DetachKeys != "" {
 		keys, err = term.ToBytes(c.DetachKeys)
 		if err != nil {
-			return validationError{errors.Errorf("Invalid detach keys (%s) provided", c.DetachKeys)}
+			return errdefs.InvalidParameter(errors.Errorf("Invalid detach keys (%s) provided", c.DetachKeys))
 		}
 	}
 
@@ -32,11 +33,11 @@ func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerA
 	}
 	if container.IsPaused() {
 		err := fmt.Errorf("container %s is paused, unpause the container before attach", prefixOrName)
-		return stateConflictError{err}
+		return errdefs.Conflict(err)
 	}
 	if container.IsRestarting() {
 		err := fmt.Errorf("container %s is restarting, wait until the container is running", prefixOrName)
-		return stateConflictError{err}
+		return errdefs.Conflict(err)
 	}
 
 	cfg := stream.AttachConfig{

+ 0 - 56
daemon/cluster/errors.go

@@ -20,48 +20,6 @@ const (
 	errSwarmNotManager notAvailableError = "This node is not a swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager."
 )
 
-type notFoundError struct {
-	cause error
-}
-
-func (e notFoundError) Error() string {
-	return e.cause.Error()
-}
-
-func (e notFoundError) NotFound() {}
-
-func (e notFoundError) Cause() error {
-	return e.cause
-}
-
-type ambiguousResultsError struct {
-	cause error
-}
-
-func (e ambiguousResultsError) Error() string {
-	return e.cause.Error()
-}
-
-func (e ambiguousResultsError) InvalidParameter() {}
-
-func (e ambiguousResultsError) Cause() error {
-	return e.cause
-}
-
-type convertError struct {
-	cause error
-}
-
-func (e convertError) Error() string {
-	return e.cause.Error()
-}
-
-func (e convertError) InvalidParameter() {}
-
-func (e convertError) Cause() error {
-	return e.cause
-}
-
 type notAllowedError string
 
 func (e notAllowedError) Error() string {
@@ -70,20 +28,6 @@ func (e notAllowedError) Error() string {
 
 func (e notAllowedError) Forbidden() {}
 
-type validationError struct {
-	cause error
-}
-
-func (e validationError) Error() string {
-	return e.cause.Error()
-}
-
-func (e validationError) InvalidParameter() {}
-
-func (e validationError) Cause() error {
-	return e.cause
-}
-
 type notAvailableError string
 
 func (e notAvailableError) Error() string {

+ 12 - 11
daemon/cluster/helpers.go

@@ -3,6 +3,7 @@ package cluster
 import (
 	"fmt"
 
+	"github.com/docker/docker/api/errdefs"
 	swarmapi "github.com/docker/swarmkit/api"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
@@ -48,11 +49,11 @@ func getNode(ctx context.Context, c swarmapi.ControlClient, input string) (*swar
 
 	if len(rl.Nodes) == 0 {
 		err := fmt.Errorf("node %s not found", input)
-		return nil, notFoundError{err}
+		return nil, errdefs.NotFound(err)
 	}
 
 	if l := len(rl.Nodes); l > 1 {
-		return nil, ambiguousResultsError{fmt.Errorf("node %s is ambiguous (%d matches found)", input, l)}
+		return nil, errdefs.InvalidParameter(fmt.Errorf("node %s is ambiguous (%d matches found)", input, l))
 	}
 
 	return rl.Nodes[0], nil
@@ -84,11 +85,11 @@ func getService(ctx context.Context, c swarmapi.ControlClient, input string, ins
 
 	if len(rl.Services) == 0 {
 		err := fmt.Errorf("service %s not found", input)
-		return nil, notFoundError{err}
+		return nil, errdefs.NotFound(err)
 	}
 
 	if l := len(rl.Services); l > 1 {
-		return nil, ambiguousResultsError{fmt.Errorf("service %s is ambiguous (%d matches found)", input, l)}
+		return nil, errdefs.InvalidParameter(fmt.Errorf("service %s is ambiguous (%d matches found)", input, l))
 	}
 
 	if !insertDefaults {
@@ -128,11 +129,11 @@ func getTask(ctx context.Context, c swarmapi.ControlClient, input string) (*swar
 
 	if len(rl.Tasks) == 0 {
 		err := fmt.Errorf("task %s not found", input)
-		return nil, notFoundError{err}
+		return nil, errdefs.NotFound(err)
 	}
 
 	if l := len(rl.Tasks); l > 1 {
-		return nil, ambiguousResultsError{fmt.Errorf("task %s is ambiguous (%d matches found)", input, l)}
+		return nil, errdefs.InvalidParameter(fmt.Errorf("task %s is ambiguous (%d matches found)", input, l))
 	}
 
 	return rl.Tasks[0], nil
@@ -164,11 +165,11 @@ func getSecret(ctx context.Context, c swarmapi.ControlClient, input string) (*sw
 
 	if len(rl.Secrets) == 0 {
 		err := fmt.Errorf("secret %s not found", input)
-		return nil, notFoundError{err}
+		return nil, errdefs.NotFound(err)
 	}
 
 	if l := len(rl.Secrets); l > 1 {
-		return nil, ambiguousResultsError{fmt.Errorf("secret %s is ambiguous (%d matches found)", input, l)}
+		return nil, errdefs.InvalidParameter(fmt.Errorf("secret %s is ambiguous (%d matches found)", input, l))
 	}
 
 	return rl.Secrets[0], nil
@@ -200,11 +201,11 @@ func getConfig(ctx context.Context, c swarmapi.ControlClient, input string) (*sw
 
 	if len(rl.Configs) == 0 {
 		err := fmt.Errorf("config %s not found", input)
-		return nil, notFoundError{err}
+		return nil, errdefs.NotFound(err)
 	}
 
 	if l := len(rl.Configs); l > 1 {
-		return nil, ambiguousResultsError{fmt.Errorf("config %s is ambiguous (%d matches found)", input, l)}
+		return nil, errdefs.InvalidParameter(fmt.Errorf("config %s is ambiguous (%d matches found)", input, l))
 	}
 
 	return rl.Configs[0], nil
@@ -238,7 +239,7 @@ func getNetwork(ctx context.Context, c swarmapi.ControlClient, input string) (*s
 	}
 
 	if l := len(rl.Networks); l > 1 {
-		return nil, ambiguousResultsError{fmt.Errorf("network %s is ambiguous (%d matches found)", input, l)}
+		return nil, errdefs.InvalidParameter(fmt.Errorf("network %s is ambiguous (%d matches found)", input, l))
 	}
 
 	return rl.Networks[0], nil

+ 2 - 1
daemon/cluster/networks.go

@@ -3,6 +3,7 @@ package cluster
 import (
 	"fmt"
 
+	"github.com/docker/docker/api/errdefs"
 	apitypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/network"
 	types "github.com/docker/docker/api/types/swarm"
@@ -298,7 +299,7 @@ func (c *Cluster) populateNetworkID(ctx context.Context, client swarmapi.Control
 				// and use its id for the request.
 				apiNetwork, err = getNetwork(ctx, client, ln.Name())
 				if err != nil {
-					return errors.Wrap(notFoundError{err}, "could not find the corresponding predefined swarm network")
+					return errors.Wrap(errdefs.NotFound(err), "could not find the corresponding predefined swarm network")
 				}
 				goto setid
 			}

+ 2 - 1
daemon/cluster/nodes.go

@@ -1,6 +1,7 @@
 package cluster
 
 import (
+	"github.com/docker/docker/api/errdefs"
 	apitypes "github.com/docker/docker/api/types"
 	types "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/daemon/cluster/convert"
@@ -64,7 +65,7 @@ func (c *Cluster) UpdateNode(input string, version uint64, spec types.NodeSpec)
 	return c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
 		nodeSpec, err := convert.NodeSpecToGRPC(spec)
 		if err != nil {
-			return convertError{err}
+			return errdefs.InvalidParameter(err)
 		}
 
 		ctx, cancel := c.getRequestContext()

+ 3 - 2
daemon/cluster/services.go

@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/errdefs"
 	apitypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	types "github.com/docker/docker/api/types/swarm"
@@ -128,7 +129,7 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string, queryRe
 
 		serviceSpec, err := convert.ServiceSpecToGRPC(s)
 		if err != nil {
-			return convertError{err}
+			return errdefs.InvalidParameter(err)
 		}
 
 		resp = &apitypes.ServiceCreateResponse{}
@@ -232,7 +233,7 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ
 
 		serviceSpec, err := convert.ServiceSpecToGRPC(spec)
 		if err != nil {
-			return convertError{err}
+			return errdefs.InvalidParameter(err)
 		}
 
 		currentService, err := getService(ctx, state.controlClient, serviceIDOrName, false)

+ 6 - 5
daemon/cluster/swarm.go

@@ -6,6 +6,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	apitypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	types "github.com/docker/docker/api/types/swarm"
@@ -44,7 +45,7 @@ func (c *Cluster) Init(req types.InitRequest) (string, error) {
 	}
 
 	if err := validateAndSanitizeInitRequest(&req); err != nil {
-		return "", validationError{err}
+		return "", errdefs.InvalidParameter(err)
 	}
 
 	listenHost, listenPort, err := resolveListenAddr(req.ListenAddr)
@@ -140,7 +141,7 @@ func (c *Cluster) Join(req types.JoinRequest) error {
 	c.mu.Unlock()
 
 	if err := validateAndSanitizeJoinRequest(&req); err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	listenHost, listenPort, err := resolveListenAddr(req.ListenAddr)
@@ -232,7 +233,7 @@ func (c *Cluster) Update(version uint64, spec types.Spec, flags types.UpdateFlag
 		if spec.Annotations.Name == "" {
 			spec.Annotations.Name = "default"
 		} else if spec.Annotations.Name != "default" {
-			return validationError{errors.New(`swarm spec must be named "default"`)}
+			return errdefs.InvalidParameter(errors.New(`swarm spec must be named "default"`))
 		}
 
 		// In update, client should provide the complete spec of the swarm, including
@@ -240,7 +241,7 @@ func (c *Cluster) Update(version uint64, spec types.Spec, flags types.UpdateFlag
 		// will be used to swarmkit.
 		clusterSpec, err := convert.SwarmSpecToGRPC(spec)
 		if err != nil {
-			return convertError{err}
+			return errdefs.InvalidParameter(err)
 		}
 
 		_, err = state.controlClient.UpdateCluster(
@@ -311,7 +312,7 @@ func (c *Cluster) UnlockSwarm(req types.UnlockRequest) error {
 
 	key, err := encryption.ParseHumanReadableKey(req.UnlockKey)
 	if err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	config := nr.config

+ 3 - 2
daemon/commit.go

@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types/backend"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder/dockerfile"
@@ -136,12 +137,12 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
 
 	if container.IsDead() {
 		err := fmt.Errorf("You cannot commit container %s which is Dead", container.ID)
-		return "", stateConflictError{err}
+		return "", errdefs.Conflict(err)
 	}
 
 	if container.IsRemovalInProgress() {
 		err := fmt.Errorf("You cannot commit container %s which is being removed", container.ID)
-		return "", stateConflictError{err}
+		return "", errdefs.Conflict(err)
 	}
 
 	if c.Pause && !container.IsPaused() {

+ 3 - 2
daemon/container.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/container"
@@ -54,7 +55,7 @@ func (daemon *Daemon) GetContainer(prefixOrName string) (*container.Container, e
 		if indexError == truncindex.ErrNotExist {
 			return nil, containerNotFound(prefixOrName)
 		}
-		return nil, systemError{indexError}
+		return nil, errdefs.System(indexError)
 	}
 	return daemon.containers.Get(containerID), nil
 }
@@ -139,7 +140,7 @@ func (daemon *Daemon) newContainer(name string, operatingSystem string, config *
 		if config.Hostname == "" {
 			config.Hostname, err = os.Hostname()
 			if err != nil {
-				return nil, systemError{err}
+				return nil, errdefs.System(err)
 			}
 		}
 	} else {

+ 2 - 1
daemon/container_linux.go

@@ -3,6 +3,7 @@
 package daemon
 
 import (
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/container"
 )
 
@@ -14,7 +15,7 @@ func (daemon *Daemon) saveApparmorConfig(container *container.Container) error {
 	}
 
 	if err := parseSecurityOpt(container, container.HostConfig); err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	if !container.HostConfig.Privileged {

+ 2 - 1
daemon/container_operations.go

@@ -10,6 +10,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	containertypes "github.com/docker/docker/api/types/container"
 	networktypes "github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/container"
@@ -922,7 +923,7 @@ func (daemon *Daemon) getNetworkedContainer(containerID, connectedContainerID st
 	}
 	if !nc.IsRunning() {
 		err := fmt.Errorf("cannot join network of a non running container: %s", connectedContainerID)
-		return nil, stateConflictError{err}
+		return nil, errdefs.Conflict(err)
 	}
 	if nc.IsRestarting() {
 		return nil, errContainerIsRestarting(connectedContainerID)

+ 2 - 1
daemon/container_operations_unix.go

@@ -11,6 +11,7 @@ import (
 	"strconv"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/links"
 	"github.com/docker/docker/pkg/idtools"
@@ -91,7 +92,7 @@ func (daemon *Daemon) getPidContainer(container *container.Container) (*containe
 
 func containerIsRunning(c *container.Container) error {
 	if !c.IsRunning() {
-		return stateConflictError{errors.Errorf("container %s is not running", c.ID)}
+		return errdefs.Conflict(errors.Errorf("container %s is not running", c.ID))
 	}
 	return nil
 }

+ 8 - 7
daemon/create.go

@@ -9,6 +9,7 @@ import (
 
 	"github.com/pkg/errors"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
 	networktypes "github.com/docker/docker/api/types/network"
@@ -36,7 +37,7 @@ func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (conta
 func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (containertypes.ContainerCreateCreatedBody, error) {
 	start := time.Now()
 	if params.Config == nil {
-		return containertypes.ContainerCreateCreatedBody{}, validationError{errors.New("Config cannot be empty in order to create a container")}
+		return containertypes.ContainerCreateCreatedBody{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container"))
 	}
 
 	os := runtime.GOOS
@@ -55,12 +56,12 @@ func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, manage
 
 	warnings, err := daemon.verifyContainerSettings(os, params.HostConfig, params.Config, false)
 	if err != nil {
-		return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, validationError{err}
+		return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err)
 	}
 
 	err = verifyNetworkingConfig(params.NetworkingConfig)
 	if err != nil {
-		return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, validationError{err}
+		return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err)
 	}
 
 	if params.HostConfig == nil {
@@ -68,7 +69,7 @@ func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, manage
 	}
 	err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares)
 	if err != nil {
-		return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, validationError{err}
+		return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err)
 	}
 
 	container, err := daemon.create(params, managed)
@@ -115,11 +116,11 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
 	}
 
 	if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil {
-		return nil, validationError{err}
+		return nil, errdefs.InvalidParameter(err)
 	}
 
 	if err := daemon.mergeAndVerifyLogConfig(&params.HostConfig.LogConfig); err != nil {
-		return nil, validationError{err}
+		return nil, errdefs.InvalidParameter(err)
 	}
 
 	if container, err = daemon.newContainer(params.Name, os, params.Config, params.HostConfig, imgID, managed); err != nil {
@@ -158,7 +159,7 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
 
 	// Set RWLayer for container after mount labels have been set
 	if err := daemon.setRWLayer(container); err != nil {
-		return nil, systemError{err}
+		return nil, errdefs.System(err)
 	}
 
 	rootIDs := daemon.idMappings.RootPair()

+ 4 - 3
daemon/delete.go

@@ -7,6 +7,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/layer"
@@ -31,7 +32,7 @@ func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig)
 	// Container state RemovalInProgress should be used to avoid races.
 	if inProgress := container.SetRemovalInProgress(); inProgress {
 		err := fmt.Errorf("removal of container %s is already in progress", name)
-		return stateConflictError{err}
+		return errdefs.Conflict(err)
 	}
 	defer container.ResetRemovalInProgress()
 
@@ -87,7 +88,7 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
 				procedure = "Unpause and then " + strings.ToLower(procedure)
 			}
 			err := fmt.Errorf("You cannot remove a %s container %s. %s", state, container.ID, procedure)
-			return stateConflictError{err}
+			return errdefs.Conflict(err)
 		}
 		if err := daemon.Kill(container); err != nil {
 			return fmt.Errorf("Could not kill running container %s, cannot remove - %v", container.ID, err)
@@ -164,7 +165,7 @@ func (daemon *Daemon) VolumeRm(name string, force bool) error {
 
 	err = daemon.volumeRm(v)
 	if err != nil && volumestore.IsInUse(err) {
-		return stateConflictError{err}
+		return errdefs.Conflict(err)
 	}
 
 	if err == nil || force {

+ 6 - 75
daemon/errors.go

@@ -5,12 +5,13 @@ import (
 	"strings"
 	"syscall"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/pkg/errors"
 	"google.golang.org/grpc"
 )
 
 func errNotRunning(id string) error {
-	return stateConflictError{errors.Errorf("Container %s is not running", id)}
+	return errdefs.Conflict(errors.Errorf("Container %s is not running", id))
 }
 
 func containerNotFound(id string) error {
@@ -32,23 +33,9 @@ func (e objNotFoundError) Error() string {
 
 func (e objNotFoundError) NotFound() {}
 
-type stateConflictError struct {
-	cause error
-}
-
-func (e stateConflictError) Error() string {
-	return e.cause.Error()
-}
-
-func (e stateConflictError) Cause() error {
-	return e.cause
-}
-
-func (e stateConflictError) Conflict() {}
-
 func errContainerIsRestarting(containerID string) error {
 	cause := errors.Errorf("Container %s is restarting, wait until the container is running", containerID)
-	return stateConflictError{cause}
+	return errdefs.Conflict(cause)
 }
 
 func errExecNotFound(id string) error {
@@ -57,12 +44,12 @@ func errExecNotFound(id string) error {
 
 func errExecPaused(id string) error {
 	cause := errors.Errorf("Container %s is paused, unpause the container before exec", id)
-	return stateConflictError{cause}
+	return errdefs.Conflict(cause)
 }
 
 func errNotPaused(id string) error {
 	cause := errors.Errorf("Container %s is already paused", id)
-	return stateConflictError{cause}
+	return errdefs.Conflict(cause)
 }
 
 type nameConflictError struct {
@@ -76,34 +63,6 @@ func (e nameConflictError) Error() string {
 
 func (nameConflictError) Conflict() {}
 
-type validationError struct {
-	cause error
-}
-
-func (e validationError) Error() string {
-	return e.cause.Error()
-}
-
-func (e validationError) InvalidParameter() {}
-
-func (e validationError) Cause() error {
-	return e.cause
-}
-
-type notAllowedError struct {
-	cause error
-}
-
-func (e notAllowedError) Error() string {
-	return e.cause.Error()
-}
-
-func (e notAllowedError) Forbidden() {}
-
-func (e notAllowedError) Cause() error {
-	return e.cause
-}
-
 type containerNotModifiedError struct {
 	running bool
 }
@@ -117,20 +76,6 @@ func (e containerNotModifiedError) Error() string {
 
 func (e containerNotModifiedError) NotModified() {}
 
-type systemError struct {
-	cause error
-}
-
-func (e systemError) Error() string {
-	return e.cause.Error()
-}
-
-func (e systemError) SystemError() {}
-
-func (e systemError) Cause() error {
-	return e.cause
-}
-
 type invalidIdentifier string
 
 func (e invalidIdentifier) Error() string {
@@ -172,20 +117,6 @@ func (e invalidFilter) Error() string {
 
 func (e invalidFilter) InvalidParameter() {}
 
-type unknownError struct {
-	cause error
-}
-
-func (e unknownError) Error() string {
-	return e.cause.Error()
-}
-
-func (unknownError) Unknown() {}
-
-func (e unknownError) Cause() error {
-	return e.cause
-}
-
 type startInvalidConfigError string
 
 func (e startInvalidConfigError) Error() string {
@@ -199,7 +130,7 @@ func translateContainerdStartErr(cmd string, setExitCode func(int), err error) e
 	contains := func(s1, s2 string) bool {
 		return strings.Contains(strings.ToLower(s1), s2)
 	}
-	var retErr error = unknownError{errors.New(errDesc)}
+	var retErr = errdefs.Unknown(errors.New(errDesc))
 	// if we receive an internal error from the initial start of a container then lets
 	// return it instead of entering the restart loop
 	// set to 127 for container cmd not found/does not exist)

+ 4 - 3
daemon/exec.go

@@ -8,6 +8,7 @@ import (
 
 	"golang.org/x/net/context"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/container"
@@ -161,12 +162,12 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R
 	if ec.ExitCode != nil {
 		ec.Unlock()
 		err := fmt.Errorf("Error: Exec command %s has already run", ec.ID)
-		return stateConflictError{err}
+		return errdefs.Conflict(err)
 	}
 
 	if ec.Running {
 		ec.Unlock()
-		return stateConflictError{fmt.Errorf("Error: Exec command %s is already running", ec.ID)}
+		return errdefs.Conflict(fmt.Errorf("Error: Exec command %s is already running", ec.ID))
 	}
 	ec.Running = true
 	ec.Unlock()
@@ -267,7 +268,7 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R
 	case err := <-attachErr:
 		if err != nil {
 			if _, ok := err.(term.EscapeError); !ok {
-				return errors.Wrap(systemError{err}, "exec attach failed")
+				return errdefs.System(errors.Wrap(err, "exec attach failed"))
 			}
 			d.LogContainerEvent(c, "exec_detach")
 		}

+ 3 - 2
daemon/export.go

@@ -5,6 +5,7 @@ import (
 	"io"
 	"runtime"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/ioutils"
@@ -24,12 +25,12 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error {
 
 	if container.IsDead() {
 		err := fmt.Errorf("You cannot export container %s which is Dead", container.ID)
-		return stateConflictError{err}
+		return errdefs.Conflict(err)
 	}
 
 	if container.IsRemovalInProgress() {
 		err := fmt.Errorf("You cannot export container %s which is being removed", container.ID)
-		return stateConflictError{err}
+		return errdefs.Conflict(err)
 	}
 
 	data, err := daemon.containerExport(container)

+ 2 - 1
daemon/image.go

@@ -5,6 +5,7 @@ import (
 	"runtime"
 
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/image"
 )
 
@@ -28,7 +29,7 @@ func (e errImageDoesNotExist) NotFound() {}
 func (daemon *Daemon) GetImageIDAndOS(refOrID string) (image.ID, string, error) {
 	ref, err := reference.ParseAnyReference(refOrID)
 	if err != nil {
-		return "", "", validationError{err}
+		return "", "", errdefs.InvalidParameter(err)
 	}
 	namedRef, ok := ref.(reference.Named)
 	if !ok {

+ 2 - 1
daemon/image_delete.go

@@ -6,6 +6,7 @@ import (
 	"time"
 
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/image"
@@ -85,7 +86,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 				// we really want to avoid that the client must
 				// explicitly force its removal.
 				err := errors.Errorf("conflict: unable to remove repository reference %q (must force) - container %s is using its referenced image %s", imageRef, stringid.TruncateID(container.ID), stringid.TruncateID(imgID.String()))
-				return nil, stateConflictError{err}
+				return nil, errdefs.Conflict(err)
 			}
 		}
 

+ 4 - 3
daemon/image_pull.go

@@ -7,6 +7,7 @@ import (
 
 	dist "github.com/docker/distribution"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/distribution"
 	progressutils "github.com/docker/docker/distribution/utils"
@@ -26,7 +27,7 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag, platform string
 
 	ref, err := reference.ParseNormalizedNamed(image)
 	if err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	if tag != "" {
@@ -39,7 +40,7 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag, platform string
 			ref, err = reference.WithTag(ref, tag)
 		}
 		if err != nil {
-			return validationError{err}
+			return errdefs.InvalidParameter(err)
 		}
 	}
 
@@ -96,7 +97,7 @@ func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.Named, au
 	}
 	// makes sure name is not empty or `scratch`
 	if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
-		return nil, false, validationError{err}
+		return nil, false, errdefs.InvalidParameter(err)
 	}
 
 	// get endpoints

+ 5 - 4
daemon/import.go

@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/builder/remotecontext"
@@ -42,16 +43,16 @@ func (daemon *Daemon) ImportImage(src string, repository, os string, tag string,
 		var err error
 		newRef, err = reference.ParseNormalizedNamed(repository)
 		if err != nil {
-			return validationError{err}
+			return errdefs.InvalidParameter(err)
 		}
 		if _, isCanonical := newRef.(reference.Canonical); isCanonical {
-			return validationError{errors.New("cannot import digest reference")}
+			return errdefs.InvalidParameter(errors.New("cannot import digest reference"))
 		}
 
 		if tag != "" {
 			newRef, err = reference.WithTag(newRef, tag)
 			if err != nil {
-				return validationError{err}
+				return errdefs.InvalidParameter(err)
 			}
 		}
 	}
@@ -69,7 +70,7 @@ func (daemon *Daemon) ImportImage(src string, repository, os string, tag string,
 		}
 		u, err := url.Parse(src)
 		if err != nil {
-			return validationError{err}
+			return errdefs.InvalidParameter(err)
 		}
 
 		resp, err = remotecontext.GetWithStatusError(u.String())

+ 3 - 2
daemon/inspect.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	networktypes "github.com/docker/docker/api/types/network"
@@ -188,7 +189,7 @@ func (daemon *Daemon) getInspectData(container *container.Container) (*types.Con
 	// could have been removed, it will cause error if we try to get the metadata,
 	// we can ignore the error if the container is dead.
 	if err != nil && !container.Dead {
-		return nil, systemError{err}
+		return nil, errdefs.System(err)
 	}
 	contJSONBase.GraphDriver.Data = graphDriverData
 
@@ -232,7 +233,7 @@ func (daemon *Daemon) VolumeInspect(name string) (*types.Volume, error) {
 		if volumestore.IsNotExist(err) {
 			return nil, volumeNotFound(name)
 		}
-		return nil, systemError{err}
+		return nil, errdefs.System(err)
 	}
 	apiV := volumeToAPIType(v)
 	apiV.Mountpoint = v.Path()

+ 2 - 1
daemon/list.go

@@ -6,6 +6,7 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/container"
@@ -290,7 +291,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis
 
 	err = psFilters.WalkValues("health", func(value string) error {
 		if !container.IsValidHealthString(value) {
-			return validationError{errors.Errorf("Unrecognised filter value for health: %s", value)}
+			return errdefs.InvalidParameter(errors.Errorf("Unrecognised filter value for health: %s", value))
 		}
 
 		return nil

+ 3 - 2
daemon/logs.go

@@ -7,6 +7,7 @@ import (
 
 	"golang.org/x/net/context"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	containertypes "github.com/docker/docker/api/types/container"
@@ -30,7 +31,7 @@ func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, c
 	})
 
 	if !(config.ShowStdout || config.ShowStderr) {
-		return nil, false, validationError{errors.New("You must choose at least one stream")}
+		return nil, false, errdefs.InvalidParameter(errors.New("You must choose at least one stream"))
 	}
 	container, err := daemon.GetContainer(containerName)
 	if err != nil {
@@ -38,7 +39,7 @@ func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, c
 	}
 
 	if container.RemovalInProgress || container.Dead {
-		return nil, false, stateConflictError{errors.New("can not get logs from container which is dead or marked for removal")}
+		return nil, false, errdefs.Conflict(errors.New("can not get logs from container which is dead or marked for removal"))
 	}
 
 	if container.HostConfig.LogConfig.Type == "none" {

+ 2 - 1
daemon/names.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"strings"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/names"
 	"github.com/docker/docker/pkg/namesgenerator"
@@ -56,7 +57,7 @@ func (daemon *Daemon) generateIDAndName(name string) (string, string, error) {
 
 func (daemon *Daemon) reserveName(id, name string) (string, error) {
 	if !validContainerNamePattern.MatchString(strings.TrimPrefix(name, "/")) {
-		return "", validationError{errors.Errorf("Invalid container name (%s), only %s are allowed", name, validContainerNameChars)}
+		return "", errdefs.InvalidParameter(errors.Errorf("Invalid container name (%s), only %s are allowed", name, validContainerNameChars))
 	}
 	if name[0] != '/' {
 		name = "/" + name

+ 11 - 6
daemon/network.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 	"sync"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/network"
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
@@ -52,13 +53,17 @@ func (daemon *Daemon) FindUniqueNetwork(term string) (libnetwork.Network, error)
 	case len(listByFullName) == 1:
 		return listByFullName[0], nil
 	case len(listByFullName) > 1:
-		return nil, fmt.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))
+		return nil, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found on name)", term, len(listByFullName)))
 	case len(listByPartialID) == 1:
 		return listByPartialID[0], nil
 	case len(listByPartialID) > 1:
-		return nil, fmt.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))
+		return nil, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID)))
 	}
-	return nil, libnetwork.ErrNoSuchNetwork(term)
+
+	// Be very careful to change the error type here, the
+	// libnetwork.ErrNoSuchNetwork error is used by the controller
+	// to retry the creation of the network as managed through the swarm manager
+	return nil, errdefs.NotFound(libnetwork.ErrNoSuchNetwork(term))
 }
 
 // GetNetworkByID function returns a network whose ID matches the given ID.
@@ -264,7 +269,7 @@ func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.N
 func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
 	if runconfig.IsPreDefinedNetwork(create.Name) && !agent {
 		err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name)
-		return nil, notAllowedError{err}
+		return nil, errdefs.Forbidden(err)
 	}
 
 	var warning string
@@ -522,7 +527,7 @@ func (daemon *Daemon) deleteLoadBalancerSandbox(n libnetwork.Network) {
 func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error {
 	if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic {
 		err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name())
-		return notAllowedError{err}
+		return errdefs.Forbidden(err)
 	}
 
 	if dynamic && !nw.Info().Dynamic() {
@@ -532,7 +537,7 @@ func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error {
 			return nil
 		}
 		err := fmt.Errorf("%s is not a dynamic network", nw.Name())
-		return notAllowedError{err}
+		return errdefs.Forbidden(err)
 	}
 
 	if err := nw.Delete(); err != nil {

+ 4 - 3
daemon/rename.go

@@ -3,6 +3,7 @@ package daemon
 import (
 	"strings"
 
+	"github.com/docker/docker/api/errdefs"
 	dockercontainer "github.com/docker/docker/container"
 	"github.com/docker/libnetwork"
 	"github.com/pkg/errors"
@@ -19,7 +20,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
 	)
 
 	if oldName == "" || newName == "" {
-		return validationError{errors.New("Neither old nor new names may be empty")}
+		return errdefs.InvalidParameter(errors.New("Neither old nor new names may be empty"))
 	}
 
 	if newName[0] != '/' {
@@ -38,13 +39,13 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
 	oldIsAnonymousEndpoint := container.NetworkSettings.IsAnonymousEndpoint
 
 	if oldName == newName {
-		return validationError{errors.New("Renaming a container with the same name as its current name")}
+		return errdefs.InvalidParameter(errors.New("Renaming a container with the same name as its current name"))
 	}
 
 	links := map[string]*dockercontainer.Container{}
 	for k, v := range daemon.linkIndex.children(container) {
 		if !strings.HasPrefix(k, oldName) {
-			return validationError{errors.Errorf("Linked container %s does not match parent %s", k, oldName)}
+			return errdefs.InvalidParameter(errors.Errorf("Linked container %s does not match parent %s", k, oldName))
 		}
 		links[strings.TrimPrefix(k, oldName)] = v
 	}

+ 14 - 13
daemon/start.go

@@ -5,6 +5,7 @@ import (
 	"runtime"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
@@ -15,7 +16,7 @@ import (
 // ContainerStart starts a container.
 func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
 	if checkpoint != "" && !daemon.HasExperimental() {
-		return validationError{errors.New("checkpoint is only supported in experimental mode")}
+		return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
 	}
 
 	container, err := daemon.GetContainer(name)
@@ -28,7 +29,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
 		defer container.Unlock()
 
 		if container.Paused {
-			return stateConflictError{errors.New("cannot start a paused container, try unpause instead")}
+			return errdefs.Conflict(errors.New("cannot start a paused container, try unpause instead"))
 		}
 
 		if container.Running {
@@ -36,7 +37,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
 		}
 
 		if container.RemovalInProgress || container.Dead {
-			return stateConflictError{errors.New("container is marked for removal and cannot be started")}
+			return errdefs.Conflict(errors.New("container is marked for removal and cannot be started"))
 		}
 		return nil
 	}
@@ -53,13 +54,13 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
 			logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12")
 			oldNetworkMode := container.HostConfig.NetworkMode
 			if err := daemon.setSecurityOptions(container, hostConfig); err != nil {
-				return validationError{err}
+				return errdefs.InvalidParameter(err)
 			}
 			if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil {
-				return validationError{err}
+				return errdefs.InvalidParameter(err)
 			}
 			if err := daemon.setHostConfig(container, hostConfig); err != nil {
-				return validationError{err}
+				return errdefs.InvalidParameter(err)
 			}
 			newNetworkMode := container.HostConfig.NetworkMode
 			if string(oldNetworkMode) != string(newNetworkMode) {
@@ -67,27 +68,27 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
 				// old networks. It is a deprecated feature and has been removed in Docker 1.12
 				container.NetworkSettings.Networks = nil
 				if err := container.CheckpointTo(daemon.containersReplica); err != nil {
-					return systemError{err}
+					return errdefs.System(err)
 				}
 			}
 			container.InitDNSHostConfig()
 		}
 	} else {
 		if hostConfig != nil {
-			return validationError{errors.New("Supplying a hostconfig on start is not supported. It should be supplied on create")}
+			return errdefs.InvalidParameter(errors.New("Supplying a hostconfig on start is not supported. It should be supplied on create"))
 		}
 	}
 
 	// check if hostConfig is in line with the current system settings.
 	// It may happen cgroups are umounted or the like.
 	if _, err = daemon.verifyContainerSettings(container.OS, container.HostConfig, nil, false); err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 	// Adapt for old containers in case we have updates in this function and
 	// old containers never have chance to call the new function in create stage.
 	if hostConfig != nil {
 		if err := daemon.adaptContainerSettings(container.HostConfig, false); err != nil {
-			return validationError{err}
+			return errdefs.InvalidParameter(err)
 		}
 	}
 
@@ -111,12 +112,12 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
 	}
 
 	if container.RemovalInProgress || container.Dead {
-		return stateConflictError{errors.New("container is marked for removal and cannot be started")}
+		return errdefs.Conflict(errors.New("container is marked for removal and cannot be started"))
 	}
 
 	if checkpointDir != "" {
 		// TODO(mlaventure): how would we support that?
-		return notAllowedError{errors.New("custom checkpointdir is not supported")}
+		return errdefs.Forbidden(errors.New("custom checkpointdir is not supported"))
 	}
 
 	// if we encounter an error during start we need to ensure that any other
@@ -155,7 +156,7 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
 
 	spec, err := daemon.createSpec(container)
 	if err != nil {
-		return systemError{err}
+		return errdefs.System(err)
 	}
 
 	if resetRestartManager {

+ 2 - 1
daemon/start_unix.go

@@ -8,6 +8,7 @@ import (
 	"path/filepath"
 
 	"github.com/containerd/containerd/linux/runctypes"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/container"
 	"github.com/pkg/errors"
 )
@@ -16,7 +17,7 @@ func (daemon *Daemon) getRuntimeScript(container *container.Container) (string,
 	name := container.HostConfig.Runtime
 	rt := daemon.configStore.GetRuntime(name)
 	if rt == nil {
-		return "", validationError{errors.Errorf("no such runtime '%s'", name)}
+		return "", errdefs.InvalidParameter(errors.Errorf("no such runtime '%s'", name))
 	}
 
 	if len(rt.Args) > 0 {

+ 2 - 1
daemon/stop.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	containerpkg "github.com/docker/docker/container"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
@@ -28,7 +29,7 @@ func (daemon *Daemon) ContainerStop(name string, seconds *int) error {
 		seconds = &stopTimeout
 	}
 	if err := daemon.containerStop(container, *seconds); err != nil {
-		return errors.Wrapf(systemError{err}, "cannot stop container: %s", name)
+		return errdefs.System(errors.Wrapf(err, "cannot stop container: %s", name))
 	}
 	return nil
 }

+ 3 - 2
daemon/update.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types/container"
 	"github.com/pkg/errors"
 )
@@ -19,7 +20,7 @@ func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostCon
 
 	warnings, err = daemon.verifyContainerSettings(c.OS, hostConfig, nil, true)
 	if err != nil {
-		return container.ContainerUpdateOKBody{Warnings: warnings}, validationError{err}
+		return container.ContainerUpdateOKBody{Warnings: warnings}, errdefs.InvalidParameter(err)
 	}
 
 	if err := daemon.update(name, hostConfig); err != nil {
@@ -80,7 +81,7 @@ func (daemon *Daemon) update(name string, hostConfig *container.HostConfig) erro
 		if err := daemon.containerd.UpdateResources(context.Background(), container.ID, toContainerdResources(hostConfig.Resources)); err != nil {
 			restoreConfig = true
 			// TODO: it would be nice if containerd responded with better errors here so we can classify this better.
-			return errCannotUpdate(container.ID, systemError{err})
+			return errCannotUpdate(container.ID, errdefs.System(err))
 		}
 	}
 

+ 2 - 1
daemon/volumes.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
 	mounttypes "github.com/docker/docker/api/types/mount"
@@ -175,7 +176,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
 	for _, cfg := range hostConfig.Mounts {
 		mp, err := parser.ParseMountSpec(cfg)
 		if err != nil {
-			return validationError{err}
+			return errdefs.InvalidParameter(err)
 		}
 
 		if binds[mp.Destination] {

+ 2 - 15
distribution/errors.go

@@ -12,6 +12,7 @@ import (
 	"github.com/docker/distribution/registry/api/v2"
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client/auth"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/distribution/xfer"
 	"github.com/sirupsen/logrus"
 )
@@ -85,20 +86,6 @@ func (e notFoundError) Cause() error {
 	return e.cause
 }
 
-type unknownError struct {
-	cause error
-}
-
-func (e unknownError) Error() string {
-	return e.cause.Error()
-}
-
-func (e unknownError) Cause() error {
-	return e.cause
-}
-
-func (e unknownError) Unknown() {}
-
 // TranslatePullError is used to convert an error from a registry pull
 // operation to an error representing the entire pull operation. Any error
 // information which is not used by the returned error gets output to
@@ -121,7 +108,7 @@ func TranslatePullError(err error, ref reference.Named) error {
 		return TranslatePullError(v.Err, ref)
 	}
 
-	return unknownError{err}
+	return errdefs.Unknown(err)
 }
 
 // continueOnError returns true if we should fallback to the next endpoint

+ 11 - 10
libcontainerd/client_daemon.go

@@ -27,10 +27,11 @@ import (
 	"github.com/containerd/containerd/archive"
 	"github.com/containerd/containerd/cio"
 	"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/linux/runctypes"
 	"github.com/containerd/typeurl"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/opencontainers/image-spec/specs-go/v1"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
@@ -181,7 +182,7 @@ func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, run
 
 	bdir, err := prepareBundleDir(filepath.Join(c.stateDir, id), ociSpec)
 	if err != nil {
-		return wrapSystemError(errors.Wrap(err, "prepare bundle dir failed"))
+		return errdefs.System(errors.Wrap(err, "prepare bundle dir failed"))
 	}
 
 	c.logger.WithField("bundle", bdir).WithField("root", ociSpec.Root.Path).Debug("bundle dir created")
@@ -536,11 +537,11 @@ func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDi
 
 	b, err := content.ReadBlob(ctx, c.remote.ContentStore(), img.Target().Digest)
 	if err != nil {
-		return wrapSystemError(errors.Wrapf(err, "failed to retrieve checkpoint data"))
+		return errdefs.System(errors.Wrapf(err, "failed to retrieve checkpoint data"))
 	}
 	var index v1.Index
 	if err := json.Unmarshal(b, &index); err != nil {
-		return wrapSystemError(errors.Wrapf(err, "failed to decode checkpoint data"))
+		return errdefs.System(errors.Wrapf(err, "failed to decode checkpoint data"))
 	}
 
 	var cpDesc *v1.Descriptor
@@ -551,17 +552,17 @@ func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDi
 		}
 	}
 	if cpDesc == nil {
-		return wrapSystemError(errors.Wrapf(err, "invalid checkpoint"))
+		return errdefs.System(errors.Wrapf(err, "invalid checkpoint"))
 	}
 
 	rat, err := c.remote.ContentStore().ReaderAt(ctx, cpDesc.Digest)
 	if err != nil {
-		return wrapSystemError(errors.Wrapf(err, "failed to get checkpoint reader"))
+		return errdefs.System(errors.Wrapf(err, "failed to get checkpoint reader"))
 	}
 	defer rat.Close()
 	_, err = archive.Apply(ctx, checkpointDir, content.NewReader(rat))
 	if err != nil {
-		return wrapSystemError(errors.Wrapf(err, "failed to read checkpoint reader"))
+		return errdefs.System(errors.Wrapf(err, "failed to read checkpoint reader"))
 	}
 
 	return err
@@ -847,14 +848,14 @@ func wrapError(err error) error {
 	switch {
 	case err == nil:
 		return nil
-	case errdefs.IsNotFound(err):
-		return wrapNotFoundError(err)
+	case containerderrors.IsNotFound(err):
+		return errdefs.NotFound(err)
 	}
 
 	msg := err.Error()
 	for _, s := range []string{"container does not exist", "not found", "no such container"} {
 		if strings.Contains(msg, s) {
-			return wrapNotFoundError(err)
+			return errdefs.NotFound(err)
 		}
 	}
 	return err

+ 7 - 40
libcontainerd/errors.go

@@ -1,46 +1,13 @@
 package libcontainerd
 
-import "errors"
+import (
+	"errors"
 
-type liberr struct {
-	err error
-}
+	"github.com/docker/docker/api/errdefs"
+)
 
-func (e liberr) Error() string {
-	return e.err.Error()
-}
+func newNotFoundError(err string) error { return errdefs.NotFound(errors.New(err)) }
 
-func (e liberr) Cause() error {
-	return e.err
-}
+func newInvalidParameterError(err string) error { return errdefs.InvalidParameter(errors.New(err)) }
 
-type notFoundErr struct {
-	liberr
-}
-
-func (notFoundErr) NotFound() {}
-
-func newNotFoundError(err string) error { return notFoundErr{liberr{errors.New(err)}} }
-func wrapNotFoundError(err error) error { return notFoundErr{liberr{err}} }
-
-type invalidParamErr struct {
-	liberr
-}
-
-func (invalidParamErr) InvalidParameter() {}
-
-func newInvalidParameterError(err string) error { return invalidParamErr{liberr{errors.New(err)}} }
-
-type conflictErr struct {
-	liberr
-}
-
-func (conflictErr) ConflictErr() {}
-
-func newConflictError(err string) error { return conflictErr{liberr{errors.New(err)}} }
-
-type sysErr struct {
-	liberr
-}
-
-func wrapSystemError(err error) error { return sysErr{liberr{err}} }
+func newConflictError(err string) error { return errdefs.Conflict(errors.New(err)) }

+ 7 - 6
plugin/backend_linux.go

@@ -14,6 +14,7 @@ import (
 
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/distribution"
@@ -233,7 +234,7 @@ func (pm *Manager) Privileges(ctx context.Context, ref reference.Named, metaHead
 	}
 	var config types.PluginConfig
 	if err := json.Unmarshal(cs.config, &config); err != nil {
-		return nil, systemError{err}
+		return nil, errdefs.System(err)
 	}
 
 	return computePrivileges(config), nil
@@ -255,12 +256,12 @@ func (pm *Manager) Upgrade(ctx context.Context, ref reference.Named, name string
 
 	// revalidate because Pull is public
 	if _, err := reference.ParseNormalizedNamed(name); err != nil {
-		return errors.Wrapf(validationError{err}, "failed to parse %q", name)
+		return errors.Wrapf(errdefs.InvalidParameter(err), "failed to parse %q", name)
 	}
 
 	tmpRootFSDir, err := ioutil.TempDir(pm.tmpDir(), ".rootfs")
 	if err != nil {
-		return errors.Wrap(systemError{err}, "error preparing upgrade")
+		return errors.Wrap(errdefs.System(err), "error preparing upgrade")
 	}
 	defer os.RemoveAll(tmpRootFSDir)
 
@@ -302,17 +303,17 @@ func (pm *Manager) Pull(ctx context.Context, ref reference.Named, name string, m
 	// revalidate because Pull is public
 	nameref, err := reference.ParseNormalizedNamed(name)
 	if err != nil {
-		return errors.Wrapf(validationError{err}, "failed to parse %q", name)
+		return errors.Wrapf(errdefs.InvalidParameter(err), "failed to parse %q", name)
 	}
 	name = reference.FamiliarString(reference.TagNameOnly(nameref))
 
 	if err := pm.config.Store.validateName(name); err != nil {
-		return validationError{err}
+		return errdefs.InvalidParameter(err)
 	}
 
 	tmpRootFSDir, err := ioutil.TempDir(pm.tmpDir(), ".rootfs")
 	if err != nil {
-		return errors.Wrap(systemError{err}, "error preparing pull")
+		return errors.Wrap(errdefs.System(err), "error preparing pull")
 	}
 	defer os.RemoveAll(tmpRootFSDir)
 

+ 0 - 28
plugin/errors.go

@@ -26,34 +26,6 @@ func (name errDisabled) Error() string {
 
 func (name errDisabled) Conflict() {}
 
-type validationError struct {
-	cause error
-}
-
-func (e validationError) Error() string {
-	return e.cause.Error()
-}
-
-func (validationError) Conflict() {}
-
-func (e validationError) Cause() error {
-	return e.cause
-}
-
-type systemError struct {
-	cause error
-}
-
-func (e systemError) Error() string {
-	return e.cause.Error()
-}
-
-func (systemError) SystemError() {}
-
-func (e systemError) Cause() error {
-	return e.cause
-}
-
 type invalidFilter struct {
 	filter string
 	value  []string

+ 5 - 4
plugin/manager_linux.go

@@ -7,6 +7,7 @@ import (
 	"path/filepath"
 	"time"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/daemon/initlayer"
 	"github.com/docker/docker/pkg/containerfs"
@@ -217,12 +218,12 @@ func (pm *Manager) upgradePlugin(p *v2.Plugin, configDigest digest.Digest, blobs
 	// This could happen if the plugin was disabled with `-f` with active mounts.
 	// If there is anything in `orig` is still mounted, this should error out.
 	if err := mount.RecursiveUnmount(orig); err != nil {
-		return systemError{err}
+		return errdefs.System(err)
 	}
 
 	backup := orig + "-old"
 	if err := os.Rename(orig, backup); err != nil {
-		return errors.Wrap(systemError{err}, "error backing up plugin data before upgrade")
+		return errors.Wrap(errdefs.System(err), "error backing up plugin data before upgrade")
 	}
 
 	defer func() {
@@ -248,7 +249,7 @@ func (pm *Manager) upgradePlugin(p *v2.Plugin, configDigest digest.Digest, blobs
 	}()
 
 	if err := os.Rename(tmpRootFSDir, orig); err != nil {
-		return errors.Wrap(systemError{err}, "error upgrading")
+		return errors.Wrap(errdefs.System(err), "error upgrading")
 	}
 
 	p.PluginObj.Config = config
@@ -288,7 +289,7 @@ func (pm *Manager) setupNewPlugin(configDigest digest.Digest, blobsums []digest.
 // createPlugin creates a new plugin. take lock before calling.
 func (pm *Manager) createPlugin(name string, configDigest digest.Digest, blobsums []digest.Digest, rootFSDir string, privileges *types.PluginPrivileges, opts ...CreateOpt) (p *v2.Plugin, err error) {
 	if err := pm.config.Store.validateName(name); err != nil { // todo: this check is wrong. remove store
-		return nil, validationError{err}
+		return nil, errdefs.InvalidParameter(err)
 	}
 
 	config, err := pm.setupNewPlugin(configDigest, blobsums, privileges)

+ 3 - 2
plugin/store.go

@@ -5,6 +5,7 @@ import (
 	"strings"
 
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/plugin/v2"
@@ -144,7 +145,7 @@ func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlug
 	if errors.Cause(err) == plugins.ErrNotFound {
 		return nil, errNotFound(name)
 	}
-	return nil, errors.Wrap(systemError{err}, "legacy plugin")
+	return nil, errors.Wrap(errdefs.System(err), "legacy plugin")
 }
 
 // GetAllManagedPluginsByCap returns a list of managed plugins matching the given capability.
@@ -172,7 +173,7 @@ func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, er
 	if allowV1PluginsFallback {
 		pl, err := plugins.GetAll(capability)
 		if err != nil {
-			return nil, errors.Wrap(systemError{err}, "legacy plugin")
+			return nil, errors.Wrap(errdefs.System(err), "legacy plugin")
 		}
 		for _, p := range pl {
 			result = append(result, p)

+ 8 - 7
registry/auth.go

@@ -10,6 +10,7 @@ import (
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/auth/challenge"
 	"github.com/docker/distribution/registry/client/transport"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/pkg/errors"
@@ -29,7 +30,7 @@ func loginV1(authConfig *types.AuthConfig, apiEndpoint APIEndpoint, userAgent st
 	logrus.Debugf("attempting v1 login to registry endpoint %s", serverAddress)
 
 	if serverAddress == "" {
-		return "", "", systemError{errors.New("server Error: Server Address not set")}
+		return "", "", errdefs.System(errors.New("server Error: Server Address not set"))
 	}
 
 	req, err := http.NewRequest("GET", serverAddress+"users/", nil)
@@ -47,23 +48,23 @@ func loginV1(authConfig *types.AuthConfig, apiEndpoint APIEndpoint, userAgent st
 	defer resp.Body.Close()
 	body, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
-		return "", "", systemError{err}
+		return "", "", errdefs.System(err)
 	}
 
 	switch resp.StatusCode {
 	case http.StatusOK:
 		return "Login Succeeded", "", nil
 	case http.StatusUnauthorized:
-		return "", "", unauthorizedError{errors.New("Wrong login/password, please try again")}
+		return "", "", errdefs.Unauthorized(errors.New("Wrong login/password, please try again"))
 	case http.StatusForbidden:
 		// *TODO: Use registry configuration to determine what this says, if anything?
-		return "", "", notActivatedError{errors.Errorf("Login: Account is not active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)}
+		return "", "", errdefs.Forbidden(errors.Errorf("Login: Account is not active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress))
 	case http.StatusInternalServerError:
 		logrus.Errorf("%s returned status code %d. Response Body :\n%s", req.URL.String(), resp.StatusCode, body)
-		return "", "", systemError{errors.New("Internal Server Error")}
+		return "", "", errdefs.System(errors.New("Internal Server Error"))
 	}
-	return "", "", systemError{errors.Errorf("Login: %s (Code: %d; Headers: %s)", body,
-		resp.StatusCode, resp.Header)}
+	return "", "", errdefs.System(errors.Errorf("Login: %s (Code: %d; Headers: %s)", body,
+		resp.StatusCode, resp.Header))
 }
 
 type loginCredentialStore struct {

+ 2 - 57
registry/errors.go

@@ -4,6 +4,7 @@ import (
 	"net/url"
 
 	"github.com/docker/distribution/registry/api/errcode"
+	"github.com/docker/docker/api/errdefs"
 )
 
 type notFoundError string
@@ -14,62 +15,6 @@ func (e notFoundError) Error() string {
 
 func (notFoundError) NotFound() {}
 
-type validationError struct {
-	cause error
-}
-
-func (e validationError) Error() string {
-	return e.cause.Error()
-}
-
-func (e validationError) InvalidParameter() {}
-
-func (e validationError) Cause() error {
-	return e.cause
-}
-
-type unauthorizedError struct {
-	cause error
-}
-
-func (e unauthorizedError) Error() string {
-	return e.cause.Error()
-}
-
-func (e unauthorizedError) Unauthorized() {}
-
-func (e unauthorizedError) Cause() error {
-	return e.cause
-}
-
-type systemError struct {
-	cause error
-}
-
-func (e systemError) Error() string {
-	return e.cause.Error()
-}
-
-func (e systemError) SystemError() {}
-
-func (e systemError) Cause() error {
-	return e.cause
-}
-
-type notActivatedError struct {
-	cause error
-}
-
-func (e notActivatedError) Error() string {
-	return e.cause.Error()
-}
-
-func (e notActivatedError) Forbidden() {}
-
-func (e notActivatedError) Cause() error {
-	return e.cause
-}
-
 func translateV2AuthError(err error) error {
 	switch e := err.(type) {
 	case *url.Error:
@@ -77,7 +22,7 @@ func translateV2AuthError(err error) error {
 		case errcode.Error:
 			switch e2.Code {
 			case errcode.ErrorCodeUnauthorized:
-				return unauthorizedError{err}
+				return errdefs.Unauthorized(err)
 			}
 		}
 	}

+ 3 - 2
registry/service.go

@@ -11,6 +11,7 @@ import (
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client/auth"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/pkg/errors"
@@ -117,12 +118,12 @@ func (s *DefaultService) Auth(ctx context.Context, authConfig *types.AuthConfig,
 	}
 	u, err := url.Parse(serverAddress)
 	if err != nil {
-		return "", "", validationError{errors.Errorf("unable to parse server address: %v", err)}
+		return "", "", errdefs.InvalidParameter(errors.Errorf("unable to parse server address: %v", err))
 	}
 
 	endpoints, err := s.LookupPushEndpoints(u.Host)
 	if err != nil {
-		return "", "", validationError{err}
+		return "", "", errdefs.InvalidParameter(err)
 	}
 
 	for _, endpoint := range endpoints {

+ 4 - 3
registry/session.go

@@ -19,6 +19,7 @@ import (
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/api/errcode"
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/pkg/ioutils"
@@ -734,20 +735,20 @@ func shouldRedirect(response *http.Response) bool {
 // SearchRepositories performs a search against the remote repository
 func (r *Session) SearchRepositories(term string, limit int) (*registrytypes.SearchResults, error) {
 	if limit < 1 || limit > 100 {
-		return nil, validationError{errors.Errorf("Limit %d is outside the range of [1, 100]", limit)}
+		return nil, errdefs.InvalidParameter(errors.Errorf("Limit %d is outside the range of [1, 100]", limit))
 	}
 	logrus.Debugf("Index server: %s", r.indexEndpoint)
 	u := r.indexEndpoint.String() + "search?q=" + url.QueryEscape(term) + "&n=" + url.QueryEscape(fmt.Sprintf("%d", limit))
 
 	req, err := http.NewRequest("GET", u, nil)
 	if err != nil {
-		return nil, errors.Wrap(validationError{err}, "Error building request")
+		return nil, errors.Wrap(errdefs.InvalidParameter(err), "Error building request")
 	}
 	// Have the AuthTransport send authentication, when logged in.
 	req.Header.Set("X-Docker-Token", "true")
 	res, err := r.client.Do(req)
 	if err != nil {
-		return nil, systemError{err}
+		return nil, errdefs.System(err)
 	}
 	defer res.Body.Close()
 	if res.StatusCode != 200 {

+ 9 - 22
volume/local/local.go

@@ -13,6 +13,7 @@ import (
 	"strings"
 	"sync"
 
+	"github.com/docker/docker/api/errdefs"
 	"github.com/docker/docker/daemon/names"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/mount"
@@ -139,20 +140,6 @@ func (r *Root) Name() string {
 	return volume.DefaultDriverName
 }
 
-type systemError struct {
-	err error
-}
-
-func (e systemError) Error() string {
-	return e.err.Error()
-}
-
-func (e systemError) SystemError() {}
-
-func (e systemError) Cause() error {
-	return e.err
-}
-
 // Create creates a new volume.Volume with the provided name, creating
 // the underlying directory tree required for this volume in the
 // process.
@@ -171,7 +158,7 @@ func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error
 
 	path := r.DataPath(name)
 	if err := idtools.MkdirAllAndChown(path, 0755, r.rootIDs); err != nil {
-		return nil, errors.Wrapf(systemError{err}, "error while creating volume path '%s'", path)
+		return nil, errors.Wrapf(errdefs.System(err), "error while creating volume path '%s'", path)
 	}
 
 	var err error
@@ -197,7 +184,7 @@ func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error
 			return nil, err
 		}
 		if err = ioutil.WriteFile(filepath.Join(filepath.Dir(path), "opts.json"), b, 600); err != nil {
-			return nil, errors.Wrap(systemError{err}, "error while persisting volume options")
+			return nil, errdefs.System(errors.Wrap(err, "error while persisting volume options"))
 		}
 	}
 
@@ -215,11 +202,11 @@ func (r *Root) Remove(v volume.Volume) error {
 
 	lv, ok := v.(*localVolume)
 	if !ok {
-		return systemError{errors.Errorf("unknown volume type %T", v)}
+		return errdefs.System(errors.Errorf("unknown volume type %T", v))
 	}
 
 	if lv.active.count > 0 {
-		return systemError{errors.Errorf("volume has active mounts")}
+		return errdefs.System(errors.Errorf("volume has active mounts"))
 	}
 
 	if err := lv.unmount(); err != nil {
@@ -235,7 +222,7 @@ func (r *Root) Remove(v volume.Volume) error {
 	}
 
 	if !r.scopedPath(realPath) {
-		return systemError{errors.Errorf("Unable to remove a directory of out the Docker root %s: %s", r.scope, realPath)}
+		return errdefs.System(errors.Errorf("Unable to remove a directory of out the Docker root %s: %s", r.scope, realPath))
 	}
 
 	if err := removePath(realPath); err != nil {
@@ -251,7 +238,7 @@ func removePath(path string) error {
 		if os.IsNotExist(err) {
 			return nil
 		}
-		return errors.Wrapf(systemError{err}, "error removing volume path '%s'", path)
+		return errdefs.System(errors.Wrapf(err, "error removing volume path '%s'", path))
 	}
 	return nil
 }
@@ -334,7 +321,7 @@ func (v *localVolume) Mount(id string) (string, error) {
 	if v.opts != nil {
 		if !v.active.mounted {
 			if err := v.mount(); err != nil {
-				return "", systemError{err}
+				return "", errdefs.System(err)
 			}
 			v.active.mounted = true
 		}
@@ -368,7 +355,7 @@ func (v *localVolume) unmount() error {
 	if v.opts != nil {
 		if err := mount.Unmount(v.path); err != nil {
 			if mounted, mErr := mount.Mounted(v.path); mounted || mErr != nil {
-				return errors.Wrapf(systemError{err}, "error while unmounting volume path '%s'", v.path)
+				return errdefs.System(errors.Wrapf(err, "error while unmounting volume path '%s'", v.path))
 			}
 		}
 		v.active.mounted = false