github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/api/cloudcontroller/ccv3/errors.go (about) 1 package ccv3 2 3 import ( 4 "encoding/json" 5 "net/http" 6 7 "code.cloudfoundry.org/cli/api/cloudcontroller" 8 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 9 ) 10 11 // errorWrapper is the wrapper that converts responses with 4xx and 5xx status 12 // codes to an error. 13 type errorWrapper struct { 14 connection cloudcontroller.Connection 15 } 16 17 func newErrorWrapper() *errorWrapper { 18 return new(errorWrapper) 19 } 20 21 // Wrap wraps a Cloud Controller connection in this error handling wrapper. 22 func (e *errorWrapper) Wrap(innerconnection cloudcontroller.Connection) cloudcontroller.Connection { 23 e.connection = innerconnection 24 return e 25 } 26 27 // Make creates a connection in the wrapped connection and handles errors 28 // that it returns. 29 func (e *errorWrapper) Make(request *cloudcontroller.Request, passedResponse *cloudcontroller.Response) error { 30 err := e.connection.Make(request, passedResponse) 31 32 if rawHTTPStatusErr, ok := err.(ccerror.RawHTTPStatusError); ok { 33 if rawHTTPStatusErr.StatusCode >= http.StatusInternalServerError { 34 return convert500(rawHTTPStatusErr) 35 } 36 return convert400(rawHTTPStatusErr) 37 } 38 return err 39 } 40 41 func convert400(rawHTTPStatusErr ccerror.RawHTTPStatusError) error { 42 firstErr, errorResponse, err := unmarshalFirstV3Error(rawHTTPStatusErr) 43 if err != nil { 44 return err 45 } 46 47 switch rawHTTPStatusErr.StatusCode { 48 case http.StatusUnauthorized: // 401 49 if firstErr.Title == "CF-InvalidAuthToken" { 50 return ccerror.InvalidAuthTokenError{Message: firstErr.Detail} 51 } 52 return ccerror.UnauthorizedError{Message: firstErr.Detail} 53 case http.StatusForbidden: // 403 54 return ccerror.ForbiddenError{Message: firstErr.Detail} 55 case http.StatusNotFound: // 404 56 return handleNotFound(firstErr) 57 case http.StatusUnprocessableEntity: // 422 58 return handleUnprocessableEntity(firstErr) 59 case http.StatusServiceUnavailable: // 503 60 if firstErr.Title == "CF-TaskWorkersUnavailable" { 61 return ccerror.TaskWorkersUnavailableError{Message: firstErr.Detail} 62 } 63 return ccerror.ServiceUnavailableError{Message: firstErr.Detail} 64 default: 65 return ccerror.V3UnexpectedResponseError{ 66 ResponseCode: rawHTTPStatusErr.StatusCode, 67 RequestIDs: rawHTTPStatusErr.RequestIDs, 68 V3ErrorResponse: errorResponse, 69 } 70 } 71 } 72 73 func convert500(rawHTTPStatusErr ccerror.RawHTTPStatusError) error { 74 switch rawHTTPStatusErr.StatusCode { 75 case http.StatusServiceUnavailable: // 503 76 firstErr, _, err := unmarshalFirstV3Error(rawHTTPStatusErr) 77 if err != nil { 78 return err 79 } 80 if firstErr.Title == "CF-TaskWorkersUnavailable" { 81 return ccerror.TaskWorkersUnavailableError{Message: firstErr.Detail} 82 } 83 return ccerror.ServiceUnavailableError{Message: firstErr.Detail} 84 default: 85 return ccerror.V3UnexpectedResponseError{ 86 ResponseCode: rawHTTPStatusErr.StatusCode, 87 RequestIDs: rawHTTPStatusErr.RequestIDs, 88 V3ErrorResponse: ccerror.V3ErrorResponse{ 89 Errors: []ccerror.V3Error{{ 90 Detail: string(rawHTTPStatusErr.RawResponse), 91 }}, 92 }, 93 } 94 } 95 } 96 97 func unmarshalFirstV3Error(rawHTTPStatusErr ccerror.RawHTTPStatusError) (ccerror.V3Error, ccerror.V3ErrorResponse, error) { 98 // Try to unmarshal the raw error into a CC error. If unmarshaling fails, 99 // return the raw error. 100 var errorResponse ccerror.V3ErrorResponse 101 err := json.Unmarshal(rawHTTPStatusErr.RawResponse, &errorResponse) 102 // error parsing json 103 if err != nil { 104 return ccerror.V3Error{}, errorResponse, ccerror.UnknownHTTPSourceError{ 105 StatusCode: rawHTTPStatusErr.StatusCode, 106 RawResponse: rawHTTPStatusErr.RawResponse, 107 } 108 } 109 110 errors := errorResponse.Errors 111 if len(errors) == 0 { 112 return ccerror.V3Error{}, errorResponse, ccerror.V3UnexpectedResponseError{ 113 ResponseCode: rawHTTPStatusErr.StatusCode, 114 V3ErrorResponse: errorResponse, 115 } 116 } 117 // There could be multiple errors in the future but for now we only convert 118 // the first error. 119 firstErr := errors[0] 120 return firstErr, errorResponse, nil 121 } 122 123 func handleNotFound(errorResponse ccerror.V3Error) error { 124 switch errorResponse.Detail { 125 case "App not found": 126 return ccerror.ApplicationNotFoundError{} 127 case "Droplet not found": 128 return ccerror.DropletNotFoundError{} 129 case "Instance not found": 130 return ccerror.InstanceNotFoundError{} 131 case "Process not found": 132 return ccerror.ProcessNotFoundError{} 133 default: 134 return ccerror.ResourceNotFoundError{Message: errorResponse.Detail} 135 } 136 } 137 138 func handleUnprocessableEntity(errorResponse ccerror.V3Error) error { 139 switch errorResponse.Detail { 140 case "name must be unique in space": 141 return ccerror.NameNotUniqueInSpaceError{} 142 case "Buildpack must be an existing admin buildpack or a valid git URI": 143 return ccerror.InvalidBuildpackError{} 144 default: 145 return ccerror.UnprocessableEntityError{Message: errorResponse.Detail} 146 } 147 }