github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/common/errors.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "net/http" 8 9 "github.com/juju/collections/set" 10 "github.com/juju/errors" 11 12 "github.com/juju/juju/environs/context" 13 ) 14 15 // ZoneIndependentError wraps the given error such that it 16 // satisfies environs.IsAvailabilityZoneIndependent. 17 func ZoneIndependentError(err error) error { 18 if err == nil { 19 return nil 20 } 21 wrapped := errors.Wrap(err, zoneIndependentError{err}) 22 wrapped.(*errors.Err).SetLocation(1) 23 return wrapped 24 } 25 26 type zoneIndependentError struct { 27 error 28 } 29 30 // AvailabilityZoneIndependent is part of the 31 // environs.AvailabilityZoneError interface. 32 func (zoneIndependentError) AvailabilityZoneIndependent() bool { 33 return true 34 } 35 36 // credentialNotValid represents an error when a provider credential is not valid. 37 // Realistically, this is not a transient error. Without a valid credential we 38 // cannot do much on the provider. This is fatal. 39 type credentialNotValid struct { 40 error 41 } 42 43 // CredentialNotValid returns an error which wraps err and satisfies 44 // IsCredentialNotValid(). 45 func CredentialNotValid(err error) error { 46 if err == nil { 47 return nil 48 } 49 wrapped := errors.Wrap(err, &credentialNotValid{err}) 50 wrapped.(*errors.Err).SetLocation(1) 51 return wrapped 52 } 53 54 // NewCredentialNotValid returns an error with given message and satisfies 55 // IsCredentialNotValid(). 56 func NewCredentialNotValid(message string) error { 57 err := errors.New("credential not valid: " + message) 58 wrapped := errors.Wrap(err, &credentialNotValid{err}) 59 wrapped.(*errors.Err).SetLocation(1) 60 return wrapped 61 } 62 63 // CredentialNotValidf returns a wrapped error with given message and satisfies 64 // IsCredentialNotValid(). 65 func CredentialNotValidf(err error, message string) error { 66 wrapped := errors.Wrapf(err, &credentialNotValid{err}, message) 67 wrapped.(*errors.Err).SetLocation(1) 68 return wrapped 69 } 70 71 // IsCredentialNotValid reports whether err was created with CredentialNotValid(). 72 func IsCredentialNotValid(err error) bool { 73 err = errors.Cause(err) 74 _, ok := err.(*credentialNotValid) 75 return ok 76 } 77 78 // AuthorisationFailureStatusCodes contains http status code that signify authorisation difficulties. 79 var AuthorisationFailureStatusCodes = set.NewInts( 80 http.StatusUnauthorized, 81 http.StatusPaymentRequired, 82 http.StatusForbidden, 83 http.StatusProxyAuthRequired, 84 ) 85 86 // MaybeHandleCredentialError determines if a given error relates to an invalid credential. 87 // If it is, the credential is invalidated and the return bool is true. 88 func MaybeHandleCredentialError(isAuthError func(error) bool, err error, ctx context.ProviderCallContext) bool { 89 denied := isAuthError(errors.Cause(err)) 90 if ctx != nil && denied { 91 invalidateErr := ctx.InvalidateCredential("cloud denied access") 92 if invalidateErr != nil { 93 logger.Warningf("could not invalidate stored cloud credential on the controller: %v", invalidateErr) 94 } 95 } 96 return denied 97 } 98 99 // HandleCredentialError determines if a given error relates to an invalid credential. 100 func HandleCredentialError(isAuthError func(error) bool, err error, ctx context.ProviderCallContext) { 101 MaybeHandleCredentialError(isAuthError, err, ctx) 102 }