github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/common/errors.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "fmt" 8 "net/http" 9 "strings" 10 11 "github.com/juju/errors" 12 "github.com/juju/txn" 13 "gopkg.in/juju/names.v2" 14 "gopkg.in/macaroon.v1" 15 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/core/leadership" 18 "github.com/juju/juju/core/lease" 19 "github.com/juju/juju/state" 20 ) 21 22 func NotSupportedError(tag names.Tag, operation string) error { 23 return errors.Errorf("entity %q does not support %s", tag, operation) 24 } 25 26 type noAddressSetError struct { 27 unitTag names.UnitTag 28 addressName string 29 } 30 31 func (e *noAddressSetError) Error() string { 32 return fmt.Sprintf("%q has no %s address set", e.unitTag, e.addressName) 33 } 34 35 func NoAddressSetError(unitTag names.UnitTag, addressName string) error { 36 return &noAddressSetError{unitTag: unitTag, addressName: addressName} 37 } 38 39 func isNoAddressSetError(err error) bool { 40 _, ok := err.(*noAddressSetError) 41 return ok 42 } 43 44 type unknownModelError struct { 45 uuid string 46 } 47 48 func (e *unknownModelError) Error() string { 49 return fmt.Sprintf("unknown model: %q", e.uuid) 50 } 51 52 func UnknownModelError(uuid string) error { 53 return &unknownModelError{uuid: uuid} 54 } 55 56 func isUnknownModelError(err error) bool { 57 _, ok := err.(*unknownModelError) 58 return ok 59 } 60 61 // DischargeRequiredError is the error returned when a macaroon requires discharging 62 // to complete authentication. 63 type DischargeRequiredError struct { 64 Cause error 65 Macaroon *macaroon.Macaroon 66 } 67 68 // Error implements the error interface. 69 func (e *DischargeRequiredError) Error() string { 70 return e.Cause.Error() 71 } 72 73 // IsDischargeRequiredError reports whether the cause 74 // of the error is a *DischargeRequiredError. 75 func IsDischargeRequiredError(err error) bool { 76 _, ok := errors.Cause(err).(*DischargeRequiredError) 77 return ok 78 } 79 80 // IsUpgradeInProgress returns true if this error is caused 81 // by an upgrade in progress. 82 func IsUpgradeInProgressError(err error) bool { 83 if state.IsUpgradeInProgressError(err) { 84 return true 85 } 86 return errors.Cause(err) == params.UpgradeInProgressError 87 } 88 89 var ( 90 ErrBadId = errors.New("id not found") 91 ErrBadCreds = errors.New("invalid entity name or password") 92 ErrNoCreds = errors.New("no credentials provided") 93 ErrLoginExpired = errors.New("login expired") 94 ErrPerm = errors.New("permission denied") 95 ErrNotLoggedIn = errors.New("not logged in") 96 ErrUnknownWatcher = errors.New("unknown watcher id") 97 ErrStoppedWatcher = errors.New("watcher has been stopped") 98 ErrBadRequest = errors.New("invalid request") 99 ErrTryAgain = errors.New("try again") 100 ErrActionNotAvailable = errors.New("action no longer available") 101 ) 102 103 // OperationBlockedError returns an error which signifies that 104 // an operation has been blocked; the message should describe 105 // what has been blocked. 106 func OperationBlockedError(msg string) error { 107 if msg == "" { 108 msg = "the operation has been blocked" 109 } 110 return ¶ms.Error{ 111 Message: msg, 112 Code: params.CodeOperationBlocked, 113 } 114 } 115 116 var singletonErrorCodes = map[error]string{ 117 state.ErrCannotEnterScopeYet: params.CodeCannotEnterScopeYet, 118 state.ErrCannotEnterScope: params.CodeCannotEnterScope, 119 state.ErrUnitHasSubordinates: params.CodeUnitHasSubordinates, 120 state.ErrDead: params.CodeDead, 121 txn.ErrExcessiveContention: params.CodeExcessiveContention, 122 leadership.ErrClaimDenied: params.CodeLeadershipClaimDenied, 123 lease.ErrClaimDenied: params.CodeLeaseClaimDenied, 124 ErrBadId: params.CodeNotFound, 125 ErrBadCreds: params.CodeUnauthorized, 126 ErrNoCreds: params.CodeNoCreds, 127 ErrLoginExpired: params.CodeLoginExpired, 128 ErrPerm: params.CodeUnauthorized, 129 ErrNotLoggedIn: params.CodeUnauthorized, 130 ErrUnknownWatcher: params.CodeNotFound, 131 ErrStoppedWatcher: params.CodeStopped, 132 ErrTryAgain: params.CodeTryAgain, 133 ErrActionNotAvailable: params.CodeActionNotAvailable, 134 } 135 136 func singletonCode(err error) (string, bool) { 137 // All error types may not be hashable; deal with 138 // that by catching the panic if we try to look up 139 // a non-hashable type. 140 defer func() { 141 recover() 142 }() 143 code, ok := singletonErrorCodes[err] 144 return code, ok 145 } 146 147 func singletonError(err error) (error, bool) { 148 errCode := params.ErrCode(err) 149 for singleton, code := range singletonErrorCodes { 150 if errCode == code && singleton.Error() == err.Error() { 151 return singleton, true 152 } 153 } 154 return nil, false 155 } 156 157 // ServerErrorAndStatus is like ServerError but also 158 // returns an HTTP status code appropriate for using 159 // in a response holding the given error. 160 func ServerErrorAndStatus(err error) (*params.Error, int) { 161 err1 := ServerError(err) 162 if err1 == nil { 163 return nil, http.StatusOK 164 } 165 status := http.StatusInternalServerError 166 switch err1.Code { 167 case params.CodeUnauthorized: 168 status = http.StatusUnauthorized 169 case params.CodeNotFound, 170 params.CodeUserNotFound, 171 params.CodeModelNotFound: 172 status = http.StatusNotFound 173 case params.CodeBadRequest: 174 status = http.StatusBadRequest 175 case params.CodeMethodNotAllowed: 176 status = http.StatusMethodNotAllowed 177 case params.CodeOperationBlocked: 178 // This should really be http.StatusForbidden but earlier versions 179 // of juju clients rely on the 400 status, so we leave it like that. 180 status = http.StatusBadRequest 181 case params.CodeForbidden: 182 status = http.StatusForbidden 183 case params.CodeDischargeRequired: 184 status = http.StatusUnauthorized 185 case params.CodeRetry: 186 status = http.StatusServiceUnavailable 187 } 188 return err1, status 189 } 190 191 // ServerError returns an error suitable for returning to an API 192 // client, with an error code suitable for various kinds of errors 193 // generated in packages outside the API. 194 func ServerError(err error) *params.Error { 195 if err == nil { 196 return nil 197 } 198 logger.Tracef("server RPC error %v", errors.Details(err)) 199 msg := err.Error() 200 // Skip past annotations when looking for the code. 201 err = errors.Cause(err) 202 code, ok := singletonCode(err) 203 var info *params.ErrorInfo 204 switch { 205 case ok: 206 case errors.IsUnauthorized(err): 207 code = params.CodeUnauthorized 208 case errors.IsNotFound(err): 209 code = params.CodeNotFound 210 case errors.IsUserNotFound(err): 211 code = params.CodeUserNotFound 212 case errors.IsAlreadyExists(err): 213 code = params.CodeAlreadyExists 214 case errors.IsNotAssigned(err): 215 code = params.CodeNotAssigned 216 case state.IsHasAssignedUnitsError(err): 217 code = params.CodeHasAssignedUnits 218 case state.IsHasHostedModelsError(err): 219 code = params.CodeHasHostedModels 220 case isNoAddressSetError(err): 221 code = params.CodeNoAddressSet 222 case errors.IsNotProvisioned(err): 223 code = params.CodeNotProvisioned 224 case IsUpgradeInProgressError(err): 225 code = params.CodeUpgradeInProgress 226 case state.IsHasAttachmentsError(err): 227 code = params.CodeMachineHasAttachedStorage 228 case isUnknownModelError(err): 229 code = params.CodeModelNotFound 230 case errors.IsNotSupported(err): 231 code = params.CodeNotSupported 232 case errors.IsBadRequest(err): 233 code = params.CodeBadRequest 234 case errors.IsMethodNotAllowed(err): 235 code = params.CodeMethodNotAllowed 236 default: 237 if err, ok := err.(*DischargeRequiredError); ok { 238 code = params.CodeDischargeRequired 239 info = ¶ms.ErrorInfo{ 240 Macaroon: err.Macaroon, 241 // One macaroon fits all. 242 MacaroonPath: "/", 243 } 244 break 245 } 246 code = params.ErrCode(err) 247 } 248 return ¶ms.Error{ 249 Message: msg, 250 Code: code, 251 Info: info, 252 } 253 } 254 255 func DestroyErr(desc string, ids, errs []string) error { 256 // TODO(waigani) refactor DestroyErr to take a map of ids to errors. 257 if len(errs) == 0 { 258 return nil 259 } 260 msg := "some %s were not destroyed" 261 if len(errs) == len(ids) { 262 msg = "no %s were destroyed" 263 } 264 msg = fmt.Sprintf(msg, desc) 265 return errors.Errorf("%s: %s", msg, strings.Join(errs, "; ")) 266 } 267 268 // RestoreError makes a best effort at converting the given error 269 // back into an error originally converted by ServerError(). 270 func RestoreError(err error) error { 271 err = errors.Cause(err) 272 273 if apiErr, ok := err.(*params.Error); !ok { 274 return err 275 } else if apiErr == nil { 276 return nil 277 } 278 if params.ErrCode(err) == "" { 279 return err 280 } 281 msg := err.Error() 282 283 if singleton, ok := singletonError(err); ok { 284 return singleton 285 } 286 287 // TODO(ericsnow) Support the other error types handled by ServerError(). 288 switch { 289 case params.IsCodeUnauthorized(err): 290 return errors.NewUnauthorized(nil, msg) 291 case params.IsCodeNotFound(err): 292 // TODO(ericsnow) UnknownModelError should be handled here too. 293 // ...by parsing msg? 294 return errors.NewNotFound(nil, msg) 295 case params.IsCodeUserNotFound(err): 296 return errors.NewUserNotFound(nil, msg) 297 case params.IsCodeAlreadyExists(err): 298 return errors.NewAlreadyExists(nil, msg) 299 case params.IsCodeNotAssigned(err): 300 return errors.NewNotAssigned(nil, msg) 301 case params.IsCodeHasAssignedUnits(err): 302 // TODO(ericsnow) Handle state.HasAssignedUnitsError here. 303 // ...by parsing msg? 304 return err 305 case params.IsCodeHasHostedModels(err): 306 return err 307 case params.IsCodeNoAddressSet(err): 308 // TODO(ericsnow) Handle isNoAddressSetError here. 309 // ...by parsing msg? 310 return err 311 case params.IsCodeNotProvisioned(err): 312 return errors.NewNotProvisioned(nil, msg) 313 case params.IsCodeUpgradeInProgress(err): 314 // TODO(ericsnow) Handle state.UpgradeInProgressError here. 315 // ...by parsing msg? 316 return err 317 case params.IsCodeMachineHasAttachedStorage(err): 318 // TODO(ericsnow) Handle state.HasAttachmentsError here. 319 // ...by parsing msg? 320 return err 321 case params.IsCodeNotSupported(err): 322 return errors.NewNotSupported(nil, msg) 323 case params.IsBadRequest(err): 324 return errors.NewBadRequest(nil, msg) 325 case params.IsMethodNotAllowed(err): 326 return errors.NewMethodNotAllowed(nil, msg) 327 case params.ErrCode(err) == params.CodeDischargeRequired: 328 // TODO(ericsnow) Handle DischargeRequiredError here. 329 return err 330 default: 331 return err 332 } 333 }