client: Use containerd errdefs to convert http errors

Previously, we were using our own `FromStatusCode` function to map HTTP
status codes to Docker error types. Switch to the containerd code.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski
2025-05-19 14:16:41 +02:00
parent a3bee4156f
commit a3ce441ae0
7 changed files with 144 additions and 3 deletions

View File

@@ -4,8 +4,10 @@ import (
"context"
"errors"
"fmt"
"net/http"
cerrdefs "github.com/containerd/errdefs"
"github.com/containerd/errdefs/pkg/errhttp"
"github.com/docker/docker/api/types/versions"
)
@@ -85,3 +87,43 @@ func (cli *Client) NewVersionError(ctx context.Context, APIrequired, feature str
}
return nil
}
type httpError struct {
err error
errdef error
}
func (e *httpError) Error() string {
return e.err.Error()
}
func (e *httpError) Unwrap() error {
return e.err
}
func (e *httpError) Is(target error) bool {
return errors.Is(e.errdef, target)
}
// httpErrorFromStatusCode creates an errdef error, based on the provided HTTP status-code
func httpErrorFromStatusCode(err error, statusCode int) error {
if err == nil {
return nil
}
base := errhttp.ToNative(statusCode)
if base != nil {
return &httpError{err: err, errdef: base}
}
switch {
case statusCode >= http.StatusOK && statusCode < http.StatusBadRequest:
// it's a client error
return err
case statusCode >= http.StatusBadRequest && statusCode < http.StatusInternalServerError:
return &httpError{err: err, errdef: cerrdefs.ErrInvalidArgument}
case statusCode >= http.StatusInternalServerError && statusCode < 600:
return &httpError{err: err, errdef: cerrdefs.ErrInternal}
default:
return &httpError{err: err, errdef: cerrdefs.ErrUnknown}
}
}

View File

@@ -15,7 +15,6 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/errdefs"
"github.com/pkg/errors"
)
@@ -197,7 +196,7 @@ func (cli *Client) checkResponseErr(serverResp *http.Response) (retErr error) {
return nil
}
defer func() {
retErr = errdefs.FromStatusCode(retErr, serverResp.StatusCode)
retErr = httpErrorFromStatusCode(retErr, serverResp.StatusCode)
}()
var body []byte

View File

@@ -5,6 +5,8 @@ import (
)
// FromStatusCode creates an errdef error, based on the provided HTTP status-code
//
// Deprecated: Use [cerrdefs.ToNative] instead
func FromStatusCode(err error, statusCode int) error {
if err == nil {
return nil

View File

@@ -83,6 +83,7 @@ func TestFromStatusCode(t *testing.T) {
for _, tc := range testCases {
t.Run(http.StatusText(tc.status), func(t *testing.T) {
//nolint:staticcheck // ignore SA1019: FromStatusCode is deprecated
err := FromStatusCode(tc.err, tc.status)
if !tc.check(err) {
t.Errorf("unexpected error-type %T", err)

View File

@@ -30,6 +30,7 @@ require (
github.com/containerd/containerd/v2 v2.0.5
github.com/containerd/continuity v0.4.5
github.com/containerd/errdefs v1.0.0
github.com/containerd/errdefs/pkg v0.3.0
github.com/containerd/fifo v1.1.0
github.com/containerd/log v0.1.0
github.com/containerd/platforms v1.0.0-rc.1
@@ -148,7 +149,6 @@ require (
github.com/container-storage-interface/spec v1.5.0 // indirect
github.com/containerd/accelerated-container-image v1.3.0 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/go-cni v1.1.12 // indirect
github.com/containerd/go-runc v1.1.0 // indirect
github.com/containerd/nydus-snapshotter v0.15.0 // indirect

View File

@@ -0,0 +1,96 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package errhttp provides utility functions for translating errors to
// and from a HTTP context.
//
// The functions ToHTTP and ToNative can be used to map server-side and
// client-side errors to the correct types.
package errhttp
import (
"errors"
"net/http"
"github.com/containerd/errdefs"
"github.com/containerd/errdefs/pkg/internal/cause"
)
// ToHTTP returns the best status code for the given error
func ToHTTP(err error) int {
switch {
case errdefs.IsNotFound(err):
return http.StatusNotFound
case errdefs.IsInvalidArgument(err):
return http.StatusBadRequest
case errdefs.IsConflict(err):
return http.StatusConflict
case errdefs.IsNotModified(err):
return http.StatusNotModified
case errdefs.IsFailedPrecondition(err):
return http.StatusPreconditionFailed
case errdefs.IsUnauthorized(err):
return http.StatusUnauthorized
case errdefs.IsPermissionDenied(err):
return http.StatusForbidden
case errdefs.IsResourceExhausted(err):
return http.StatusTooManyRequests
case errdefs.IsInternal(err):
return http.StatusInternalServerError
case errdefs.IsNotImplemented(err):
return http.StatusNotImplemented
case errdefs.IsUnavailable(err):
return http.StatusServiceUnavailable
case errdefs.IsUnknown(err):
var unexpected cause.ErrUnexpectedStatus
if errors.As(err, &unexpected) && unexpected.Status >= 200 && unexpected.Status < 600 {
return unexpected.Status
}
return http.StatusInternalServerError
default:
return http.StatusInternalServerError
}
}
// ToNative returns the error best matching the HTTP status code
func ToNative(statusCode int) error {
switch statusCode {
case http.StatusNotFound:
return errdefs.ErrNotFound
case http.StatusBadRequest:
return errdefs.ErrInvalidArgument
case http.StatusConflict:
return errdefs.ErrConflict
case http.StatusPreconditionFailed:
return errdefs.ErrFailedPrecondition
case http.StatusUnauthorized:
return errdefs.ErrUnauthenticated
case http.StatusForbidden:
return errdefs.ErrPermissionDenied
case http.StatusNotModified:
return errdefs.ErrNotModified
case http.StatusTooManyRequests:
return errdefs.ErrResourceExhausted
case http.StatusInternalServerError:
return errdefs.ErrInternal
case http.StatusNotImplemented:
return errdefs.ErrNotImplemented
case http.StatusServiceUnavailable:
return errdefs.ErrUnavailable
default:
return cause.ErrUnexpectedStatus{Status: statusCode}
}
}

1
vendor/modules.txt vendored
View File

@@ -420,6 +420,7 @@ github.com/containerd/errdefs
# github.com/containerd/errdefs/pkg v0.3.0
## explicit; go 1.22
github.com/containerd/errdefs/pkg/errgrpc
github.com/containerd/errdefs/pkg/errhttp
github.com/containerd/errdefs/pkg/internal/cause
github.com/containerd/errdefs/pkg/internal/types
# github.com/containerd/fifo v1.1.0