Handle nonstandard token endpoint errors

https://github.com/docker/distribution/pull/1249 changed token fetching
to parse HTTP error response bodies as serialized errcodes. However,
Docker Hub's authentication endpoint does not return error bodies in
this format. To work around this, convert its format into
ErrCodeUnauthorized or ErrCodeUnknown.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2016-01-20 14:45:08 -08:00
parent 05b8317291
commit 59254013be
2 changed files with 25 additions and 3 deletions

View file

@ -69,6 +69,15 @@ func (ec *ErrorCode) UnmarshalText(text []byte) error {
return nil return nil
} }
// WithMessage creates a new Error struct based on the passed-in info and
// overrides the Message property.
func (ec ErrorCode) WithMessage(message string) Error {
return Error{
Code: ec,
Message: message,
}
}
// WithDetail creates a new Error struct based on the passed-in info and // WithDetail creates a new Error struct based on the passed-in info and
// set the Detail property appropriately // set the Detail property appropriately
func (ec ErrorCode) WithDetail(detail interface{}) Error { func (ec ErrorCode) WithDetail(detail interface{}) Error {

View file

@ -31,13 +31,26 @@ func (e *UnexpectedHTTPResponseError) Error() string {
return fmt.Sprintf("Error parsing HTTP response: %s: %q", e.ParseErr.Error(), string(e.Response)) return fmt.Sprintf("Error parsing HTTP response: %s: %q", e.ParseErr.Error(), string(e.Response))
} }
func parseHTTPErrorResponse(r io.Reader) error { func parseHTTPErrorResponse(statusCode int, r io.Reader) error {
var errors errcode.Errors var errors errcode.Errors
body, err := ioutil.ReadAll(r) body, err := ioutil.ReadAll(r)
if err != nil { if err != nil {
return err return err
} }
// For backward compatibility, handle irregularly formatted
// messages that contain a "details" field.
var detailsErr struct {
Details string `json:"details"`
}
err = json.Unmarshal(body, &detailsErr)
if err == nil && detailsErr.Details != "" {
if statusCode == http.StatusUnauthorized {
return errcode.ErrorCodeUnauthorized.WithMessage(detailsErr.Details)
}
return errcode.ErrorCodeUnknown.WithMessage(detailsErr.Details)
}
if err := json.Unmarshal(body, &errors); err != nil { if err := json.Unmarshal(body, &errors); err != nil {
return &UnexpectedHTTPResponseError{ return &UnexpectedHTTPResponseError{
ParseErr: err, ParseErr: err,
@ -53,14 +66,14 @@ func parseHTTPErrorResponse(r io.Reader) error {
// range. // range.
func HandleErrorResponse(resp *http.Response) error { func HandleErrorResponse(resp *http.Response) error {
if resp.StatusCode == 401 { if resp.StatusCode == 401 {
err := parseHTTPErrorResponse(resp.Body) err := parseHTTPErrorResponse(resp.StatusCode, resp.Body)
if uErr, ok := err.(*UnexpectedHTTPResponseError); ok { if uErr, ok := err.(*UnexpectedHTTPResponseError); ok {
return errcode.ErrorCodeUnauthorized.WithDetail(uErr.Response) return errcode.ErrorCodeUnauthorized.WithDetail(uErr.Response)
} }
return err return err
} }
if resp.StatusCode >= 400 && resp.StatusCode < 500 { if resp.StatusCode >= 400 && resp.StatusCode < 500 {
return parseHTTPErrorResponse(resp.Body) return parseHTTPErrorResponse(resp.StatusCode, resp.Body)
} }
return &UnexpectedHTTPStatusError{Status: resp.Status} return &UnexpectedHTTPStatusError{Status: resp.Status}
} }