Updated documentation and adjusted field names

This commit is contained in:
Clayton Coleman
2015-03-26 17:24:17 -04:00
parent 524f8731b5
commit aa44d29d10
7 changed files with 65 additions and 30 deletions

View File

@@ -198,15 +198,15 @@ func NewMethodNotSupported(kind, action string) error {
// NewServerTimeout returns an error indicating the requested action could not be completed due to a
// transient error, and the client should try again.
func NewServerTimeout(kind, operation string, retryAfter int) error {
func NewServerTimeout(kind, operation string, retryAfterSeconds int) error {
return &StatusError{api.Status{
Status: api.StatusFailure,
Code: http.StatusInternalServerError,
Reason: api.StatusReasonServerTimeout,
Details: &api.StatusDetails{
Kind: kind,
ID: operation,
RetryAfter: retryAfter,
Kind: kind,
ID: operation,
RetryAfterSeconds: retryAfterSeconds,
},
Message: fmt.Sprintf("The %s operation against %s could not be completed at this time, please try again.", operation, kind),
}}
@@ -227,14 +227,14 @@ func NewInternalError(err error) error {
// NewTimeoutError returns an error indicating that a timeout occurred before the request
// could be completed. Clients may retry, but the operation may still complete.
func NewTimeoutError(message string, retryAfter int) error {
func NewTimeoutError(message string, retryAfterSeconds int) error {
return &StatusError{api.Status{
Status: api.StatusFailure,
Code: StatusServerTimeout,
Reason: api.StatusReasonTimeout,
Message: fmt.Sprintf("Timeout: %s", message),
Details: &api.StatusDetails{
RetryAfter: retryAfter,
RetryAfterSeconds: retryAfterSeconds,
},
}}
}
@@ -308,7 +308,7 @@ func SuggestsClientDelay(err error) (int, bool) {
if t.Status().Details != nil {
switch t.Status().Reason {
case api.StatusReasonServerTimeout, api.StatusReasonTimeout:
return t.Status().Details.RetryAfter, true
return t.Status().Details.RetryAfterSeconds, true
}
}
}

View File

@@ -1201,7 +1201,7 @@ type StatusDetails struct {
// failure. Not all StatusReasons may provide detailed causes.
Causes []StatusCause `json:"causes,omitempty"`
// If specified, the time in seconds before the operation should be retried.
RetryAfter int `json:"retryAfter,omitempty"`
RetryAfterSeconds int `json:"retryAfterSeconds,omitempty"`
}
// Values of Status.Status
@@ -1285,7 +1285,7 @@ const (
// Details (optional):
// "kind" string - the kind attribute of the resource being acted on.
// "id" string - the operation that is being attempted.
// "retryAfter" int - the number of seconds before the operation should be retried
// "retryAfterSeconds" int - the number of seconds before the operation should be retried
// Status code 500
StatusReasonServerTimeout StatusReason = "ServerTimeout"
@@ -1293,9 +1293,9 @@ const (
// Clients can get this response only when they specified a timeout param in the request,
// 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.
// wait at least the number of seconds specified by the retryAfterSeconds field.
// Details (optional):
// "retryAfter" int - the number of seconds before the operation should be retried
// "retryAfterSeconds" int - the number of seconds before the operation should be retried
// Status code 504
StatusReasonTimeout StatusReason = "Timeout"

View File

@@ -1015,7 +1015,7 @@ type StatusDetails struct {
// 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"`
RetryAfterSeconds int `json:"retryAfterSeconds,omitempty" description:"the number of seconds before the client should attempt to retry this operation"`
}
// Values of Status.Status

View File

@@ -1029,7 +1029,7 @@ type StatusDetails struct {
// 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"`
RetryAfterSeconds int `json:"retryAfterSeconds,omitempty" description:"the number of seconds before the client should attempt to retry this operation"`
}
// Values of Status.Status

View File

@@ -1188,7 +1188,7 @@ type StatusDetails struct {
// 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"`
RetryAfterSeconds int `json:"retryAfterSeconds,omitempty" description:"the number of seconds before the client should attempt to retry this operation"`
}
// Values of Status.Status

View File

@@ -645,6 +645,24 @@ func (r *Request) transformResponse(resp *http.Response, req *http.Request, body
}
// transformUnstructuredResponseError handles an error from the server that is not in a structured form.
// It is expected to transform any response that is not recognizable as a clear server sent error from the
// K8S API using the information provided with the request. In practice, HTTP proxies and client libraries
// introduce a level of uncertainty to the responses returned by servers that in common use result in
// unexpected responses. The rough structure is:
//
// 1. Assume the server sends you something sane - JSON + well defined error objects + proper codes
// - this is the happy path
// - when you get this output, trust what the server sends
// 2. Guard against empty fields / bodies in received JSON and attempt to cull sufficient info from them to
// generate a reasonable facsimile of the original failure.
// - Be sure to use a distinct error type or flag that allows a client to distinguish between this and error 1 above
// 3. Handle true disconnect failures / completely malformed data by moving up to a more generic client error
// 4. Distinguish between various connection failures like SSL certificates, timeouts, proxy errors, unexpected
// initial contact, the presence of mismatched body contents from posted content types
// - Give these a separate distinct error type and capture as much as possible of the original message
//
// TODO: introduce further levels of refinement that allow a client to distinguish between 1 and 2-3.
// TODO: introduce transformation of generic http.Client.Do() errors that separates 4.
func (r *Request) transformUnstructuredResponseError(resp *http.Response, req *http.Request, body []byte) error {
if body == nil && resp.Body != nil {
if data, err := ioutil.ReadAll(resp.Body); err == nil {
@@ -679,11 +697,11 @@ func (r *Request) transformUnstructuredResponseError(resp *http.Response, req *h
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)
retryAfterSeconds, _ := retryAfterSeconds(resp)
err = errors.NewServerTimeout(r.resource, r.verb, retryAfterSeconds)
case errors.StatusTooManyRequests:
retryAfter, _ := retryAfter(resp)
err = errors.NewServerTimeout(r.resource, r.verb, retryAfter)
retryAfterSeconds, _ := retryAfterSeconds(resp)
err = errors.NewServerTimeout(r.resource, r.verb, retryAfterSeconds)
case http.StatusInternalServerError:
err = errors.NewInternalError(fmt.Errorf(message))
}
@@ -703,9 +721,9 @@ func isTextResponse(resp *http.Response) bool {
return strings.HasPrefix(media, "text/")
}
// retryAfter returns the value of the Retry-After header and true, or 0 and false if
// retryAfterSeconds 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) {
func retryAfterSeconds(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