mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Add more specific error handling and handle generic errors more effectively
The client library should type more generic errors from the server.
This commit is contained in:
		@@ -105,6 +105,21 @@ func NewAlreadyExists(kind, name string) error {
 | 
				
			|||||||
	}}
 | 
						}}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewUnauthorized returns an error indicating the client is not authorized to perform the requested
 | 
				
			||||||
 | 
					// action.
 | 
				
			||||||
 | 
					func NewUnauthorized(reason string) error {
 | 
				
			||||||
 | 
						message := reason
 | 
				
			||||||
 | 
						if len(message) == 0 {
 | 
				
			||||||
 | 
							message = "not authorized"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &StatusError{api.Status{
 | 
				
			||||||
 | 
							Status:  api.StatusFailure,
 | 
				
			||||||
 | 
							Code:    http.StatusUnauthorized,
 | 
				
			||||||
 | 
							Reason:  api.StatusReasonUnauthorized,
 | 
				
			||||||
 | 
							Message: message,
 | 
				
			||||||
 | 
						}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewForbidden returns an error indicating the requested action was forbidden
 | 
					// NewForbidden returns an error indicating the requested action was forbidden
 | 
				
			||||||
func NewForbidden(kind, name string, err error) error {
 | 
					func NewForbidden(kind, name string, err error) error {
 | 
				
			||||||
	return &StatusError{api.Status{
 | 
						return &StatusError{api.Status{
 | 
				
			||||||
@@ -183,7 +198,7 @@ func NewMethodNotSupported(kind, action string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NewServerTimeout returns an error indicating the requested action could not be completed due to a
 | 
					// NewServerTimeout returns an error indicating the requested action could not be completed due to a
 | 
				
			||||||
// transient error, and the client should try again.
 | 
					// transient error, and the client should try again.
 | 
				
			||||||
func NewServerTimeout(kind, operation string) error {
 | 
					func NewServerTimeout(kind, operation string, retryAfter int) error {
 | 
				
			||||||
	return &StatusError{api.Status{
 | 
						return &StatusError{api.Status{
 | 
				
			||||||
		Status: api.StatusFailure,
 | 
							Status: api.StatusFailure,
 | 
				
			||||||
		Code:   http.StatusInternalServerError,
 | 
							Code:   http.StatusInternalServerError,
 | 
				
			||||||
@@ -191,6 +206,7 @@ func NewServerTimeout(kind, operation string) error {
 | 
				
			|||||||
		Details: &api.StatusDetails{
 | 
							Details: &api.StatusDetails{
 | 
				
			||||||
			Kind:       kind,
 | 
								Kind:       kind,
 | 
				
			||||||
			ID:         operation,
 | 
								ID:         operation,
 | 
				
			||||||
 | 
								RetryAfter: retryAfter,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Message: fmt.Sprintf("The %s operation against %s could not be completed at this time, please try again.", operation, kind),
 | 
							Message: fmt.Sprintf("The %s operation against %s could not be completed at this time, please try again.", operation, kind),
 | 
				
			||||||
	}}
 | 
						}}
 | 
				
			||||||
@@ -211,12 +227,15 @@ func NewInternalError(err error) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NewTimeoutError returns an error indicating that a timeout occurred before the request
 | 
					// NewTimeoutError returns an error indicating that a timeout occurred before the request
 | 
				
			||||||
// could be completed.  Clients may retry, but the operation may still complete.
 | 
					// could be completed.  Clients may retry, but the operation may still complete.
 | 
				
			||||||
func NewTimeoutError(message string) error {
 | 
					func NewTimeoutError(message string, retryAfter int) error {
 | 
				
			||||||
	return &StatusError{api.Status{
 | 
						return &StatusError{api.Status{
 | 
				
			||||||
		Status:  api.StatusFailure,
 | 
							Status:  api.StatusFailure,
 | 
				
			||||||
		Code:    StatusServerTimeout,
 | 
							Code:    StatusServerTimeout,
 | 
				
			||||||
		Reason:  api.StatusReasonTimeout,
 | 
							Reason:  api.StatusReasonTimeout,
 | 
				
			||||||
		Message: fmt.Sprintf("Timeout: %s", message),
 | 
							Message: fmt.Sprintf("Timeout: %s", message),
 | 
				
			||||||
 | 
							Details: &api.StatusDetails{
 | 
				
			||||||
 | 
								RetryAfter: retryAfter,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}}
 | 
						}}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -251,6 +270,12 @@ func IsBadRequest(err error) bool {
 | 
				
			|||||||
	return reasonForError(err) == api.StatusReasonBadRequest
 | 
						return reasonForError(err) == api.StatusReasonBadRequest
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsUnauthorized determines if err is an error which indicates that the request is unauthorized and
 | 
				
			||||||
 | 
					// requires authentication by the user.
 | 
				
			||||||
 | 
					func IsUnauthorized(err error) bool {
 | 
				
			||||||
 | 
						return reasonForError(err) == api.StatusReasonUnauthorized
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsForbidden determines if err is an error which indicates that the request is forbidden and cannot
 | 
					// IsForbidden determines if err is an error which indicates that the request is forbidden and cannot
 | 
				
			||||||
// be completed as requested.
 | 
					// be completed as requested.
 | 
				
			||||||
func IsForbidden(err error) bool {
 | 
					func IsForbidden(err error) bool {
 | 
				
			||||||
@@ -275,6 +300,21 @@ func IsUnexpectedObjectError(err error) bool {
 | 
				
			|||||||
	return ok
 | 
						return ok
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SuggestsClientDelay returns true if this error suggests a client delay as well as the
 | 
				
			||||||
 | 
					// suggested seconds to wait, or false if the error does not imply a wait.
 | 
				
			||||||
 | 
					func SuggestsClientDelay(err error) (int, bool) {
 | 
				
			||||||
 | 
						switch t := err.(type) {
 | 
				
			||||||
 | 
						case *StatusError:
 | 
				
			||||||
 | 
							if t.Status().Details != nil {
 | 
				
			||||||
 | 
								switch t.Status().Reason {
 | 
				
			||||||
 | 
								case api.StatusReasonServerTimeout, api.StatusReasonTimeout:
 | 
				
			||||||
 | 
									return t.Status().Details.RetryAfter, true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0, false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func reasonForError(err error) api.StatusReason {
 | 
					func reasonForError(err error) api.StatusReason {
 | 
				
			||||||
	switch t := err.(type) {
 | 
						switch t := err.(type) {
 | 
				
			||||||
	case *StatusError:
 | 
						case *StatusError:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,9 +69,18 @@ func TestErrorNew(t *testing.T) {
 | 
				
			|||||||
	if !IsForbidden(NewForbidden("test", "2", errors.New("reason"))) {
 | 
						if !IsForbidden(NewForbidden("test", "2", errors.New("reason"))) {
 | 
				
			||||||
		t.Errorf("expected to be %s", api.StatusReasonForbidden)
 | 
							t.Errorf("expected to be %s", api.StatusReasonForbidden)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !IsServerTimeout(NewServerTimeout("test", "reason")) {
 | 
						if !IsUnauthorized(NewUnauthorized("reason")) {
 | 
				
			||||||
 | 
							t.Errorf("expected to be %s", api.StatusReasonUnauthorized)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !IsServerTimeout(NewServerTimeout("test", "reason", 0)) {
 | 
				
			||||||
		t.Errorf("expected to be %s", api.StatusReasonServerTimeout)
 | 
							t.Errorf("expected to be %s", api.StatusReasonServerTimeout)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if time, ok := SuggestsClientDelay(NewServerTimeout("test", "doing something", 10)); time != 10 || !ok {
 | 
				
			||||||
 | 
							t.Errorf("expected to be %s", api.StatusReasonServerTimeout)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if time, ok := SuggestsClientDelay(NewTimeoutError("test reason", 10)); time != 10 || !ok {
 | 
				
			||||||
 | 
							t.Errorf("expected to be %s", api.StatusReasonTimeout)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if !IsMethodNotSupported(NewMethodNotSupported("foo", "delete")) {
 | 
						if !IsMethodNotSupported(NewMethodNotSupported("foo", "delete")) {
 | 
				
			||||||
		t.Errorf("expected to be %s", api.StatusReasonMethodNotAllowed)
 | 
							t.Errorf("expected to be %s", api.StatusReasonMethodNotAllowed)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,7 +84,7 @@ func CheckGeneratedNameError(strategy RESTCreateStrategy, err error, obj runtime
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return errors.NewServerTimeout(kind, "POST")
 | 
						return errors.NewServerTimeout(kind, "POST", 0)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// objectMetaAndKind retrieves kind and ObjectMeta from a runtime object, or returns an error.
 | 
					// objectMetaAndKind retrieves kind and ObjectMeta from a runtime object, or returns an error.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1200,6 +1200,8 @@ type StatusDetails struct {
 | 
				
			|||||||
	// The Causes array includes more details associated with the StatusReason
 | 
						// The Causes array includes more details associated with the StatusReason
 | 
				
			||||||
	// failure. Not all StatusReasons may provide detailed causes.
 | 
						// failure. Not all StatusReasons may provide detailed causes.
 | 
				
			||||||
	Causes []StatusCause `json:"causes,omitempty"`
 | 
						Causes []StatusCause `json:"causes,omitempty"`
 | 
				
			||||||
 | 
						// If specified, the time in seconds before the operation should be retried.
 | 
				
			||||||
 | 
						RetryAfter int `json:"retryAfter,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Values of Status.Status
 | 
					// Values of Status.Status
 | 
				
			||||||
@@ -1220,6 +1222,13 @@ const (
 | 
				
			|||||||
	// Status code 500.
 | 
						// Status code 500.
 | 
				
			||||||
	StatusReasonUnknown StatusReason = ""
 | 
						StatusReasonUnknown StatusReason = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// StatusReasonUnauthorized means the server can be reached and understood the request, but requires
 | 
				
			||||||
 | 
						// the user to present appropriate authorization credentials (identified by the WWW-Authenticate header)
 | 
				
			||||||
 | 
						// in order for the action to be completed. If the user has specified credentials on the request, the
 | 
				
			||||||
 | 
						// server considers them insufficient.
 | 
				
			||||||
 | 
						// Status code 401
 | 
				
			||||||
 | 
						StatusReasonUnauthorized StatusReason = "Unauthorized"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// StatusReasonForbidden means the server can be reached and understood the request, but refuses
 | 
						// StatusReasonForbidden means the server can be reached and understood the request, but refuses
 | 
				
			||||||
	// to take any further action.  It is the result of the server being configured to deny access for some reason
 | 
						// to take any further action.  It is the result of the server being configured to deny access for some reason
 | 
				
			||||||
	// to the requested resource by the client.
 | 
						// to the requested resource by the client.
 | 
				
			||||||
@@ -1276,12 +1285,17 @@ const (
 | 
				
			|||||||
	// Details (optional):
 | 
						// Details (optional):
 | 
				
			||||||
	//   "kind" string - the kind attribute of the resource being acted on.
 | 
						//   "kind" string - the kind attribute of the resource being acted on.
 | 
				
			||||||
	//   "id"   string - the operation that is being attempted.
 | 
						//   "id"   string - the operation that is being attempted.
 | 
				
			||||||
 | 
						//   "retryAfter" int - the number of seconds before the operation should be retried
 | 
				
			||||||
	// Status code 500
 | 
						// Status code 500
 | 
				
			||||||
	StatusReasonServerTimeout StatusReason = "ServerTimeout"
 | 
						StatusReasonServerTimeout StatusReason = "ServerTimeout"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// StatusReasonTimeout means that the request could not be completed within the given time.
 | 
						// StatusReasonTimeout means that the request could not be completed within the given time.
 | 
				
			||||||
	// Clients can get this response only when they specified a timeout param in the request.
 | 
						// Clients can get this response only when they specified a timeout param in the request,
 | 
				
			||||||
	// The request might succeed with an increased value of timeout param.
 | 
						// or if the server cannot complete the operation within a reasonable amount of time.
 | 
				
			||||||
 | 
						// The request might succeed with an increased value of timeout param. The client *should*
 | 
				
			||||||
 | 
						// wait at least the number of seconds specified by the retryAfter field.
 | 
				
			||||||
 | 
						// Details (optional):
 | 
				
			||||||
 | 
						//   "retryAfter" int - the number of seconds before the operation should be retried
 | 
				
			||||||
	// Status code 504
 | 
						// Status code 504
 | 
				
			||||||
	StatusReasonTimeout StatusReason = "Timeout"
 | 
						StatusReasonTimeout StatusReason = "Timeout"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1014,6 +1014,8 @@ type StatusDetails struct {
 | 
				
			|||||||
	// The Causes array includes more details associated with the StatusReason
 | 
						// The Causes array includes more details associated with the StatusReason
 | 
				
			||||||
	// failure. Not all StatusReasons may provide detailed causes.
 | 
						// failure. Not all StatusReasons may provide detailed causes.
 | 
				
			||||||
	Causes []StatusCause `json:"causes,omitempty" description:"the Causes array includes more details associated with the StatusReason failure; not all StatusReasons may provide detailed causes"`
 | 
						Causes []StatusCause `json:"causes,omitempty" description:"the Causes array includes more details associated with the StatusReason failure; not all StatusReasons may provide detailed causes"`
 | 
				
			||||||
 | 
						// If specified, the time in seconds before the operation should be retried.
 | 
				
			||||||
 | 
						RetryAfter int `json:"retryAfter,omitempty" description:"the number of seconds before the client should attempt to retry this operation"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Values of Status.Status
 | 
					// Values of Status.Status
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1028,6 +1028,8 @@ type StatusDetails struct {
 | 
				
			|||||||
	// The Causes array includes more details associated with the StatusReason
 | 
						// The Causes array includes more details associated with the StatusReason
 | 
				
			||||||
	// failure. Not all StatusReasons may provide detailed causes.
 | 
						// failure. Not all StatusReasons may provide detailed causes.
 | 
				
			||||||
	Causes []StatusCause `json:"causes,omitempty" description:"the Causes array includes more details associated with the StatusReason failure; not all StatusReasons may provide detailed causes"`
 | 
						Causes []StatusCause `json:"causes,omitempty" description:"the Causes array includes more details associated with the StatusReason failure; not all StatusReasons may provide detailed causes"`
 | 
				
			||||||
 | 
						// If specified, the time in seconds before the operation should be retried.
 | 
				
			||||||
 | 
						RetryAfter int `json:"retryAfter,omitempty" description:"the number of seconds before the client should attempt to retry this operation"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Values of Status.Status
 | 
					// Values of Status.Status
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1187,6 +1187,8 @@ type StatusDetails struct {
 | 
				
			|||||||
	// The Causes array includes more details associated with the StatusReason
 | 
						// The Causes array includes more details associated with the StatusReason
 | 
				
			||||||
	// failure. Not all StatusReasons may provide detailed causes.
 | 
						// failure. Not all StatusReasons may provide detailed causes.
 | 
				
			||||||
	Causes []StatusCause `json:"causes,omitempty" description:"the Causes array includes more details associated with the StatusReason failure; not all StatusReasons may provide detailed causes"`
 | 
						Causes []StatusCause `json:"causes,omitempty" description:"the Causes array includes more details associated with the StatusReason failure; not all StatusReasons may provide detailed causes"`
 | 
				
			||||||
 | 
						// If specified, the time in seconds before the operation should be retried.
 | 
				
			||||||
 | 
						RetryAfter int `json:"retryAfter,omitempty" description:"the number of seconds before the client should attempt to retry this operation"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Values of Status.Status
 | 
					// Values of Status.Status
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -436,7 +436,7 @@ func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object,
 | 
				
			|||||||
	case err = <-errCh:
 | 
						case err = <-errCh:
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	case <-time.After(timeout):
 | 
						case <-time.After(timeout):
 | 
				
			||||||
		return nil, errors.NewTimeoutError("request did not complete within allowed duration")
 | 
							return nil, errors.NewTimeoutError("request did not complete within allowed duration", 0)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"mime"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
@@ -621,12 +622,15 @@ func (r *Request) transformResponse(body []byte, resp *http.Response, req *http.
 | 
				
			|||||||
		// no-op, we've been upgraded
 | 
							// no-op, we've been upgraded
 | 
				
			||||||
	case resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusPartialContent:
 | 
						case resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusPartialContent:
 | 
				
			||||||
		if !isStatusResponse {
 | 
							if !isStatusResponse {
 | 
				
			||||||
			var err error
 | 
								var err error = &UnexpectedStatusError{
 | 
				
			||||||
			err = &UnexpectedStatusError{
 | 
					 | 
				
			||||||
				Request:  req,
 | 
									Request:  req,
 | 
				
			||||||
				Response: resp,
 | 
									Response: resp,
 | 
				
			||||||
				Body:     string(body),
 | 
									Body:     string(body),
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								message := "unknown"
 | 
				
			||||||
 | 
								if isTextResponse(resp) {
 | 
				
			||||||
 | 
									message = strings.TrimSpace(string(body))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			// TODO: handle other error classes we know about
 | 
								// TODO: handle other error classes we know about
 | 
				
			||||||
			switch resp.StatusCode {
 | 
								switch resp.StatusCode {
 | 
				
			||||||
			case http.StatusConflict:
 | 
								case http.StatusConflict:
 | 
				
			||||||
@@ -638,7 +642,21 @@ func (r *Request) transformResponse(body []byte, resp *http.Response, req *http.
 | 
				
			|||||||
			case http.StatusNotFound:
 | 
								case http.StatusNotFound:
 | 
				
			||||||
				err = errors.NewNotFound(r.resource, r.resourceName)
 | 
									err = errors.NewNotFound(r.resource, r.resourceName)
 | 
				
			||||||
			case http.StatusBadRequest:
 | 
								case http.StatusBadRequest:
 | 
				
			||||||
				err = errors.NewBadRequest(err.Error())
 | 
									err = errors.NewBadRequest(message)
 | 
				
			||||||
 | 
								case http.StatusUnauthorized:
 | 
				
			||||||
 | 
									err = errors.NewUnauthorized(message)
 | 
				
			||||||
 | 
								case http.StatusForbidden:
 | 
				
			||||||
 | 
									err = errors.NewForbidden(r.resource, r.resourceName, err)
 | 
				
			||||||
 | 
								case errors.StatusUnprocessableEntity:
 | 
				
			||||||
 | 
									err = errors.NewInvalid(r.resource, r.resourceName, nil)
 | 
				
			||||||
 | 
								case errors.StatusServerTimeout:
 | 
				
			||||||
 | 
									retryAfter, _ := retryAfter(resp)
 | 
				
			||||||
 | 
									err = errors.NewServerTimeout(r.resource, r.verb, retryAfter)
 | 
				
			||||||
 | 
								case errors.StatusTooManyRequests:
 | 
				
			||||||
 | 
									retryAfter, _ := retryAfter(resp)
 | 
				
			||||||
 | 
									err = errors.NewServerTimeout(r.resource, r.verb, retryAfter)
 | 
				
			||||||
 | 
								case http.StatusInternalServerError:
 | 
				
			||||||
 | 
									err = errors.NewInternalError(fmt.Errorf(message))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return nil, false, err
 | 
								return nil, false, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -657,6 +675,30 @@ func (r *Request) transformResponse(body []byte, resp *http.Response, req *http.
 | 
				
			|||||||
	return body, created, nil
 | 
						return body, created, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// isTextResponse returns true if the response appears to be a textual media type.
 | 
				
			||||||
 | 
					func isTextResponse(resp *http.Response) bool {
 | 
				
			||||||
 | 
						contentType := resp.Header.Get("Content-Type")
 | 
				
			||||||
 | 
						if len(contentType) == 0 {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						media, _, err := mime.ParseMediaType(contentType)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.HasPrefix(media, "text/")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// retryAfter returns the value of the Retry-After header and true, or 0 and false if
 | 
				
			||||||
 | 
					// the header was missing or not a valid number.
 | 
				
			||||||
 | 
					func retryAfter(resp *http.Response) (int, bool) {
 | 
				
			||||||
 | 
						if h := resp.Header.Get("Retry-After"); len(h) > 0 {
 | 
				
			||||||
 | 
							if i, err := strconv.Atoi(h); err == nil {
 | 
				
			||||||
 | 
								return i, true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0, false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Result contains the result of calling Request.Do().
 | 
					// Result contains the result of calling Request.Do().
 | 
				
			||||||
type Result struct {
 | 
					type Result struct {
 | 
				
			||||||
	body    []byte
 | 
						body    []byte
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -222,6 +222,7 @@ func TestTransformResponse(t *testing.T) {
 | 
				
			|||||||
		Data     []byte
 | 
							Data     []byte
 | 
				
			||||||
		Created  bool
 | 
							Created  bool
 | 
				
			||||||
		Error    bool
 | 
							Error    bool
 | 
				
			||||||
 | 
							ErrFn    func(err error) bool
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{Response: &http.Response{StatusCode: 200}, Data: []byte{}},
 | 
							{Response: &http.Response{StatusCode: 200}, Data: []byte{}},
 | 
				
			||||||
		{Response: &http.Response{StatusCode: 201}, Data: []byte{}, Created: true},
 | 
							{Response: &http.Response{StatusCode: 201}, Data: []byte{}, Created: true},
 | 
				
			||||||
@@ -230,6 +231,30 @@ func TestTransformResponse(t *testing.T) {
 | 
				
			|||||||
		{Response: &http.Response{StatusCode: 422}, Error: true},
 | 
							{Response: &http.Response{StatusCode: 422}, Error: true},
 | 
				
			||||||
		{Response: &http.Response{StatusCode: 409}, Error: true},
 | 
							{Response: &http.Response{StatusCode: 409}, Error: true},
 | 
				
			||||||
		{Response: &http.Response{StatusCode: 404}, Error: true},
 | 
							{Response: &http.Response{StatusCode: 404}, Error: true},
 | 
				
			||||||
 | 
							{Response: &http.Response{StatusCode: 401}, Error: true},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Response: &http.Response{
 | 
				
			||||||
 | 
									StatusCode: 401,
 | 
				
			||||||
 | 
									Header:     http.Header{"Content-Type": []string{"application/json"}},
 | 
				
			||||||
 | 
									Body:       ioutil.NopCloser(bytes.NewReader(invalid)),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Error: true,
 | 
				
			||||||
 | 
								ErrFn: func(err error) bool {
 | 
				
			||||||
 | 
									return err.Error() != "aaaaa" && apierrors.IsUnauthorized(err)
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Response: &http.Response{
 | 
				
			||||||
 | 
									StatusCode: 401,
 | 
				
			||||||
 | 
									Header:     http.Header{"Content-Type": []string{"text/any"}},
 | 
				
			||||||
 | 
									Body:       ioutil.NopCloser(bytes.NewReader(invalid)),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Error: true,
 | 
				
			||||||
 | 
								ErrFn: func(err error) bool {
 | 
				
			||||||
 | 
									return err.Error() == "aaaaa" && apierrors.IsUnauthorized(err)
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{Response: &http.Response{StatusCode: 403}, Error: true},
 | 
				
			||||||
		{Response: &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
 | 
							{Response: &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
 | 
				
			||||||
		{Response: &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
 | 
							{Response: &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -246,6 +271,18 @@ func TestTransformResponse(t *testing.T) {
 | 
				
			|||||||
		hasErr := err != nil
 | 
							hasErr := err != nil
 | 
				
			||||||
		if hasErr != test.Error {
 | 
							if hasErr != test.Error {
 | 
				
			||||||
			t.Errorf("%d: unexpected error: %t %v", i, test.Error, err)
 | 
								t.Errorf("%d: unexpected error: %t %v", i, test.Error, err)
 | 
				
			||||||
 | 
							} else if hasErr && test.Response.StatusCode > 399 {
 | 
				
			||||||
 | 
								status, ok := err.(APIStatus)
 | 
				
			||||||
 | 
								if !ok {
 | 
				
			||||||
 | 
									t.Errorf("%d: response should have been transformable into APIStatus: %v", i, err)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if status.Status().Code != test.Response.StatusCode {
 | 
				
			||||||
 | 
									t.Errorf("%d: status code did not match response: %#v", i, status.Status())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if test.ErrFn != nil && !test.ErrFn(err) {
 | 
				
			||||||
 | 
								t.Errorf("%d: error function did not match: %v", i, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !(test.Data == nil && response == nil) && !api.Semantic.DeepDerivative(test.Data, response) {
 | 
							if !(test.Data == nil && response == nil) && !api.Semantic.DeepDerivative(test.Data, response) {
 | 
				
			||||||
			t.Errorf("%d: unexpected response: %#v %#v", i, test.Data, response)
 | 
								t.Errorf("%d: unexpected response: %#v %#v", i, test.Data, response)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -225,7 +225,13 @@ func TestDoRequestSuccess(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDoRequestFailed(t *testing.T) {
 | 
					func TestDoRequestFailed(t *testing.T) {
 | 
				
			||||||
	status := &api.Status{Status: api.StatusFailure, Reason: api.StatusReasonInvalid, Details: &api.StatusDetails{ID: "test", Kind: "test"}}
 | 
						status := &api.Status{
 | 
				
			||||||
 | 
							Code:    http.StatusNotFound,
 | 
				
			||||||
 | 
							Status:  api.StatusFailure,
 | 
				
			||||||
 | 
							Reason:  api.StatusReasonNotFound,
 | 
				
			||||||
 | 
							Message: " \"\" not found",
 | 
				
			||||||
 | 
							Details: &api.StatusDetails{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	expectedBody, _ := latest.Codec.Encode(status)
 | 
						expectedBody, _ := latest.Codec.Encode(status)
 | 
				
			||||||
	fakeHandler := util.FakeHandler{
 | 
						fakeHandler := util.FakeHandler{
 | 
				
			||||||
		StatusCode:   404,
 | 
							StatusCode:   404,
 | 
				
			||||||
@@ -253,7 +259,7 @@ func TestDoRequestFailed(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	actual := ss.Status()
 | 
						actual := ss.Status()
 | 
				
			||||||
	if !reflect.DeepEqual(status, &actual) {
 | 
						if !reflect.DeepEqual(status, &actual) {
 | 
				
			||||||
		t.Errorf("Unexpected mis-match. Expected %#v.  Saw %#v", status, actual)
 | 
							t.Errorf("Unexpected mis-match: %s", util.ObjectDiff(status, &actual))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user