github.com/containerd/Containerd@v1.4.13/remotes/docker/errcode.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package docker
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"strings"
    23  )
    24  
    25  // ErrorCoder is the base interface for ErrorCode and Error allowing
    26  // users of each to just call ErrorCode to get the real ID of each
    27  type ErrorCoder interface {
    28  	ErrorCode() ErrorCode
    29  }
    30  
    31  // ErrorCode represents the error type. The errors are serialized via strings
    32  // and the integer format may change and should *never* be exported.
    33  type ErrorCode int
    34  
    35  var _ error = ErrorCode(0)
    36  
    37  // ErrorCode just returns itself
    38  func (ec ErrorCode) ErrorCode() ErrorCode {
    39  	return ec
    40  }
    41  
    42  // Error returns the ID/Value
    43  func (ec ErrorCode) Error() string {
    44  	// NOTE(stevvooe): Cannot use message here since it may have unpopulated args.
    45  	return strings.ToLower(strings.Replace(ec.String(), "_", " ", -1))
    46  }
    47  
    48  // Descriptor returns the descriptor for the error code.
    49  func (ec ErrorCode) Descriptor() ErrorDescriptor {
    50  	d, ok := errorCodeToDescriptors[ec]
    51  
    52  	if !ok {
    53  		return ErrorCodeUnknown.Descriptor()
    54  	}
    55  
    56  	return d
    57  }
    58  
    59  // String returns the canonical identifier for this error code.
    60  func (ec ErrorCode) String() string {
    61  	return ec.Descriptor().Value
    62  }
    63  
    64  // Message returned the human-readable error message for this error code.
    65  func (ec ErrorCode) Message() string {
    66  	return ec.Descriptor().Message
    67  }
    68  
    69  // MarshalText encodes the receiver into UTF-8-encoded text and returns the
    70  // result.
    71  func (ec ErrorCode) MarshalText() (text []byte, err error) {
    72  	return []byte(ec.String()), nil
    73  }
    74  
    75  // UnmarshalText decodes the form generated by MarshalText.
    76  func (ec *ErrorCode) UnmarshalText(text []byte) error {
    77  	desc, ok := idToDescriptors[string(text)]
    78  
    79  	if !ok {
    80  		desc = ErrorCodeUnknown.Descriptor()
    81  	}
    82  
    83  	*ec = desc.Code
    84  
    85  	return nil
    86  }
    87  
    88  // WithMessage creates a new Error struct based on the passed-in info and
    89  // overrides the Message property.
    90  func (ec ErrorCode) WithMessage(message string) Error {
    91  	return Error{
    92  		Code:    ec,
    93  		Message: message,
    94  	}
    95  }
    96  
    97  // WithDetail creates a new Error struct based on the passed-in info and
    98  // set the Detail property appropriately
    99  func (ec ErrorCode) WithDetail(detail interface{}) Error {
   100  	return Error{
   101  		Code:    ec,
   102  		Message: ec.Message(),
   103  	}.WithDetail(detail)
   104  }
   105  
   106  // WithArgs creates a new Error struct and sets the Args slice
   107  func (ec ErrorCode) WithArgs(args ...interface{}) Error {
   108  	return Error{
   109  		Code:    ec,
   110  		Message: ec.Message(),
   111  	}.WithArgs(args...)
   112  }
   113  
   114  // Error provides a wrapper around ErrorCode with extra Details provided.
   115  type Error struct {
   116  	Code    ErrorCode   `json:"code"`
   117  	Message string      `json:"message"`
   118  	Detail  interface{} `json:"detail,omitempty"`
   119  
   120  	// TODO(duglin): See if we need an "args" property so we can do the
   121  	// variable substitution right before showing the message to the user
   122  }
   123  
   124  var _ error = Error{}
   125  
   126  // ErrorCode returns the ID/Value of this Error
   127  func (e Error) ErrorCode() ErrorCode {
   128  	return e.Code
   129  }
   130  
   131  // Error returns a human readable representation of the error.
   132  func (e Error) Error() string {
   133  	return fmt.Sprintf("%s: %s", e.Code.Error(), e.Message)
   134  }
   135  
   136  // WithDetail will return a new Error, based on the current one, but with
   137  // some Detail info added
   138  func (e Error) WithDetail(detail interface{}) Error {
   139  	return Error{
   140  		Code:    e.Code,
   141  		Message: e.Message,
   142  		Detail:  detail,
   143  	}
   144  }
   145  
   146  // WithArgs uses the passed-in list of interface{} as the substitution
   147  // variables in the Error's Message string, but returns a new Error
   148  func (e Error) WithArgs(args ...interface{}) Error {
   149  	return Error{
   150  		Code:    e.Code,
   151  		Message: fmt.Sprintf(e.Code.Message(), args...),
   152  		Detail:  e.Detail,
   153  	}
   154  }
   155  
   156  // ErrorDescriptor provides relevant information about a given error code.
   157  type ErrorDescriptor struct {
   158  	// Code is the error code that this descriptor describes.
   159  	Code ErrorCode
   160  
   161  	// Value provides a unique, string key, often captilized with
   162  	// underscores, to identify the error code. This value is used as the
   163  	// keyed value when serializing api errors.
   164  	Value string
   165  
   166  	// Message is a short, human readable decription of the error condition
   167  	// included in API responses.
   168  	Message string
   169  
   170  	// Description provides a complete account of the errors purpose, suitable
   171  	// for use in documentation.
   172  	Description string
   173  
   174  	// HTTPStatusCode provides the http status code that is associated with
   175  	// this error condition.
   176  	HTTPStatusCode int
   177  }
   178  
   179  // ParseErrorCode returns the value by the string error code.
   180  // `ErrorCodeUnknown` will be returned if the error is not known.
   181  func ParseErrorCode(value string) ErrorCode {
   182  	ed, ok := idToDescriptors[value]
   183  	if ok {
   184  		return ed.Code
   185  	}
   186  
   187  	return ErrorCodeUnknown
   188  }
   189  
   190  // Errors provides the envelope for multiple errors and a few sugar methods
   191  // for use within the application.
   192  type Errors []error
   193  
   194  var _ error = Errors{}
   195  
   196  func (errs Errors) Error() string {
   197  	switch len(errs) {
   198  	case 0:
   199  		return "<nil>"
   200  	case 1:
   201  		return errs[0].Error()
   202  	default:
   203  		msg := "errors:\n"
   204  		for _, err := range errs {
   205  			msg += err.Error() + "\n"
   206  		}
   207  		return msg
   208  	}
   209  }
   210  
   211  // Len returns the current number of errors.
   212  func (errs Errors) Len() int {
   213  	return len(errs)
   214  }
   215  
   216  // MarshalJSON converts slice of error, ErrorCode or Error into a
   217  // slice of Error - then serializes
   218  func (errs Errors) MarshalJSON() ([]byte, error) {
   219  	var tmpErrs struct {
   220  		Errors []Error `json:"errors,omitempty"`
   221  	}
   222  
   223  	for _, daErr := range errs {
   224  		var err Error
   225  
   226  		switch daErr := daErr.(type) {
   227  		case ErrorCode:
   228  			err = daErr.WithDetail(nil)
   229  		case Error:
   230  			err = daErr
   231  		default:
   232  			err = ErrorCodeUnknown.WithDetail(daErr)
   233  
   234  		}
   235  
   236  		// If the Error struct was setup and they forgot to set the
   237  		// Message field (meaning its "") then grab it from the ErrCode
   238  		msg := err.Message
   239  		if msg == "" {
   240  			msg = err.Code.Message()
   241  		}
   242  
   243  		tmpErrs.Errors = append(tmpErrs.Errors, Error{
   244  			Code:    err.Code,
   245  			Message: msg,
   246  			Detail:  err.Detail,
   247  		})
   248  	}
   249  
   250  	return json.Marshal(tmpErrs)
   251  }
   252  
   253  // UnmarshalJSON deserializes []Error and then converts it into slice of
   254  // Error or ErrorCode
   255  func (errs *Errors) UnmarshalJSON(data []byte) error {
   256  	var tmpErrs struct {
   257  		Errors []Error
   258  	}
   259  
   260  	if err := json.Unmarshal(data, &tmpErrs); err != nil {
   261  		return err
   262  	}
   263  
   264  	var newErrs Errors
   265  	for _, daErr := range tmpErrs.Errors {
   266  		// If Message is empty or exactly matches the Code's message string
   267  		// then just use the Code, no need for a full Error struct
   268  		if daErr.Detail == nil && (daErr.Message == "" || daErr.Message == daErr.Code.Message()) {
   269  			// Error's w/o details get converted to ErrorCode
   270  			newErrs = append(newErrs, daErr.Code)
   271  		} else {
   272  			// Error's w/ details are untouched
   273  			newErrs = append(newErrs, Error{
   274  				Code:    daErr.Code,
   275  				Message: daErr.Message,
   276  				Detail:  daErr.Detail,
   277  			})
   278  		}
   279  	}
   280  
   281  	*errs = newErrs
   282  	return nil
   283  }