github.com/lalkh/containerd@v1.4.3/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 }