-
Notifications
You must be signed in to change notification settings - Fork 383
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1487 from Luap99/remove-deps1
Remove github.com/docker/distribution/registry/client package
- Loading branch information
Showing
7 changed files
with
272 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// Code below is taken from https://github.com/distribution/distribution/blob/a4d9db5a884b70be0c96dd6a7a9dbef4f2798c51/registry/client/errors.go | ||
// Copyright 2022 github.com/distribution/distribution authors. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package docker | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"net/http" | ||
|
||
"github.com/docker/distribution/registry/api/errcode" | ||
dockerChallenge "github.com/docker/distribution/registry/client/auth/challenge" | ||
) | ||
|
||
// errNoErrorsInBody is returned when an HTTP response body parses to an empty | ||
// errcode.Errors slice. | ||
var errNoErrorsInBody = errors.New("no error details found in HTTP response body") | ||
|
||
// unexpectedHTTPStatusError is returned when an unexpected HTTP status is | ||
// returned when making a registry api call. | ||
type unexpectedHTTPStatusError struct { | ||
Status string | ||
} | ||
|
||
func (e *unexpectedHTTPStatusError) Error() string { | ||
return fmt.Sprintf("received unexpected HTTP status: %s", e.Status) | ||
} | ||
|
||
// unexpectedHTTPResponseError is returned when an expected HTTP status code | ||
// is returned, but the content was unexpected and failed to be parsed. | ||
type unexpectedHTTPResponseError struct { | ||
ParseErr error | ||
StatusCode int | ||
Response []byte | ||
} | ||
|
||
func (e *unexpectedHTTPResponseError) Error() string { | ||
return fmt.Sprintf("error parsing HTTP %d response body: %s: %q", e.StatusCode, e.ParseErr.Error(), string(e.Response)) | ||
} | ||
|
||
func parseHTTPErrorResponse(statusCode int, r io.Reader) error { | ||
var errors errcode.Errors | ||
body, err := ioutil.ReadAll(r) | ||
if err != nil { | ||
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 != "" { | ||
switch statusCode { | ||
case http.StatusUnauthorized: | ||
return errcode.ErrorCodeUnauthorized.WithMessage(detailsErr.Details) | ||
case http.StatusTooManyRequests: | ||
return errcode.ErrorCodeTooManyRequests.WithMessage(detailsErr.Details) | ||
default: | ||
return errcode.ErrorCodeUnknown.WithMessage(detailsErr.Details) | ||
} | ||
} | ||
|
||
if err := json.Unmarshal(body, &errors); err != nil { | ||
return &unexpectedHTTPResponseError{ | ||
ParseErr: err, | ||
StatusCode: statusCode, | ||
Response: body, | ||
} | ||
} | ||
|
||
if len(errors) == 0 { | ||
// If there was no error specified in the body, return | ||
// UnexpectedHTTPResponseError. | ||
return &unexpectedHTTPResponseError{ | ||
ParseErr: errNoErrorsInBody, | ||
StatusCode: statusCode, | ||
Response: body, | ||
} | ||
} | ||
|
||
return errors | ||
} | ||
|
||
func makeErrorList(err error) []error { | ||
if errL, ok := err.(errcode.Errors); ok { | ||
return []error(errL) | ||
} | ||
return []error{err} | ||
} | ||
|
||
func mergeErrors(err1, err2 error) error { | ||
return errcode.Errors(append(makeErrorList(err1), makeErrorList(err2)...)) | ||
} | ||
|
||
// handleErrorResponse returns error parsed from HTTP response for an | ||
// unsuccessful HTTP response code (in the range 400 - 499 inclusive). An | ||
// UnexpectedHTTPStatusError returned for response code outside of expected | ||
// range. | ||
func handleErrorResponse(resp *http.Response) error { | ||
if resp.StatusCode >= 400 && resp.StatusCode < 500 { | ||
// Check for OAuth errors within the `WWW-Authenticate` header first | ||
// See https://tools.ietf.org/html/rfc6750#section-3 | ||
for _, c := range dockerChallenge.ResponseChallenges(resp) { | ||
if c.Scheme == "bearer" { | ||
var err errcode.Error | ||
// codes defined at https://tools.ietf.org/html/rfc6750#section-3.1 | ||
switch c.Parameters["error"] { | ||
case "invalid_token": | ||
err.Code = errcode.ErrorCodeUnauthorized | ||
case "insufficient_scope": | ||
err.Code = errcode.ErrorCodeDenied | ||
default: | ||
continue | ||
} | ||
if description := c.Parameters["error_description"]; description != "" { | ||
err.Message = description | ||
} else { | ||
err.Message = err.Code.Message() | ||
} | ||
|
||
return mergeErrors(err, parseHTTPErrorResponse(resp.StatusCode, resp.Body)) | ||
} | ||
} | ||
err := parseHTTPErrorResponse(resp.StatusCode, resp.Body) | ||
if uErr, ok := err.(*unexpectedHTTPResponseError); ok && resp.StatusCode == 401 { | ||
return errcode.ErrorCodeUnauthorized.WithDetail(uErr.Response) | ||
} | ||
return err | ||
} | ||
return &unexpectedHTTPStatusError{Status: resp.Status} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// Code below is taken from https://github.com/distribution/distribution/blob/a4d9db5a884b70be0c96dd6a7a9dbef4f2798c51/registry/client/errors.go | ||
// Copyright 2022 github.com/distribution/distribution authors. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package docker | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"net/http" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
type nopCloser struct { | ||
io.Reader | ||
} | ||
|
||
func (nopCloser) Close() error { return nil } | ||
|
||
func TestHandleErrorResponse401ValidBody(t *testing.T) { | ||
json := "{\"errors\":[{\"code\":\"UNAUTHORIZED\",\"message\":\"action requires authentication\"}]}" | ||
response := &http.Response{ | ||
Status: "401 Unauthorized", | ||
StatusCode: 401, | ||
Body: nopCloser{bytes.NewBufferString(json)}, | ||
} | ||
err := handleErrorResponse(response) | ||
|
||
expectedMsg := "unauthorized: action requires authentication" | ||
if !strings.Contains(err.Error(), expectedMsg) { | ||
t.Errorf("Expected \"%s\", got: \"%s\"", expectedMsg, err.Error()) | ||
} | ||
} | ||
|
||
func TestHandleErrorResponse401WithInvalidBody(t *testing.T) { | ||
json := "{invalid json}" | ||
response := &http.Response{ | ||
Status: "401 Unauthorized", | ||
StatusCode: 401, | ||
Body: nopCloser{bytes.NewBufferString(json)}, | ||
} | ||
err := handleErrorResponse(response) | ||
|
||
expectedMsg := "unauthorized: authentication required" | ||
if !strings.Contains(err.Error(), expectedMsg) { | ||
t.Errorf("Expected \"%s\", got: \"%s\"", expectedMsg, err.Error()) | ||
} | ||
} | ||
|
||
func TestHandleErrorResponseExpectedStatusCode400ValidBody(t *testing.T) { | ||
json := "{\"errors\":[{\"code\":\"DIGEST_INVALID\",\"message\":\"provided digest does not match\"}]}" | ||
response := &http.Response{ | ||
Status: "400 Bad Request", | ||
StatusCode: 400, | ||
Body: nopCloser{bytes.NewBufferString(json)}, | ||
} | ||
err := handleErrorResponse(response) | ||
|
||
expectedMsg := "digest invalid: provided digest does not match" | ||
if !strings.Contains(err.Error(), expectedMsg) { | ||
t.Errorf("Expected \"%s\", got: \"%s\"", expectedMsg, err.Error()) | ||
} | ||
} | ||
|
||
func TestHandleErrorResponseExpectedStatusCode404EmptyErrorSlice(t *testing.T) { | ||
json := `{"randomkey": "randomvalue"}` | ||
response := &http.Response{ | ||
Status: "404 Not Found", | ||
StatusCode: 404, | ||
Body: nopCloser{bytes.NewBufferString(json)}, | ||
} | ||
err := handleErrorResponse(response) | ||
|
||
expectedMsg := `error parsing HTTP 404 response body: no error details found in HTTP response body: "{\"randomkey\": \"randomvalue\"}"` | ||
if !strings.Contains(err.Error(), expectedMsg) { | ||
t.Errorf("Expected \"%s\", got: \"%s\"", expectedMsg, err.Error()) | ||
} | ||
} | ||
|
||
func TestHandleErrorResponseExpectedStatusCode404InvalidBody(t *testing.T) { | ||
json := "{invalid json}" | ||
response := &http.Response{ | ||
Status: "404 Not Found", | ||
StatusCode: 404, | ||
Body: nopCloser{bytes.NewBufferString(json)}, | ||
} | ||
err := handleErrorResponse(response) | ||
|
||
expectedMsg := "error parsing HTTP 404 response body: invalid character 'i' looking for beginning of object key string: \"{invalid json}\"" | ||
if !strings.Contains(err.Error(), expectedMsg) { | ||
t.Errorf("Expected \"%s\", got: \"%s\"", expectedMsg, err.Error()) | ||
} | ||
} | ||
|
||
func TestHandleErrorResponseUnexpectedStatusCode501(t *testing.T) { | ||
response := &http.Response{ | ||
Status: "501 Not Implemented", | ||
StatusCode: 501, | ||
Body: nopCloser{bytes.NewBufferString("{\"Error Encountered\" : \"Function not implemented.\"}")}, | ||
} | ||
err := handleErrorResponse(response) | ||
|
||
expectedMsg := "received unexpected HTTP status: 501 Not Implemented" | ||
if !strings.Contains(err.Error(), expectedMsg) { | ||
t.Errorf("Expected \"%s\", got: \"%s\"", expectedMsg, err.Error()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters