github.com/orange-cloudfoundry/cli@v7.1.0+incompatible/api/cloudcontroller/ccv2/errors.go (about)

     1  package ccv2
     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 converts RawHTTPStatusError, which represents responses with 4xx and
    22  // 5xx status codes, to specific errors.
    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 passedResponse.HTTPResponse.StatusCode >= http.StatusInternalServerError {
    28  			return convert500(rawHTTPStatusErr)
    29  		}
    30  
    31  		return convert400(rawHTTPStatusErr)
    32  	}
    33  	return err
    34  }
    35  
    36  // Wrap wraps a Cloud Controller connection in this error handling wrapper.
    37  func (e *errorWrapper) Wrap(innerconnection cloudcontroller.Connection) cloudcontroller.Connection {
    38  	e.connection = innerconnection
    39  	return e
    40  }
    41  
    42  func convert400(rawHTTPStatusErr ccerror.RawHTTPStatusError) error {
    43  	// Try to unmarshal the raw error into a CC error. If unmarshaling fails,
    44  	// either we're not talking to a CC, or the CC returned invalid json.
    45  	errorResponse, err := unmarshalRawHTTPErr(rawHTTPStatusErr)
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	switch rawHTTPStatusErr.StatusCode {
    51  	case http.StatusBadRequest: // 400
    52  		return handleBadRequest(errorResponse)
    53  	case http.StatusUnauthorized: // 401
    54  		return handleUnauthorized(errorResponse)
    55  	case http.StatusForbidden: // 403
    56  		return ccerror.ForbiddenError{Message: errorResponse.Description}
    57  	case http.StatusNotFound: // 404
    58  		return ccerror.ResourceNotFoundError{Message: errorResponse.Description}
    59  	case http.StatusUnprocessableEntity: // 422
    60  		return handleUnprocessableEntity(errorResponse)
    61  	default:
    62  		return ccerror.V2UnexpectedResponseError{
    63  			RequestIDs:      rawHTTPStatusErr.RequestIDs,
    64  			ResponseCode:    rawHTTPStatusErr.StatusCode,
    65  			V2ErrorResponse: errorResponse,
    66  		}
    67  	}
    68  }
    69  
    70  func convert500(rawHTTPStatusErr ccerror.RawHTTPStatusError) error {
    71  	errorResponse, err := unmarshalRawHTTPErr(rawHTTPStatusErr)
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	switch rawHTTPStatusErr.StatusCode {
    77  	case http.StatusBadGateway: // 502
    78  		return handleBadGateway(errorResponse, rawHTTPStatusErr)
    79  	default:
    80  		return v2UnexpectedResponseError(rawHTTPStatusErr)
    81  	}
    82  }
    83  
    84  func handleBadGateway(errorResponse ccerror.V2ErrorResponse, rawHTTPStatusErr ccerror.RawHTTPStatusError) error {
    85  	switch errorResponse.ErrorCode {
    86  	case "CF-ServiceBrokerCatalogInvalid":
    87  		return ccerror.ServiceBrokerCatalogInvalidError{Message: errorResponse.Description}
    88  	case "CF-ServiceBrokerRequestRejected":
    89  		return ccerror.ServiceBrokerRequestRejectedError{Message: errorResponse.Description}
    90  	case "CF-ServiceBrokerBadResponse":
    91  		return ccerror.ServiceBrokerBadResponseError{Message: errorResponse.Description}
    92  	default:
    93  		return v2UnexpectedResponseError(rawHTTPStatusErr)
    94  	}
    95  }
    96  
    97  func handleBadRequest(errorResponse ccerror.V2ErrorResponse) error {
    98  	switch errorResponse.ErrorCode {
    99  	case "CF-AppStoppedStatsError":
   100  		return ccerror.ApplicationStoppedStatsError{Message: errorResponse.Description}
   101  	case "CF-BuildpackInvalid":
   102  		return ccerror.BuildpackInvalidError{Message: errorResponse.Description}
   103  	case "CF-BuildpackNameTaken":
   104  		return ccerror.BuildpackNameTakenError{Message: errorResponse.Description}
   105  	case "CF-InstancesError":
   106  		return ccerror.InstancesError{Message: errorResponse.Description}
   107  	case "CF-InvalidRelation":
   108  		return ccerror.InvalidRelationError{Message: errorResponse.Description}
   109  	case "CF-NotStaged":
   110  		return ccerror.NotStagedError{Message: errorResponse.Description}
   111  	case "CF-ServiceBindingAppServiceTaken":
   112  		return ccerror.ServiceBindingTakenError{Message: errorResponse.Description}
   113  	case "CF-ServiceKeyNameTaken":
   114  		return ccerror.ServiceKeyTakenError{Message: errorResponse.Description}
   115  	case "CF-OrganizationNameTaken":
   116  		return ccerror.OrganizationNameTakenError{}
   117  	case "CF-SpaceNameTaken":
   118  		return ccerror.SpaceNameTakenError{Message: errorResponse.Description}
   119  	case "CF-ServiceInstanceNameTaken":
   120  		return ccerror.ServiceInstanceNameTakenError{Message: errorResponse.Description}
   121  	case "CF-ServicePlanVisibilityAlreadyExists":
   122  		return ccerror.ServicePlanVisibilityExistsError{Message: errorResponse.Description}
   123  	default:
   124  		return ccerror.BadRequestError{Message: errorResponse.Description}
   125  	}
   126  }
   127  
   128  func handleUnauthorized(errorResponse ccerror.V2ErrorResponse) error {
   129  	if errorResponse.ErrorCode == "CF-InvalidAuthToken" {
   130  		return ccerror.InvalidAuthTokenError{Message: errorResponse.Description}
   131  	}
   132  
   133  	return ccerror.UnauthorizedError{Message: errorResponse.Description}
   134  }
   135  
   136  func handleUnprocessableEntity(errorResponse ccerror.V2ErrorResponse) error {
   137  	if errorResponse.ErrorCode == "CF-BuildpackNameStackTaken" {
   138  		return ccerror.BuildpackAlreadyExistsForStackError{Message: errorResponse.Description}
   139  	}
   140  	return ccerror.UnprocessableEntityError{Message: errorResponse.Description}
   141  }
   142  
   143  func unmarshalRawHTTPErr(rawHTTPStatusErr ccerror.RawHTTPStatusError) (ccerror.V2ErrorResponse, error) {
   144  	var errorResponse ccerror.V2ErrorResponse
   145  	err := json.Unmarshal(rawHTTPStatusErr.RawResponse, &errorResponse)
   146  	if err != nil {
   147  		// ccv2/info.go converts this error to an APINotFoundError.
   148  		return ccerror.V2ErrorResponse{}, ccerror.UnknownHTTPSourceError{StatusCode: rawHTTPStatusErr.StatusCode, RawResponse: rawHTTPStatusErr.RawResponse}
   149  	}
   150  	return errorResponse, nil
   151  }
   152  
   153  func v2UnexpectedResponseError(rawHTTPStatusErr ccerror.RawHTTPStatusError) ccerror.V2UnexpectedResponseError {
   154  	return ccerror.V2UnexpectedResponseError{
   155  		ResponseCode: rawHTTPStatusErr.StatusCode,
   156  		RequestIDs:   rawHTTPStatusErr.RequestIDs,
   157  		V2ErrorResponse: ccerror.V2ErrorResponse{
   158  			Description: string(rawHTTPStatusErr.RawResponse),
   159  		},
   160  	}
   161  }