forked from TrueCloudLab/distribution
registry/client: combine SuccessStatus and HandleErrorResponse
The SuccessStatus acted on the response's status code, and was used to return early, before checking the same status code with HandleErrorResponse. This patch combines both functions into a HandleHTTPResponseError, which returns an error for "non-success" status-codes, which simplifies handling of responses, and makes some logic slightly more idiomatic. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
285b601af9
commit
c8ba5d7081
5 changed files with 170 additions and 150 deletions
|
@ -360,8 +360,7 @@ func (th *tokenHandler) fetchTokenWithOAuth(ctx context.Context, realm *url.URL,
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if !client.SuccessStatus(resp.StatusCode) {
|
if err := client.HandleHTTPResponseError(resp); err != nil {
|
||||||
err := client.HandleErrorResponse(resp)
|
|
||||||
return "", time.Time{}, err
|
return "", time.Time{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,8 +442,7 @@ func (th *tokenHandler) fetchTokenWithBasicAuth(ctx context.Context, realm *url.
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if !client.SuccessStatus(resp.StatusCode) {
|
if err := client.HandleHTTPResponseError(resp); err != nil {
|
||||||
err := client.HandleErrorResponse(resp)
|
|
||||||
return "", time.Time{}, err
|
return "", time.Time{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (hbu *httpBlobUpload) handleErrorResponse(resp *http.Response) error {
|
||||||
if resp.StatusCode == http.StatusNotFound {
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
return distribution.ErrBlobUploadUnknown
|
return distribution.ErrBlobUploadUnknown
|
||||||
}
|
}
|
||||||
return HandleErrorResponse(resp)
|
return HandleHTTPResponseError(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hbu *httpBlobUpload) ReadFrom(r io.Reader) (n int64, err error) {
|
func (hbu *httpBlobUpload) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
@ -51,8 +51,8 @@ func (hbu *httpBlobUpload) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if !SuccessStatus(resp.StatusCode) {
|
if err := hbu.handleErrorResponse(resp); err != nil {
|
||||||
return 0, hbu.handleErrorResponse(resp)
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hbu.uuid = resp.Header.Get("Docker-Upload-UUID")
|
hbu.uuid = resp.Header.Get("Docker-Upload-UUID")
|
||||||
|
@ -87,8 +87,8 @@ func (hbu *httpBlobUpload) Write(p []byte) (n int, err error) {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if !SuccessStatus(resp.StatusCode) {
|
if err := hbu.handleErrorResponse(resp); err != nil {
|
||||||
return 0, hbu.handleErrorResponse(resp)
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hbu.uuid = resp.Header.Get("Docker-Upload-UUID")
|
hbu.uuid = resp.Header.Get("Docker-Upload-UUID")
|
||||||
|
@ -137,8 +137,8 @@ func (hbu *httpBlobUpload) Commit(ctx context.Context, desc distribution.Descrip
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if !SuccessStatus(resp.StatusCode) {
|
if err := hbu.handleErrorResponse(resp); err != nil {
|
||||||
return distribution.Descriptor{}, hbu.handleErrorResponse(resp)
|
return distribution.Descriptor{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return hbu.statter.Stat(ctx, desc.Digest)
|
return hbu.statter.Stat(ctx, desc.Digest)
|
||||||
|
@ -155,7 +155,7 @@ func (hbu *httpBlobUpload) Cancel(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode == http.StatusNotFound || SuccessStatus(resp.StatusCode) {
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return hbu.handleErrorResponse(resp)
|
return hbu.handleErrorResponse(resp)
|
||||||
|
|
|
@ -116,11 +116,16 @@ func mergeErrors(err1, err2 error) error {
|
||||||
return errcode.Errors(append(makeErrorList(err1), makeErrorList(err2)...))
|
return errcode.Errors(append(makeErrorList(err1), makeErrorList(err2)...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleErrorResponse returns error parsed from HTTP response for an
|
// HandleHTTPResponseError returns error parsed from HTTP response, if any.
|
||||||
// unsuccessful HTTP response code (in the range 400 - 499 inclusive). An
|
// It returns nil if no error occurred (HTTP status 200-399), or an error
|
||||||
// UnexpectedHTTPStatusError returned for response code outside of expected
|
// for unsuccessful HTTP response codes (in the range 400 - 499 inclusive).
|
||||||
// range.
|
// If possible, it returns a typed error, but an UnexpectedHTTPStatusError
|
||||||
func HandleErrorResponse(resp *http.Response) error {
|
// is returned for response code outside the expected range (HTTP status < 200
|
||||||
|
// and > 500).
|
||||||
|
func HandleHTTPResponseError(resp *http.Response) error {
|
||||||
|
if resp.StatusCode >= 200 && resp.StatusCode <= 399 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if resp.StatusCode >= 400 && resp.StatusCode < 500 {
|
if resp.StatusCode >= 400 && resp.StatusCode < 500 {
|
||||||
// Check for OAuth errors within the `WWW-Authenticate` header first
|
// Check for OAuth errors within the `WWW-Authenticate` header first
|
||||||
// See https://tools.ietf.org/html/rfc6750#section-3
|
// See https://tools.ietf.org/html/rfc6750#section-3
|
||||||
|
@ -153,8 +158,23 @@ func HandleErrorResponse(resp *http.Response) error {
|
||||||
return &UnexpectedHTTPStatusError{Status: resp.Status}
|
return &UnexpectedHTTPStatusError{Status: resp.Status}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Deprecated: use [HandleHTTPResponseError] and check the error.
|
||||||
|
func HandleErrorResponse(resp *http.Response) error {
|
||||||
|
if resp.StatusCode >= 200 && resp.StatusCode <= 399 {
|
||||||
|
return &UnexpectedHTTPStatusError{Status: resp.Status}
|
||||||
|
}
|
||||||
|
return HandleHTTPResponseError(resp)
|
||||||
|
}
|
||||||
|
|
||||||
// SuccessStatus returns true if the argument is a successful HTTP response
|
// SuccessStatus returns true if the argument is a successful HTTP response
|
||||||
// code (in the range 200 - 399 inclusive).
|
// code (in the range 200 - 399 inclusive).
|
||||||
|
//
|
||||||
|
// Deprecated: use [HandleHTTPResponseError] and check the error.
|
||||||
func SuccessStatus(status int) bool {
|
func SuccessStatus(status int) bool {
|
||||||
return status >= 200 && status <= 399
|
return status >= 200 && status <= 399
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,18 @@ type nopCloser struct {
|
||||||
|
|
||||||
func (nopCloser) Close() error { return nil }
|
func (nopCloser) Close() error { return nil }
|
||||||
|
|
||||||
func TestHandleErrorResponse401ValidBody(t *testing.T) {
|
func TestHandleHTTPResponseError200ValidBody(t *testing.T) {
|
||||||
|
response := &http.Response{
|
||||||
|
Status: "200 OK",
|
||||||
|
StatusCode: 200,
|
||||||
|
}
|
||||||
|
err := HandleHTTPResponseError(response)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected no error, got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleHTTPResponseError401ValidBody(t *testing.T) {
|
||||||
json := `{"errors":[{"code":"UNAUTHORIZED","message":"action requires authentication"}]}`
|
json := `{"errors":[{"code":"UNAUTHORIZED","message":"action requires authentication"}]}`
|
||||||
response := &http.Response{
|
response := &http.Response{
|
||||||
Status: "401 Unauthorized",
|
Status: "401 Unauthorized",
|
||||||
|
@ -22,7 +33,7 @@ func TestHandleErrorResponse401ValidBody(t *testing.T) {
|
||||||
Body: nopCloser{bytes.NewBufferString(json)},
|
Body: nopCloser{bytes.NewBufferString(json)},
|
||||||
Header: http.Header{"Content-Type": []string{"application/json; charset=utf-8"}},
|
Header: http.Header{"Content-Type": []string{"application/json; charset=utf-8"}},
|
||||||
}
|
}
|
||||||
err := HandleErrorResponse(response)
|
err := HandleHTTPResponseError(response)
|
||||||
|
|
||||||
expectedMsg := "unauthorized: action requires authentication"
|
expectedMsg := "unauthorized: action requires authentication"
|
||||||
if !strings.Contains(err.Error(), expectedMsg) {
|
if !strings.Contains(err.Error(), expectedMsg) {
|
||||||
|
@ -30,7 +41,7 @@ func TestHandleErrorResponse401ValidBody(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorResponse401WithInvalidBody(t *testing.T) {
|
func TestHandleHTTPResponseError401WithInvalidBody(t *testing.T) {
|
||||||
json := "{invalid json}"
|
json := "{invalid json}"
|
||||||
response := &http.Response{
|
response := &http.Response{
|
||||||
Status: "401 Unauthorized",
|
Status: "401 Unauthorized",
|
||||||
|
@ -38,7 +49,7 @@ func TestHandleErrorResponse401WithInvalidBody(t *testing.T) {
|
||||||
Body: nopCloser{bytes.NewBufferString(json)},
|
Body: nopCloser{bytes.NewBufferString(json)},
|
||||||
Header: http.Header{"Content-Type": []string{"application/json; charset=utf-8"}},
|
Header: http.Header{"Content-Type": []string{"application/json; charset=utf-8"}},
|
||||||
}
|
}
|
||||||
err := HandleErrorResponse(response)
|
err := HandleHTTPResponseError(response)
|
||||||
|
|
||||||
expectedMsg := "unauthorized: authentication required"
|
expectedMsg := "unauthorized: authentication required"
|
||||||
if !strings.Contains(err.Error(), expectedMsg) {
|
if !strings.Contains(err.Error(), expectedMsg) {
|
||||||
|
@ -46,7 +57,7 @@ func TestHandleErrorResponse401WithInvalidBody(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorResponseExpectedStatusCode400ValidBody(t *testing.T) {
|
func TestHandleHTTPResponseErrorExpectedStatusCode400ValidBody(t *testing.T) {
|
||||||
json := `{"errors":[{"code":"DIGEST_INVALID","message":"provided digest does not match"}]}`
|
json := `{"errors":[{"code":"DIGEST_INVALID","message":"provided digest does not match"}]}`
|
||||||
response := &http.Response{
|
response := &http.Response{
|
||||||
Status: "400 Bad Request",
|
Status: "400 Bad Request",
|
||||||
|
@ -54,7 +65,7 @@ func TestHandleErrorResponseExpectedStatusCode400ValidBody(t *testing.T) {
|
||||||
Body: nopCloser{bytes.NewBufferString(json)},
|
Body: nopCloser{bytes.NewBufferString(json)},
|
||||||
Header: http.Header{"Content-Type": []string{"application/json"}},
|
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||||
}
|
}
|
||||||
err := HandleErrorResponse(response)
|
err := HandleHTTPResponseError(response)
|
||||||
|
|
||||||
expectedMsg := "digest invalid: provided digest does not match"
|
expectedMsg := "digest invalid: provided digest does not match"
|
||||||
if !strings.Contains(err.Error(), expectedMsg) {
|
if !strings.Contains(err.Error(), expectedMsg) {
|
||||||
|
@ -62,7 +73,7 @@ func TestHandleErrorResponseExpectedStatusCode400ValidBody(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorResponseExpectedStatusCode404EmptyErrorSlice(t *testing.T) {
|
func TestHandleHTTPResponseErrorExpectedStatusCode404EmptyErrorSlice(t *testing.T) {
|
||||||
json := `{"randomkey": "randomvalue"}`
|
json := `{"randomkey": "randomvalue"}`
|
||||||
response := &http.Response{
|
response := &http.Response{
|
||||||
Status: "404 Not Found",
|
Status: "404 Not Found",
|
||||||
|
@ -70,7 +81,7 @@ func TestHandleErrorResponseExpectedStatusCode404EmptyErrorSlice(t *testing.T) {
|
||||||
Body: nopCloser{bytes.NewBufferString(json)},
|
Body: nopCloser{bytes.NewBufferString(json)},
|
||||||
Header: http.Header{"Content-Type": []string{"application/json; charset=utf-8"}},
|
Header: http.Header{"Content-Type": []string{"application/json; charset=utf-8"}},
|
||||||
}
|
}
|
||||||
err := HandleErrorResponse(response)
|
err := HandleHTTPResponseError(response)
|
||||||
|
|
||||||
expectedMsg := `error parsing HTTP 404 response body: no error details found in HTTP response body: "{\"randomkey\": \"randomvalue\"}"`
|
expectedMsg := `error parsing HTTP 404 response body: no error details found in HTTP response body: "{\"randomkey\": \"randomvalue\"}"`
|
||||||
if !strings.Contains(err.Error(), expectedMsg) {
|
if !strings.Contains(err.Error(), expectedMsg) {
|
||||||
|
@ -78,7 +89,7 @@ func TestHandleErrorResponseExpectedStatusCode404EmptyErrorSlice(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorResponseExpectedStatusCode404InvalidBody(t *testing.T) {
|
func TestHandleHTTPResponseErrorExpectedStatusCode404InvalidBody(t *testing.T) {
|
||||||
json := "{invalid json}"
|
json := "{invalid json}"
|
||||||
response := &http.Response{
|
response := &http.Response{
|
||||||
Status: "404 Not Found",
|
Status: "404 Not Found",
|
||||||
|
@ -86,7 +97,7 @@ func TestHandleErrorResponseExpectedStatusCode404InvalidBody(t *testing.T) {
|
||||||
Body: nopCloser{bytes.NewBufferString(json)},
|
Body: nopCloser{bytes.NewBufferString(json)},
|
||||||
Header: http.Header{"Content-Type": []string{"application/json"}},
|
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||||
}
|
}
|
||||||
err := HandleErrorResponse(response)
|
err := HandleHTTPResponseError(response)
|
||||||
|
|
||||||
expectedMsg := "error parsing HTTP 404 response body: invalid character 'i' looking for beginning of object key string: \"{invalid json}\""
|
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) {
|
if !strings.Contains(err.Error(), expectedMsg) {
|
||||||
|
@ -94,14 +105,14 @@ func TestHandleErrorResponseExpectedStatusCode404InvalidBody(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorResponseUnexpectedStatusCode501(t *testing.T) {
|
func TestHandleHTTPResponseErrorUnexpectedStatusCode501(t *testing.T) {
|
||||||
response := &http.Response{
|
response := &http.Response{
|
||||||
Status: "501 Not Implemented",
|
Status: "501 Not Implemented",
|
||||||
StatusCode: 501,
|
StatusCode: 501,
|
||||||
Body: nopCloser{bytes.NewBufferString("{\"Error Encountered\" : \"Function not implemented.\"}")},
|
Body: nopCloser{bytes.NewBufferString("{\"Error Encountered\" : \"Function not implemented.\"}")},
|
||||||
Header: http.Header{"Content-Type": []string{"application/json"}},
|
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||||
}
|
}
|
||||||
err := HandleErrorResponse(response)
|
err := HandleHTTPResponseError(response)
|
||||||
|
|
||||||
expectedMsg := "received unexpected HTTP status: 501 Not Implemented"
|
expectedMsg := "received unexpected HTTP status: 501 Not Implemented"
|
||||||
if !strings.Contains(err.Error(), expectedMsg) {
|
if !strings.Contains(err.Error(), expectedMsg) {
|
||||||
|
@ -109,7 +120,7 @@ func TestHandleErrorResponseUnexpectedStatusCode501(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorResponseInsufficientPrivileges403(t *testing.T) {
|
func TestHandleHTTPResponseErrorInsufficientPrivileges403(t *testing.T) {
|
||||||
json := `{"details":"requesting higher privileges than access token allows"}`
|
json := `{"details":"requesting higher privileges than access token allows"}`
|
||||||
response := &http.Response{
|
response := &http.Response{
|
||||||
Status: "403 Forbidden",
|
Status: "403 Forbidden",
|
||||||
|
@ -117,7 +128,7 @@ func TestHandleErrorResponseInsufficientPrivileges403(t *testing.T) {
|
||||||
Body: nopCloser{bytes.NewBufferString(json)},
|
Body: nopCloser{bytes.NewBufferString(json)},
|
||||||
Header: http.Header{"Content-Type": []string{"application/json"}},
|
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||||
}
|
}
|
||||||
err := HandleErrorResponse(response)
|
err := HandleHTTPResponseError(response)
|
||||||
|
|
||||||
expectedMsg := "denied: requesting higher privileges than access token allows"
|
expectedMsg := "denied: requesting higher privileges than access token allows"
|
||||||
if !strings.Contains(err.Error(), expectedMsg) {
|
if !strings.Contains(err.Error(), expectedMsg) {
|
||||||
|
@ -125,14 +136,14 @@ func TestHandleErrorResponseInsufficientPrivileges403(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorResponseNonJson(t *testing.T) {
|
func TestHandleHTTPResponseErrorNonJson(t *testing.T) {
|
||||||
msg := `{"details":"requesting higher privileges than access token allows"}`
|
msg := `{"details":"requesting higher privileges than access token allows"}`
|
||||||
response := &http.Response{
|
response := &http.Response{
|
||||||
Status: "403 Forbidden",
|
Status: "403 Forbidden",
|
||||||
StatusCode: 403,
|
StatusCode: 403,
|
||||||
Body: nopCloser{bytes.NewBufferString(msg)},
|
Body: nopCloser{bytes.NewBufferString(msg)},
|
||||||
}
|
}
|
||||||
err := HandleErrorResponse(response)
|
err := HandleHTTPResponseError(response)
|
||||||
|
|
||||||
if !strings.Contains(err.Error(), msg) {
|
if !strings.Contains(err.Error(), msg) {
|
||||||
t.Errorf("Expected %q, got: %q", msg, err.Error())
|
t.Errorf("Expected %q, got: %q", msg, err.Error())
|
||||||
|
|
|
@ -89,8 +89,6 @@ type registry struct {
|
||||||
// of the slice, starting at the value provided in 'last'. The number of entries will be returned along with io.EOF if there
|
// of the slice, starting at the value provided in 'last'. The number of entries will be returned along with io.EOF if there
|
||||||
// are no more entries
|
// are no more entries
|
||||||
func (r *registry) Repositories(ctx context.Context, entries []string, last string) (int, error) {
|
func (r *registry) Repositories(ctx context.Context, entries []string, last string) (int, error) {
|
||||||
var numFilled int
|
|
||||||
var returnErr error
|
|
||||||
|
|
||||||
values := buildCatalogValues(len(entries), last)
|
values := buildCatalogValues(len(entries), last)
|
||||||
u, err := r.ub.BuildCatalogURL(values)
|
u, err := r.ub.BuildCatalogURL(values)
|
||||||
|
@ -108,28 +106,27 @@ func (r *registry) Repositories(ctx context.Context, entries []string, last stri
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if SuccessStatus(resp.StatusCode) {
|
if err := HandleHTTPResponseError(resp); err != nil {
|
||||||
var ctlg struct {
|
return 0, err
|
||||||
Repositories []string `json:"repositories"`
|
|
||||||
}
|
|
||||||
decoder := json.NewDecoder(resp.Body)
|
|
||||||
|
|
||||||
if err := decoder.Decode(&ctlg); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(entries, ctlg.Repositories)
|
|
||||||
numFilled = len(ctlg.Repositories)
|
|
||||||
|
|
||||||
link := resp.Header.Get("Link")
|
|
||||||
if link == "" {
|
|
||||||
returnErr = io.EOF
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 0, HandleErrorResponse(resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return numFilled, returnErr
|
var ctlg struct {
|
||||||
|
Repositories []string `json:"repositories"`
|
||||||
|
}
|
||||||
|
decoder := json.NewDecoder(resp.Body)
|
||||||
|
|
||||||
|
if err := decoder.Decode(&ctlg); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(entries, ctlg.Repositories)
|
||||||
|
numFilled := len(ctlg.Repositories)
|
||||||
|
|
||||||
|
if resp.Header.Get("Link") == "" {
|
||||||
|
return numFilled, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
return numFilled, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRepository creates a new Repository for the given repository name and base URL.
|
// NewRepository creates a new Repository for the given repository name and base URL.
|
||||||
|
@ -200,18 +197,17 @@ type tags struct {
|
||||||
|
|
||||||
// All returns all tags
|
// All returns all tags
|
||||||
func (t *tags) All(ctx context.Context) ([]string, error) {
|
func (t *tags) All(ctx context.Context) ([]string, error) {
|
||||||
var tags []string
|
|
||||||
|
|
||||||
listURLStr, err := t.ub.BuildTagsURL(t.name)
|
listURLStr, err := t.ub.BuildTagsURL(t.name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tags, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
listURL, err := url.Parse(listURLStr)
|
listURL, err := url.Parse(listURLStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tags, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var allTags []string
|
||||||
for {
|
for {
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, listURL.String(), nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, listURL.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -219,36 +215,36 @@ func (t *tags) All(ctx context.Context) ([]string, error) {
|
||||||
}
|
}
|
||||||
resp, err := t.client.Do(req)
|
resp, err := t.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tags, err
|
return allTags, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if SuccessStatus(resp.StatusCode) {
|
if err := HandleHTTPResponseError(resp); err != nil {
|
||||||
b, err := io.ReadAll(resp.Body)
|
return allTags, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return allTags, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsResponse := struct {
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(b, &tagsResponse); err != nil {
|
||||||
|
return allTags, err
|
||||||
|
}
|
||||||
|
allTags = append(allTags, tagsResponse.Tags...)
|
||||||
|
if link := resp.Header.Get("Link"); link != "" {
|
||||||
|
firsLink, _, _ := strings.Cut(link, ";")
|
||||||
|
linkURL, err := url.Parse(strings.Trim(firsLink, "<>"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tags, err
|
return allTags, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tagsResponse := struct {
|
listURL = listURL.ResolveReference(linkURL)
|
||||||
Tags []string `json:"tags"`
|
|
||||||
}{}
|
|
||||||
if err := json.Unmarshal(b, &tagsResponse); err != nil {
|
|
||||||
return tags, err
|
|
||||||
}
|
|
||||||
tags = append(tags, tagsResponse.Tags...)
|
|
||||||
if link := resp.Header.Get("Link"); link != "" {
|
|
||||||
firsLink, _, _ := strings.Cut(link, ";")
|
|
||||||
linkURL, err := url.Parse(strings.Trim(firsLink, "<>"))
|
|
||||||
if err != nil {
|
|
||||||
return tags, err
|
|
||||||
}
|
|
||||||
|
|
||||||
listURL = listURL.ResolveReference(linkURL)
|
|
||||||
} else {
|
|
||||||
return tags, nil
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return tags, HandleErrorResponse(resp)
|
return allTags, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,7 +341,7 @@ func (t *tags) Get(ctx context.Context, tag string) (distribution.Descriptor, er
|
||||||
if resp.StatusCode >= 200 && resp.StatusCode < 400 {
|
if resp.StatusCode >= 200 && resp.StatusCode < 400 {
|
||||||
return descriptorFromResponse(resp)
|
return descriptorFromResponse(resp)
|
||||||
}
|
}
|
||||||
return distribution.Descriptor{}, HandleErrorResponse(resp)
|
return distribution.Descriptor{}, HandleHTTPResponseError(resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,10 +374,7 @@ func (t *tags) Untag(ctx context.Context, tag string) error {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if SuccessStatus(resp.StatusCode) {
|
return HandleHTTPResponseError(resp)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return HandleErrorResponse(resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type manifests struct {
|
type manifests struct {
|
||||||
|
@ -411,12 +404,14 @@ func (ms *manifests) Exists(ctx context.Context, dgst digest.Digest) (bool, erro
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if SuccessStatus(resp.StatusCode) {
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
return true, nil
|
|
||||||
} else if resp.StatusCode == http.StatusNotFound {
|
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, HandleErrorResponse(resp)
|
|
||||||
|
if err := HandleHTTPResponseError(resp); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddEtagToTag allows a client to supply an eTag to Get which will be
|
// AddEtagToTag allows a client to supply an eTag to Get which will be
|
||||||
|
@ -517,25 +512,27 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode == http.StatusNotModified {
|
if resp.StatusCode == http.StatusNotModified {
|
||||||
return nil, distribution.ErrManifestNotModified
|
return nil, distribution.ErrManifestNotModified
|
||||||
} else if SuccessStatus(resp.StatusCode) {
|
|
||||||
if contentDgst != nil {
|
|
||||||
dgst, err := digest.Parse(resp.Header.Get("Docker-Content-Digest"))
|
|
||||||
if err == nil {
|
|
||||||
*contentDgst = dgst
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mt := resp.Header.Get("Content-Type")
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m, _, err := distribution.UnmarshalManifest(mt, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
}
|
||||||
return nil, HandleErrorResponse(resp)
|
if err := HandleHTTPResponseError(resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if contentDgst != nil {
|
||||||
|
dgst, err := digest.Parse(resp.Header.Get("Docker-Content-Digest"))
|
||||||
|
if err == nil {
|
||||||
|
*contentDgst = dgst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mt := resp.Header.Get("Content-Type")
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m, _, err := distribution.UnmarshalManifest(mt, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put puts a manifest. A tag can be specified using an options parameter which uses some shared state to hold the
|
// Put puts a manifest. A tag can be specified using an options parameter which uses some shared state to hold the
|
||||||
|
@ -594,17 +591,16 @@ func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options .
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if SuccessStatus(resp.StatusCode) {
|
if err := HandleHTTPResponseError(resp); err != nil {
|
||||||
dgstHeader := resp.Header.Get("Docker-Content-Digest")
|
return "", err
|
||||||
dgst, err := digest.Parse(dgstHeader)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return dgst, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", HandleErrorResponse(resp)
|
dgst, err := digest.Parse(resp.Header.Get("Docker-Content-Digest"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dgst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *manifests) Delete(ctx context.Context, dgst digest.Digest) error {
|
func (ms *manifests) Delete(ctx context.Context, dgst digest.Digest) error {
|
||||||
|
@ -627,10 +623,7 @@ func (ms *manifests) Delete(ctx context.Context, dgst digest.Digest) error {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if SuccessStatus(resp.StatusCode) {
|
return HandleHTTPResponseError(resp)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return HandleErrorResponse(resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(richardscothern): Restore interface and implementation with merge of #1050
|
// todo(richardscothern): Restore interface and implementation with merge of #1050
|
||||||
|
@ -689,7 +682,7 @@ func (bs *blobs) Open(ctx context.Context, dgst digest.Digest) (io.ReadSeekClose
|
||||||
if resp.StatusCode == http.StatusNotFound {
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
return distribution.ErrBlobUnknown
|
return distribution.ErrBlobUnknown
|
||||||
}
|
}
|
||||||
return HandleErrorResponse(resp)
|
return HandleHTTPResponseError(resp)
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -732,13 +725,11 @@ func (bs *blobs) Put(ctx context.Context, mediaType string, p []byte) (distribut
|
||||||
return distribution.Descriptor{}, fmt.Errorf("short copy: wrote %d of %d", n, len(p))
|
return distribution.Descriptor{}, fmt.Errorf("short copy: wrote %d of %d", n, len(p))
|
||||||
}
|
}
|
||||||
|
|
||||||
desc := distribution.Descriptor{
|
return writer.Commit(ctx, distribution.Descriptor{
|
||||||
MediaType: mediaType,
|
MediaType: mediaType,
|
||||||
Size: int64(len(p)),
|
Size: int64(len(p)),
|
||||||
Digest: dgstr.Digest(),
|
Digest: dgstr.Digest(),
|
||||||
}
|
})
|
||||||
|
|
||||||
return writer.Commit(ctx, desc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type optionFunc func(interface{}) error
|
type optionFunc func(interface{}) error
|
||||||
|
@ -827,7 +818,7 @@ func (bs *blobs) Create(ctx context.Context, options ...distribution.BlobCreateO
|
||||||
location: location,
|
location: location,
|
||||||
}, nil
|
}, nil
|
||||||
default:
|
default:
|
||||||
return nil, HandleErrorResponse(resp)
|
return nil, HandleHTTPResponseError(resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -877,26 +868,29 @@ func (bs *blobStatter) Stat(ctx context.Context, dgst digest.Digest) (distributi
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if SuccessStatus(resp.StatusCode) {
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
lengthHeader := resp.Header.Get("Content-Length")
|
|
||||||
if lengthHeader == "" {
|
|
||||||
return distribution.Descriptor{}, fmt.Errorf("missing content-length header for request: %s", u)
|
|
||||||
}
|
|
||||||
|
|
||||||
length, err := strconv.ParseInt(lengthHeader, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return distribution.Descriptor{}, fmt.Errorf("error parsing content-length: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return distribution.Descriptor{
|
|
||||||
MediaType: resp.Header.Get("Content-Type"),
|
|
||||||
Size: length,
|
|
||||||
Digest: dgst,
|
|
||||||
}, nil
|
|
||||||
} else if resp.StatusCode == http.StatusNotFound {
|
|
||||||
return distribution.Descriptor{}, distribution.ErrBlobUnknown
|
return distribution.Descriptor{}, distribution.ErrBlobUnknown
|
||||||
}
|
}
|
||||||
return distribution.Descriptor{}, HandleErrorResponse(resp)
|
|
||||||
|
if err := HandleHTTPResponseError(resp); err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lengthHeader := resp.Header.Get("Content-Length")
|
||||||
|
if lengthHeader == "" {
|
||||||
|
return distribution.Descriptor{}, fmt.Errorf("missing content-length header for request: %s", u)
|
||||||
|
}
|
||||||
|
|
||||||
|
length, err := strconv.ParseInt(lengthHeader, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, fmt.Errorf("error parsing content-length: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return distribution.Descriptor{
|
||||||
|
MediaType: resp.Header.Get("Content-Type"),
|
||||||
|
Size: length,
|
||||||
|
Digest: dgst,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildCatalogValues(maxEntries int, last string) url.Values {
|
func buildCatalogValues(maxEntries int, last string) url.Values {
|
||||||
|
@ -934,10 +928,7 @@ func (bs *blobStatter) Clear(ctx context.Context, dgst digest.Digest) error {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if SuccessStatus(resp.StatusCode) {
|
return HandleHTTPResponseError(resp)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return HandleErrorResponse(resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *blobStatter) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
func (bs *blobStatter) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
||||||
|
|
Loading…
Reference in a new issue