Files
moby/daemon/containerd/registry_errors.go
Paweł Gronowski 53c67be034 daemon/c8d: Log correct error extractOCIErrors
When logging an unmarshal failure in the registry error handling code,
the function was incorrectly logging the uninitialized `derrs` variable
instead of the actual JSON unmarshal error `jerr`.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-12-15 15:56:44 +01:00

96 lines
2.9 KiB
Go

package containerd
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/containerd/containerd/v2/core/remotes/docker"
remoteerrors "github.com/containerd/containerd/v2/core/remotes/errors"
cerrdefs "github.com/containerd/errdefs"
"github.com/containerd/log"
)
func translateRegistryError(ctx context.Context, err error) error {
if err == nil {
// Nothing to do
return nil
}
// Check for registry specific error
var derrs docker.Errors
if !errors.As(err, &derrs) {
var remoteErr remoteerrors.ErrUnexpectedStatus
var derr docker.Error
if errors.As(err, &remoteErr) {
if jerr := json.Unmarshal(remoteErr.Body, &derrs); jerr != nil {
log.G(ctx).WithError(jerr).Debug("unable to unmarshal registry error")
return fmt.Errorf("%w: %w", cerrdefs.ErrUnknown, err)
}
if len(derrs) == 0 && (remoteErr.StatusCode == http.StatusUnauthorized || remoteErr.StatusCode == http.StatusForbidden) {
// Some registries or token servers may use an old deprecated error format
// which only has a "details" field and not the OCI defined "errors" array.
var tokenErr struct {
Details string `json:"details"`
}
if jerr := json.Unmarshal(remoteErr.Body, &tokenErr); jerr == nil && tokenErr.Details != "" {
if remoteErr.StatusCode == http.StatusUnauthorized {
return cerrdefs.ErrUnauthenticated.WithMessage(fmt.Sprintf("%s - %s", docker.ErrorCodeUnauthorized.Message(), tokenErr.Details))
}
return cerrdefs.ErrPermissionDenied.WithMessage(fmt.Sprintf("%s - %s", docker.ErrorCodeDenied.Message(), tokenErr.Details))
}
}
} else if errors.As(err, &derr) {
derrs = append(derrs, derr)
} else {
return err
}
}
var errs []error
for _, err := range derrs {
var derr docker.Error
if errors.As(err, &derr) {
var message string
if derr.Message != "" {
message = derr.Message
} else {
message = derr.Code.Message()
}
if detail, ok := derr.Detail.(string); ok {
message = fmt.Sprintf("%s - %s", message, detail)
}
switch derr.Code {
case docker.ErrorCodeUnsupported:
err = cerrdefs.ErrNotImplemented.WithMessage(message)
case docker.ErrorCodeUnauthorized:
err = cerrdefs.ErrUnauthenticated.WithMessage(message)
case docker.ErrorCodeDenied:
err = cerrdefs.ErrPermissionDenied.WithMessage(message)
case docker.ErrorCodeUnavailable:
err = cerrdefs.ErrUnavailable.WithMessage(message)
case docker.ErrorCodeTooManyRequests:
err = cerrdefs.ErrResourceExhausted.WithMessage(message)
default:
err = cerrdefs.ErrUnknown.WithMessage(message)
}
} else {
errs = append(errs, cerrdefs.ErrUnknown.WithMessage(err.Error()))
}
errs = append(errs, err)
}
switch len(errs) {
case 0:
err = cerrdefs.ErrUnknown.WithMessage(err.Error())
case 1:
err = errs[0]
default:
err = errors.Join(errs...)
}
return fmt.Errorf("error from registry: %w", err)
}