github.com/ablease/cli@v6.37.1-0.20180613014814-3adbb7d7fb19+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  // Make creates a connection in the wrapped connection and handles errors
    22  // that it returns.
    23  func (e *errorWrapper) Make(request *cloudcontroller.Request, passedResponse *cloudcontroller.Response) error {
    24  	err := e.connection.Make(request, passedResponse)
    25  
    26  	if rawHTTPStatusErr, ok := err.(ccerror.RawHTTPStatusError); ok {
    27  		if rawHTTPStatusErr.StatusCode >= http.StatusInternalServerError {
    28  			return convert500(rawHTTPStatusErr)
    29  		}
    30  		return convert400(rawHTTPStatusErr)
    31  	}
    32  	return err
    33  }
    34  
    35  // Wrap wraps a Cloud Controller connection in this error handling wrapper.
    36  func (e *errorWrapper) Wrap(innerconnection cloudcontroller.Connection) cloudcontroller.Connection {
    37  	e.connection = innerconnection
    38  	return e
    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  	if len(errorResponse.Errors) > 1 {
    48  		return ccerror.MultiError{Errors: errorResponse.Errors, ResponseCode: rawHTTPStatusErr.StatusCode}
    49  	}
    50  
    51  	switch rawHTTPStatusErr.StatusCode {
    52  	case http.StatusUnauthorized: // 401
    53  		if firstErr.Title == "CF-InvalidAuthToken" {
    54  			return ccerror.InvalidAuthTokenError{Message: firstErr.Detail}
    55  		}
    56  		return ccerror.UnauthorizedError{Message: firstErr.Detail}
    57  	case http.StatusForbidden: // 403
    58  		return ccerror.ForbiddenError{Message: firstErr.Detail}
    59  	case http.StatusNotFound: // 404
    60  		return handleNotFound(firstErr)
    61  	case http.StatusUnprocessableEntity: // 422
    62  		return handleUnprocessableEntity(firstErr)
    63  	case http.StatusServiceUnavailable: // 503
    64  		if firstErr.Title == "CF-TaskWorkersUnavailable" {
    65  			return ccerror.TaskWorkersUnavailableError{Message: firstErr.Detail}
    66  		}
    67  		return ccerror.ServiceUnavailableError{Message: firstErr.Detail}
    68  	default:
    69  		return ccerror.V3UnexpectedResponseError{
    70  			ResponseCode:    rawHTTPStatusErr.StatusCode,
    71  			RequestIDs:      rawHTTPStatusErr.RequestIDs,
    72  			V3ErrorResponse: errorResponse,
    73  		}
    74  	}
    75  }
    76  
    77  func convert500(rawHTTPStatusErr ccerror.RawHTTPStatusError) error {
    78  	switch rawHTTPStatusErr.StatusCode {
    79  	case http.StatusServiceUnavailable: // 503
    80  		firstErr, _, err := unmarshalFirstV3Error(rawHTTPStatusErr)
    81  		if err != nil {
    82  			return err
    83  		}
    84  		if firstErr.Title == "CF-TaskWorkersUnavailable" {
    85  			return ccerror.TaskWorkersUnavailableError{Message: firstErr.Detail}
    86  		}
    87  		return ccerror.ServiceUnavailableError{Message: firstErr.Detail}
    88  	default:
    89  		return ccerror.V3UnexpectedResponseError{
    90  			ResponseCode: rawHTTPStatusErr.StatusCode,
    91  			RequestIDs:   rawHTTPStatusErr.RequestIDs,
    92  			V3ErrorResponse: ccerror.V3ErrorResponse{
    93  				Errors: []ccerror.V3Error{{
    94  					Detail: string(rawHTTPStatusErr.RawResponse),
    95  				}},
    96  			},
    97  		}
    98  	}
    99  }
   100  
   101  func handleNotFound(errorResponse ccerror.V3Error) error {
   102  	switch errorResponse.Detail {
   103  	case "App not found":
   104  		return ccerror.ApplicationNotFoundError{}
   105  	case "Droplet not found":
   106  		return ccerror.DropletNotFoundError{}
   107  	case "Instance not found":
   108  		return ccerror.InstanceNotFoundError{}
   109  	case "Process not found":
   110  		return ccerror.ProcessNotFoundError{}
   111  	default:
   112  		return ccerror.ResourceNotFoundError{Message: errorResponse.Detail}
   113  	}
   114  }
   115  
   116  func handleUnprocessableEntity(errorResponse ccerror.V3Error) error {
   117  	switch errorResponse.Detail {
   118  	case "name must be unique in space":
   119  		return ccerror.NameNotUniqueInSpaceError{}
   120  	case "Buildpack must be an existing admin buildpack or a valid git URI":
   121  		return ccerror.InvalidBuildpackError{}
   122  	default:
   123  		return ccerror.UnprocessableEntityError{Message: errorResponse.Detail}
   124  	}
   125  }
   126  
   127  func unmarshalFirstV3Error(rawHTTPStatusErr ccerror.RawHTTPStatusError) (ccerror.V3Error, ccerror.V3ErrorResponse, error) {
   128  	// Try to unmarshal the raw error into a CC error. If unmarshaling fails,
   129  	// return the raw error.
   130  	var errorResponse ccerror.V3ErrorResponse
   131  	err := json.Unmarshal(rawHTTPStatusErr.RawResponse, &errorResponse)
   132  	if err != nil {
   133  		return ccerror.V3Error{}, errorResponse, ccerror.UnknownHTTPSourceError{
   134  			StatusCode:  rawHTTPStatusErr.StatusCode,
   135  			RawResponse: rawHTTPStatusErr.RawResponse,
   136  		}
   137  	}
   138  
   139  	errors := errorResponse.Errors
   140  	if len(errors) == 0 {
   141  		return ccerror.V3Error{}, errorResponse, ccerror.V3UnexpectedResponseError{
   142  			ResponseCode:    rawHTTPStatusErr.StatusCode,
   143  			V3ErrorResponse: errorResponse,
   144  		}
   145  	}
   146  
   147  	return errors[0], errorResponse, nil
   148  }