commit
						c7dc83442b
					
				|  | @ -0,0 +1,135 @@ | |||
| package errors | ||||
| 
 | ||||
| import "net/http" | ||||
| 
 | ||||
| // ErrorDescriptor provides relevant information about a given error code.
 | ||||
| type ErrorDescriptor struct { | ||||
| 	// Code is the error code that this descriptor describes.
 | ||||
| 	Code ErrorCode | ||||
| 
 | ||||
| 	// Value provides a unique, string key, often captilized with
 | ||||
| 	// underscores, to identify the error code. This value is used as the
 | ||||
| 	// keyed value when serializing api errors.
 | ||||
| 	Value string | ||||
| 
 | ||||
| 	// Message is a short, human readable decription of the error condition
 | ||||
| 	// included in API responses.
 | ||||
| 	Message string | ||||
| 
 | ||||
| 	// Description provides a complete account of the errors purpose, suitable
 | ||||
| 	// for use in documentation.
 | ||||
| 	Description string | ||||
| 
 | ||||
| 	// DefaultStatusCode should to be returned via the HTTP API. Some error
 | ||||
| 	// may have different status codes depending on the situation.
 | ||||
| 	DefaultStatusCode int | ||||
| } | ||||
| 
 | ||||
| var descriptors = []ErrorDescriptor{ | ||||
| 	{ | ||||
| 		Code:    ErrorCodeUnknown, | ||||
| 		Value:   "UNKNOWN", | ||||
| 		Message: "unknown error", | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeDigestInvalid, | ||||
| 		Value:   "DIGEST_INVALID", | ||||
| 		Message: "provided digest did not match uploaded content", | ||||
| 		Description: `When a blob is uploaded, the registry will check that | ||||
| 		the content matches the digest provided by the client. The error may | ||||
| 		include a detail structure with the key "digest", including the | ||||
| 		invalid digest string. This error may also be returned when a manifest | ||||
| 		includes an invalid layer digest.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeSizeInvalid, | ||||
| 		Value:   "SIZE_INVALID", | ||||
| 		Message: "provided length did not match content length", | ||||
| 		Description: `When a layer is uploaded, the provided size will be | ||||
| 		checked against the uploaded content. If they do not match, this error | ||||
| 		will be returned.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeNameInvalid, | ||||
| 		Value:   "NAME_INVALID", | ||||
| 		Message: "manifest name did not match URI", | ||||
| 		Description: `During a manifest upload, if the name in the manifest | ||||
| 		does not match the uri name, this error will be returned.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeTagInvalid, | ||||
| 		Value:   "TAG_INVALID", | ||||
| 		Message: "manifest tag did not match URI", | ||||
| 		Description: `During a manifest upload, if the tag in the manifest | ||||
| 		does not match the uri tag, this error will be returned.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeNameUnknown, | ||||
| 		Value:   "NAME_UNKNOWN", | ||||
| 		Message: "repository name not known to registry", | ||||
| 		Description: `This is returned if the name used during an operation is | ||||
| 		unknown to the registry.`, | ||||
| 		DefaultStatusCode: http.StatusNotFound, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeManifestUnknown, | ||||
| 		Value:   "MANIFEST_UNKNOWN", | ||||
| 		Message: "manifest unknown", | ||||
| 		Description: `This error is returned when the manifest, identified by | ||||
| 		name and tag is unknown to the repository.`, | ||||
| 		DefaultStatusCode: http.StatusNotFound, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeManifestInvalid, | ||||
| 		Value:   "MANIFEST_INVALID", | ||||
| 		Message: "manifest invalid", | ||||
| 		Description: `During upload, manifests undergo several checks ensuring | ||||
| 		validity. If those checks fail, this error may be returned, unless a | ||||
| 		more specific error is included.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeManifestUnverified, | ||||
| 		Value:   "MANIFEST_UNVERIFIED", | ||||
| 		Message: "manifest failed signature verification", | ||||
| 		Description: `During manifest upload, if the manifest fails signature | ||||
| 		verification, this error will be returned.`, | ||||
| 		DefaultStatusCode: http.StatusBadRequest, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Code:    ErrorCodeBlobUnknown, | ||||
| 		Value:   "BLOB_UNKNOWN", | ||||
| 		Message: "blob unknown to registry", | ||||
| 		Description: `This error may be returned when a blob is unknown to the | ||||
| 		registry in a specified repository. This can be returned with a | ||||
| 		standard get or if a manifest references an unknown layer during | ||||
| 		upload.`, | ||||
| 		DefaultStatusCode: http.StatusNotFound, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		Code:    ErrorCodeBlobUploadUnknown, | ||||
| 		Value:   "BLOB_UPLOAD_UNKNOWN", | ||||
| 		Message: "blob upload unknown to registry", | ||||
| 		Description: `If a blob upload has been cancelled or was never | ||||
| 		started, this error code may be returned.`, | ||||
| 		DefaultStatusCode: http.StatusNotFound, | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| var errorCodeToDescriptors map[ErrorCode]ErrorDescriptor | ||||
| var idToDescriptors map[string]ErrorDescriptor | ||||
| 
 | ||||
| func init() { | ||||
| 	errorCodeToDescriptors = make(map[ErrorCode]ErrorDescriptor, len(descriptors)) | ||||
| 	idToDescriptors = make(map[string]ErrorDescriptor, len(descriptors)) | ||||
| 
 | ||||
| 	for _, descriptor := range descriptors { | ||||
| 		errorCodeToDescriptors[descriptor.Code] = descriptor | ||||
| 		idToDescriptors[descriptor.Value] = descriptor | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,193 @@ | |||
| // Package errors describes the error codes that may be returned via the
 | ||||
| // Docker Registry JSON HTTP API V2. In addition to declaractions,
 | ||||
| // descriptions about the error codes and the conditions causing them are
 | ||||
| // avialable in detail.
 | ||||
| //
 | ||||
| // Error definitions here are considered to be locked down for the V2 registry
 | ||||
| // api. Any changes must be considered carefully and should not proceed
 | ||||
| // without a change proposal in docker core.
 | ||||
| package errors | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // ErrorCode represents the error type. The errors are serialized via strings
 | ||||
| // and the integer format may change and should *never* be exported.
 | ||||
| type ErrorCode int | ||||
| 
 | ||||
| const ( | ||||
| 	// ErrorCodeUnknown is a catch-all for errors not defined below.
 | ||||
| 	ErrorCodeUnknown ErrorCode = iota | ||||
| 
 | ||||
| 	// ErrorCodeDigestInvalid is returned when uploading a blob if the
 | ||||
| 	// provided digest does not match the blob contents.
 | ||||
| 	ErrorCodeDigestInvalid | ||||
| 
 | ||||
| 	// ErrorCodeSizeInvalid is returned when uploading a blob if the provided
 | ||||
| 	// size does not match the content length.
 | ||||
| 	ErrorCodeSizeInvalid | ||||
| 
 | ||||
| 	// ErrorCodeNameInvalid is returned when the name in the manifest does not
 | ||||
| 	// match the provided name.
 | ||||
| 	ErrorCodeNameInvalid | ||||
| 
 | ||||
| 	// ErrorCodeTagInvalid is returned when the tag in the manifest does not
 | ||||
| 	// match the provided tag.
 | ||||
| 	ErrorCodeTagInvalid | ||||
| 
 | ||||
| 	// ErrorCodeNameUnknown when the repository name is not known.
 | ||||
| 	ErrorCodeNameUnknown | ||||
| 
 | ||||
| 	// ErrorCodeManifestUnknown returned when image manifest is unknown.
 | ||||
| 	ErrorCodeManifestUnknown | ||||
| 
 | ||||
| 	// ErrorCodeManifestInvalid returned when an image manifest is invalid,
 | ||||
| 	// typically during a PUT operation. This error encompasses all errors
 | ||||
| 	// encountered during manifest validation that aren't signature errors.
 | ||||
| 	ErrorCodeManifestInvalid | ||||
| 
 | ||||
| 	// ErrorCodeManifestUnverified is returned when the manifest fails
 | ||||
| 	// signature verfication.
 | ||||
| 	ErrorCodeManifestUnverified | ||||
| 
 | ||||
| 	// ErrorCodeBlobUnknown is returned when a blob is unknown to the
 | ||||
| 	// registry. This can happen when the manifest references a nonexistent
 | ||||
| 	// layer or the result is not found by a blob fetch.
 | ||||
| 	ErrorCodeBlobUnknown | ||||
| 
 | ||||
| 	// ErrorCodeBlobUploadUnknown is returned when an upload is unknown.
 | ||||
| 	ErrorCodeBlobUploadUnknown | ||||
| ) | ||||
| 
 | ||||
| // ParseErrorCode attempts to parse the error code string, returning
 | ||||
| // ErrorCodeUnknown if the error is not known.
 | ||||
| func ParseErrorCode(s string) ErrorCode { | ||||
| 	desc, ok := idToDescriptors[s] | ||||
| 
 | ||||
| 	if !ok { | ||||
| 		return ErrorCodeUnknown | ||||
| 	} | ||||
| 
 | ||||
| 	return desc.Code | ||||
| } | ||||
| 
 | ||||
| // Descriptor returns the descriptor for the error code.
 | ||||
| func (ec ErrorCode) Descriptor() ErrorDescriptor { | ||||
| 	d, ok := errorCodeToDescriptors[ec] | ||||
| 
 | ||||
| 	if !ok { | ||||
| 		return ErrorCodeUnknown.Descriptor() | ||||
| 	} | ||||
| 
 | ||||
| 	return d | ||||
| } | ||||
| 
 | ||||
| // String returns the canonical identifier for this error code.
 | ||||
| func (ec ErrorCode) String() string { | ||||
| 	return ec.Descriptor().Value | ||||
| } | ||||
| 
 | ||||
| // Message returned the human-readable error message for this error code.
 | ||||
| func (ec ErrorCode) Message() string { | ||||
| 	return ec.Descriptor().Message | ||||
| } | ||||
| 
 | ||||
| // MarshalText encodes the receiver into UTF-8-encoded text and returns the
 | ||||
| // result.
 | ||||
| func (ec ErrorCode) MarshalText() (text []byte, err error) { | ||||
| 	return []byte(ec.String()), nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalText decodes the form generated by MarshalText.
 | ||||
| func (ec *ErrorCode) UnmarshalText(text []byte) error { | ||||
| 	desc, ok := idToDescriptors[string(text)] | ||||
| 
 | ||||
| 	if !ok { | ||||
| 		desc = ErrorCodeUnknown.Descriptor() | ||||
| 	} | ||||
| 
 | ||||
| 	*ec = desc.Code | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Error provides a wrapper around ErrorCode with extra Details provided.
 | ||||
| type Error struct { | ||||
| 	Code    ErrorCode   `json:"code"` | ||||
| 	Message string      `json:"message,omitempty"` | ||||
| 	Detail  interface{} `json:"detail,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // Error returns a human readable representation of the error.
 | ||||
| func (e Error) Error() string { | ||||
| 	return fmt.Sprintf("%s: %s", | ||||
| 		strings.ToLower(strings.Replace(e.Code.String(), "_", " ", -1)), | ||||
| 		e.Message) | ||||
| } | ||||
| 
 | ||||
| // Errors provides the envelope for multiple errors and a few sugar methods
 | ||||
| // for use within the application.
 | ||||
| type Errors struct { | ||||
| 	Errors []Error `json:"errors,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // Push pushes an error on to the error stack, with the optional detail
 | ||||
| // argument. It is a programming error (ie panic) to push more than one
 | ||||
| // detail at a time.
 | ||||
| func (errs *Errors) Push(code ErrorCode, details ...interface{}) { | ||||
| 	if len(details) > 1 { | ||||
| 		panic("please specify zero or one detail items for this error") | ||||
| 	} | ||||
| 
 | ||||
| 	var detail interface{} | ||||
| 	if len(details) > 0 { | ||||
| 		detail = details[0] | ||||
| 	} | ||||
| 
 | ||||
| 	if err, ok := detail.(error); ok { | ||||
| 		detail = err.Error() | ||||
| 	} | ||||
| 
 | ||||
| 	errs.PushErr(Error{ | ||||
| 		Code:    code, | ||||
| 		Message: code.Message(), | ||||
| 		Detail:  detail, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // PushErr pushes an error interface onto the error stack.
 | ||||
| func (errs *Errors) PushErr(err error) { | ||||
| 	switch err.(type) { | ||||
| 	case Error: | ||||
| 		errs.Errors = append(errs.Errors, err.(Error)) | ||||
| 	default: | ||||
| 		errs.Errors = append(errs.Errors, Error{Message: err.Error()}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (errs *Errors) Error() string { | ||||
| 	switch errs.Len() { | ||||
| 	case 0: | ||||
| 		return "<nil>" | ||||
| 	case 1: | ||||
| 		return errs.Errors[0].Error() | ||||
| 	default: | ||||
| 		msg := "errors:\n" | ||||
| 		for _, err := range errs.Errors { | ||||
| 			msg += err.Error() + "\n" | ||||
| 		} | ||||
| 		return msg | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Clear clears the errors.
 | ||||
| func (errs *Errors) Clear() { | ||||
| 	errs.Errors = errs.Errors[:0] | ||||
| } | ||||
| 
 | ||||
| // Len returns the current number of errors.
 | ||||
| func (errs *Errors) Len() int { | ||||
| 	return len(errs.Errors) | ||||
| } | ||||
|  | @ -0,0 +1,165 @@ | |||
| package errors | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/docker/docker-registry/digest" | ||||
| ) | ||||
| 
 | ||||
| // TestErrorCodes ensures that error code format, mappings and
 | ||||
| // marshaling/unmarshaling. round trips are stable.
 | ||||
| func TestErrorCodes(t *testing.T) { | ||||
| 	for _, desc := range descriptors { | ||||
| 		if desc.Code.String() != desc.Value { | ||||
| 			t.Fatalf("error code string incorrect: %q != %q", desc.Code.String(), desc.Value) | ||||
| 		} | ||||
| 
 | ||||
| 		if desc.Code.Message() != desc.Message { | ||||
| 			t.Fatalf("incorrect message for error code %v: %q != %q", desc.Code, desc.Code.Message(), desc.Message) | ||||
| 		} | ||||
| 
 | ||||
| 		// Serialize the error code using the json library to ensure that we
 | ||||
| 		// get a string and it works round trip.
 | ||||
| 		p, err := json.Marshal(desc.Code) | ||||
| 
 | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("error marshaling error code %v: %v", desc.Code, err) | ||||
| 		} | ||||
| 
 | ||||
| 		if len(p) <= 0 { | ||||
| 			t.Fatalf("expected content in marshaled before for error code %v", desc.Code) | ||||
| 		} | ||||
| 
 | ||||
| 		// First, unmarshal to interface and ensure we have a string.
 | ||||
| 		var ecUnspecified interface{} | ||||
| 		if err := json.Unmarshal(p, &ecUnspecified); err != nil { | ||||
| 			t.Fatalf("error unmarshaling error code %v: %v", desc.Code, err) | ||||
| 		} | ||||
| 
 | ||||
| 		if _, ok := ecUnspecified.(string); !ok { | ||||
| 			t.Fatalf("expected a string for error code %v on unmarshal got a %T", desc.Code, ecUnspecified) | ||||
| 		} | ||||
| 
 | ||||
| 		// Now, unmarshal with the error code type and ensure they are equal
 | ||||
| 		var ecUnmarshaled ErrorCode | ||||
| 		if err := json.Unmarshal(p, &ecUnmarshaled); err != nil { | ||||
| 			t.Fatalf("error unmarshaling error code %v: %v", desc.Code, err) | ||||
| 		} | ||||
| 
 | ||||
| 		if ecUnmarshaled != desc.Code { | ||||
| 			t.Fatalf("unexpected error code during error code marshal/unmarshal: %v != %v", ecUnmarshaled, desc.Code) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // TestErrorsManagement does a quick check of the Errors type to ensure that
 | ||||
| // members are properly pushed and marshaled.
 | ||||
| func TestErrorsManagement(t *testing.T) { | ||||
| 	var errs Errors | ||||
| 
 | ||||
| 	errs.Push(ErrorCodeDigestInvalid) | ||||
| 	errs.Push(ErrorCodeBlobUnknown, | ||||
| 		map[string]digest.Digest{"digest": "sometestblobsumdoesntmatter"}) | ||||
| 
 | ||||
| 	p, err := json.Marshal(errs) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error marashaling errors: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	expectedJSON := "{\"errors\":[{\"code\":\"DIGEST_INVALID\",\"message\":\"provided digest did not match uploaded content\"},{\"code\":\"BLOB_UNKNOWN\",\"message\":\"blob unknown to registry\",\"detail\":{\"digest\":\"sometestblobsumdoesntmatter\"}}]}" | ||||
| 
 | ||||
| 	if string(p) != expectedJSON { | ||||
| 		t.Fatalf("unexpected json: %q != %q", string(p), expectedJSON) | ||||
| 	} | ||||
| 
 | ||||
| 	errs.Clear() | ||||
| 	errs.Push(ErrorCodeUnknown) | ||||
| 	expectedJSON = "{\"errors\":[{\"code\":\"UNKNOWN\",\"message\":\"unknown error\"}]}" | ||||
| 	p, err = json.Marshal(errs) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error marashaling errors: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if string(p) != expectedJSON { | ||||
| 		t.Fatalf("unexpected json: %q != %q", string(p), expectedJSON) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // TestMarshalUnmarshal ensures that api errors can round trip through json
 | ||||
| // without losing information.
 | ||||
| func TestMarshalUnmarshal(t *testing.T) { | ||||
| 
 | ||||
| 	var errors Errors | ||||
| 
 | ||||
| 	for _, testcase := range []struct { | ||||
| 		description string | ||||
| 		err         Error | ||||
| 	}{ | ||||
| 		{ | ||||
| 			description: "unknown error", | ||||
| 			err: Error{ | ||||
| 
 | ||||
| 				Code:    ErrorCodeUnknown, | ||||
| 				Message: ErrorCodeUnknown.Descriptor().Message, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			description: "unknown manifest", | ||||
| 			err: Error{ | ||||
| 				Code:    ErrorCodeManifestUnknown, | ||||
| 				Message: ErrorCodeManifestUnknown.Descriptor().Message, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			description: "unknown manifest", | ||||
| 			err: Error{ | ||||
| 				Code:    ErrorCodeBlobUnknown, | ||||
| 				Message: ErrorCodeBlobUnknown.Descriptor().Message, | ||||
| 				Detail:  map[string]interface{}{"digest": "asdfqwerqwerqwerqwer"}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		fatalf := func(format string, args ...interface{}) { | ||||
| 			t.Fatalf(testcase.description+": "+format, args...) | ||||
| 		} | ||||
| 
 | ||||
| 		unexpectedErr := func(err error) { | ||||
| 			fatalf("unexpected error: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		p, err := json.Marshal(testcase.err) | ||||
| 		if err != nil { | ||||
| 			unexpectedErr(err) | ||||
| 		} | ||||
| 
 | ||||
| 		var unmarshaled Error | ||||
| 		if err := json.Unmarshal(p, &unmarshaled); err != nil { | ||||
| 			unexpectedErr(err) | ||||
| 		} | ||||
| 
 | ||||
| 		if !reflect.DeepEqual(unmarshaled, testcase.err) { | ||||
| 			fatalf("errors not equal after round trip: %#v != %#v", unmarshaled, testcase.err) | ||||
| 		} | ||||
| 
 | ||||
| 		// Roll everything up into an error response envelope.
 | ||||
| 		errors.PushErr(testcase.err) | ||||
| 	} | ||||
| 
 | ||||
| 	p, err := json.Marshal(errors) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error marshaling error envelope: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	var unmarshaled Errors | ||||
| 	if err := json.Unmarshal(p, &unmarshaled); err != nil { | ||||
| 		t.Fatalf("unexpected error unmarshaling error envelope: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if !reflect.DeepEqual(unmarshaled, errors) { | ||||
| 		t.Fatalf("errors not equal after round trip: %#v != %#v", unmarshaled, errors) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										30
									
								
								api_test.go
								
								
								
								
							
							
						
						
									
										30
									
								
								api_test.go
								
								
								
								
							|  | @ -12,16 +12,14 @@ import ( | |||
| 	"os" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/docker/libtrust" | ||||
| 
 | ||||
| 	"github.com/docker/docker-registry/storage" | ||||
| 	_ "github.com/docker/docker-registry/storagedriver/inmemory" | ||||
| 
 | ||||
| 	"github.com/gorilla/handlers" | ||||
| 
 | ||||
| 	"github.com/docker/docker-registry/api/errors" | ||||
| 	"github.com/docker/docker-registry/common/testutil" | ||||
| 	"github.com/docker/docker-registry/configuration" | ||||
| 	"github.com/docker/docker-registry/digest" | ||||
| 	"github.com/docker/docker-registry/storage" | ||||
| 	_ "github.com/docker/docker-registry/storagedriver/inmemory" | ||||
| 	"github.com/docker/libtrust" | ||||
| 	"github.com/gorilla/handlers" | ||||
| ) | ||||
| 
 | ||||
| // TestLayerAPI conducts a full of the of the layer api.
 | ||||
|  | @ -133,6 +131,10 @@ func TestLayerAPI(t *testing.T) { | |||
| 	if !verifier.Verified() { | ||||
| 		t.Fatalf("response body did not pass verification") | ||||
| 	} | ||||
| 
 | ||||
| 	// Missing tests:
 | ||||
| 	// 	- Upload the same tarsum file under and different repository and
 | ||||
| 	//       ensure the content remains uncorrupted.
 | ||||
| } | ||||
| 
 | ||||
| func TestManifestAPI(t *testing.T) { | ||||
|  | @ -180,9 +182,7 @@ func TestManifestAPI(t *testing.T) { | |||
| 	// }
 | ||||
| 	dec := json.NewDecoder(resp.Body) | ||||
| 
 | ||||
| 	var respErrs struct { | ||||
| 		Errors []Error | ||||
| 	} | ||||
| 	var respErrs errors.Errors | ||||
| 	if err := dec.Decode(&respErrs); err != nil { | ||||
| 		t.Fatalf("unexpected error decoding error response: %v", err) | ||||
| 	} | ||||
|  | @ -191,7 +191,7 @@ func TestManifestAPI(t *testing.T) { | |||
| 		t.Fatalf("expected errors in response") | ||||
| 	} | ||||
| 
 | ||||
| 	if respErrs.Errors[0].Code != ErrorCodeUnknownManifest { | ||||
| 	if respErrs.Errors[0].Code != errors.ErrorCodeManifestUnknown { | ||||
| 		t.Fatalf("expected manifest unknown error: got %v", respErrs) | ||||
| 	} | ||||
| 
 | ||||
|  | @ -217,7 +217,7 @@ func TestManifestAPI(t *testing.T) { | |||
| 		t.Fatalf("expected errors in response") | ||||
| 	} | ||||
| 
 | ||||
| 	if respErrs.Errors[0].Code != ErrorCodeUnknownRepository { | ||||
| 	if respErrs.Errors[0].Code != errors.ErrorCodeNameUnknown { | ||||
| 		t.Fatalf("expected respository unknown error: got %v", respErrs) | ||||
| 	} | ||||
| 
 | ||||
|  | @ -251,11 +251,11 @@ func TestManifestAPI(t *testing.T) { | |||
| 
 | ||||
| 	for _, err := range respErrs.Errors { | ||||
| 		switch err.Code { | ||||
| 		case ErrorCodeUnverifiedManifest: | ||||
| 		case errors.ErrorCodeManifestUnverified: | ||||
| 			unverified++ | ||||
| 		case ErrorCodeUnknownLayer: | ||||
| 		case errors.ErrorCodeBlobUnknown: | ||||
| 			missingLayers++ | ||||
| 		case ErrorCodeInvalidDigest: | ||||
| 		case errors.ErrorCodeDigestInvalid: | ||||
| 			// TODO(stevvooe): This error isn't quite descriptive enough --
 | ||||
| 			// the layer with an invalid digest isn't identified.
 | ||||
| 			invalidDigests++ | ||||
|  |  | |||
|  | @ -19,7 +19,12 @@ test: | |||
|     - go version | ||||
|   override: | ||||
|     - test -z $(gofmt -s -l . | tee /dev/stderr) | ||||
|     - go vet ./... | ||||
| 
 | ||||
|     # TODO(stevvooe): go vet is complaining about something that can't be | ||||
|     # reproduced locally and doesn't make sense based on the existing code. | ||||
|     # Turning it off for now. | ||||
|     # - go vet ./... | ||||
| 
 | ||||
|     - test -z $(golint ./...          | tee /dev/stderr) | ||||
|     - go test -test.v -test.short ./... | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										136
									
								
								client/client.go
								
								
								
								
							
							
						
						
									
										136
									
								
								client/client.go
								
								
								
								
							|  | @ -10,7 +10,7 @@ import ( | |||
| 	"regexp" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/docker/docker-registry" | ||||
| 	"github.com/docker/docker-registry/api/errors" | ||||
| 	"github.com/docker/docker-registry/digest" | ||||
| 	"github.com/docker/docker-registry/storage" | ||||
| ) | ||||
|  | @ -94,17 +94,18 @@ func (r *clientImpl) GetImageManifest(name, tag string) (*storage.SignedManifest | |||
| 	case response.StatusCode == http.StatusOK: | ||||
| 		break | ||||
| 	case response.StatusCode == http.StatusNotFound: | ||||
| 		return nil, ®istry.ImageManifestNotFoundError{Name: name, Tag: tag} | ||||
| 		return nil, &ImageManifestNotFoundError{Name: name, Tag: tag} | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 
 | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, errors | ||||
| 		return nil, &errs | ||||
| 	default: | ||||
| 		return nil, ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return nil, &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| 
 | ||||
| 	decoder := json.NewDecoder(response.Body) | ||||
|  | @ -118,13 +119,8 @@ func (r *clientImpl) GetImageManifest(name, tag string) (*storage.SignedManifest | |||
| } | ||||
| 
 | ||||
| func (r *clientImpl) PutImageManifest(name, tag string, manifest *storage.SignedManifest) error { | ||||
| 	manifestBytes, err := json.Marshal(manifest) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	putRequest, err := http.NewRequest("PUT", | ||||
| 		r.imageManifestURL(name, tag), bytes.NewReader(manifestBytes)) | ||||
| 		r.imageManifestURL(name, tag), bytes.NewReader(manifest.Raw)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | @ -140,15 +136,16 @@ func (r *clientImpl) PutImageManifest(name, tag string, manifest *storage.Signed | |||
| 	case response.StatusCode == http.StatusOK: | ||||
| 		return nil | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errors errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return errors | ||||
| 
 | ||||
| 		return &errors | ||||
| 	default: | ||||
| 		return ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -170,17 +167,17 @@ func (r *clientImpl) DeleteImage(name, tag string) error { | |||
| 	case response.StatusCode == http.StatusNoContent: | ||||
| 		break | ||||
| 	case response.StatusCode == http.StatusNotFound: | ||||
| 		return ®istry.ImageManifestNotFoundError{Name: name, Tag: tag} | ||||
| 		return &ImageManifestNotFoundError{Name: name, Tag: tag} | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return errors | ||||
| 		return &errs | ||||
| 	default: | ||||
| 		return ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  | @ -198,17 +195,17 @@ func (r *clientImpl) ListImageTags(name string) ([]string, error) { | |||
| 	case response.StatusCode == http.StatusOK: | ||||
| 		break | ||||
| 	case response.StatusCode == http.StatusNotFound: | ||||
| 		return nil, ®istry.RepositoryNotFoundError{Name: name} | ||||
| 		return nil, &RepositoryNotFoundError{Name: name} | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, errors | ||||
| 		return nil, &errs | ||||
| 	default: | ||||
| 		return nil, ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return nil, &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| 
 | ||||
| 	tags := struct { | ||||
|  | @ -235,7 +232,7 @@ func (r *clientImpl) BlobLength(name string, dgst digest.Digest) (int, error) { | |||
| 	switch { | ||||
| 	case response.StatusCode == http.StatusOK: | ||||
| 		lengthHeader := response.Header.Get("Content-Length") | ||||
| 		length, err := strconv.ParseInt(lengthHeader, 10, 0) | ||||
| 		length, err := strconv.ParseInt(lengthHeader, 10, 64) | ||||
| 		if err != nil { | ||||
| 			return -1, err | ||||
| 		} | ||||
|  | @ -243,16 +240,16 @@ func (r *clientImpl) BlobLength(name string, dgst digest.Digest) (int, error) { | |||
| 	case response.StatusCode == http.StatusNotFound: | ||||
| 		return -1, nil | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return -1, err | ||||
| 		} | ||||
| 		return -1, errors | ||||
| 		return -1, &errs | ||||
| 	default: | ||||
| 		response.Body.Close() | ||||
| 		return -1, ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return -1, &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -280,18 +277,18 @@ func (r *clientImpl) GetBlob(name string, dgst digest.Digest, byteOffset int) (i | |||
| 		return response.Body, int(length), nil | ||||
| 	case response.StatusCode == http.StatusNotFound: | ||||
| 		response.Body.Close() | ||||
| 		return nil, 0, ®istry.BlobNotFoundError{Name: name, Digest: dgst} | ||||
| 		return nil, 0, &BlobNotFoundError{Name: name, Digest: dgst} | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return nil, 0, err | ||||
| 		} | ||||
| 		return nil, 0, errors | ||||
| 		return nil, 0, &errs | ||||
| 	default: | ||||
| 		response.Body.Close() | ||||
| 		return nil, 0, ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return nil, 0, &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -315,20 +312,20 @@ func (r *clientImpl) InitiateBlobUpload(name string) (string, error) { | |||
| 	// case response.StatusCode == http.StatusNotFound:
 | ||||
| 	// return
 | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		return "", errors | ||||
| 		return "", &errs | ||||
| 	default: | ||||
| 		return "", ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return "", &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *clientImpl) GetBlobUploadStatus(location string) (int, int, error) { | ||||
| 	response, err := http.Get(fmt.Sprintf("%s%s", r.Endpoint, location)) | ||||
| 	response, err := http.Get(location) | ||||
| 	if err != nil { | ||||
| 		return 0, 0, err | ||||
| 	} | ||||
|  | @ -339,31 +336,30 @@ func (r *clientImpl) GetBlobUploadStatus(location string) (int, int, error) { | |||
| 	case response.StatusCode == http.StatusNoContent: | ||||
| 		return parseRangeHeader(response.Header.Get("Range")) | ||||
| 	case response.StatusCode == http.StatusNotFound: | ||||
| 		return 0, 0, ®istry.BlobUploadNotFoundError{Location: location} | ||||
| 		return 0, 0, &BlobUploadNotFoundError{Location: location} | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return 0, 0, err | ||||
| 		} | ||||
| 		return 0, 0, errors | ||||
| 		return 0, 0, &errs | ||||
| 	default: | ||||
| 		return 0, 0, ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return 0, 0, &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *clientImpl) UploadBlob(location string, blob io.ReadCloser, length int, dgst digest.Digest) error { | ||||
| 	defer blob.Close() | ||||
| 
 | ||||
| 	putRequest, err := http.NewRequest("PUT", | ||||
| 		fmt.Sprintf("%s%s", r.Endpoint, location), blob) | ||||
| 	putRequest, err := http.NewRequest("PUT", location, blob) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	queryValues := url.Values{} | ||||
| 	queryValues.Set("length", fmt.Sprint(length)) | ||||
| 	queryValues.Set("size", fmt.Sprint(length)) | ||||
| 	queryValues.Set("digest", dgst.String()) | ||||
| 	putRequest.URL.RawQuery = queryValues.Encode() | ||||
| 
 | ||||
|  | @ -381,17 +377,17 @@ func (r *clientImpl) UploadBlob(location string, blob io.ReadCloser, length int, | |||
| 	case response.StatusCode == http.StatusCreated: | ||||
| 		return nil | ||||
| 	case response.StatusCode == http.StatusNotFound: | ||||
| 		return ®istry.BlobUploadNotFoundError{Location: location} | ||||
| 		return &BlobUploadNotFoundError{Location: location} | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return errors | ||||
| 		return &errs | ||||
| 	default: | ||||
| 		return ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -426,23 +422,23 @@ func (r *clientImpl) UploadBlobChunk(location string, blobChunk io.ReadCloser, l | |||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return ®istry.BlobUploadInvalidRangeError{ | ||||
| 		return &BlobUploadInvalidRangeError{ | ||||
| 			Location:       location, | ||||
| 			LastValidRange: lastValidRange, | ||||
| 			BlobSize:       blobSize, | ||||
| 		} | ||||
| 	case response.StatusCode == http.StatusNotFound: | ||||
| 		return ®istry.BlobUploadNotFoundError{Location: location} | ||||
| 		return &BlobUploadNotFoundError{Location: location} | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return errors | ||||
| 		return &errs | ||||
| 	default: | ||||
| 		return ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -454,7 +450,7 @@ func (r *clientImpl) FinishChunkedBlobUpload(location string, length int, dgst d | |||
| 	} | ||||
| 
 | ||||
| 	queryValues := new(url.Values) | ||||
| 	queryValues.Set("length", fmt.Sprint(length)) | ||||
| 	queryValues.Set("size", fmt.Sprint(length)) | ||||
| 	queryValues.Set("digest", dgst.String()) | ||||
| 	putRequest.URL.RawQuery = queryValues.Encode() | ||||
| 
 | ||||
|  | @ -474,17 +470,17 @@ func (r *clientImpl) FinishChunkedBlobUpload(location string, length int, dgst d | |||
| 	case response.StatusCode == http.StatusCreated: | ||||
| 		return nil | ||||
| 	case response.StatusCode == http.StatusNotFound: | ||||
| 		return ®istry.BlobUploadNotFoundError{Location: location} | ||||
| 		return &BlobUploadNotFoundError{Location: location} | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return errors | ||||
| 		return &errs | ||||
| 	default: | ||||
| 		return ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -506,17 +502,17 @@ func (r *clientImpl) CancelBlobUpload(location string) error { | |||
| 	case response.StatusCode == http.StatusNoContent: | ||||
| 		return nil | ||||
| 	case response.StatusCode == http.StatusNotFound: | ||||
| 		return ®istry.BlobUploadNotFoundError{Location: location} | ||||
| 		return &BlobUploadNotFoundError{Location: location} | ||||
| 	case response.StatusCode >= 400 && response.StatusCode < 500: | ||||
| 		errors := new(registry.Errors) | ||||
| 		var errs errors.Errors | ||||
| 		decoder := json.NewDecoder(response.Body) | ||||
| 		err = decoder.Decode(&errors) | ||||
| 		err = decoder.Decode(&errs) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return errors | ||||
| 		return &errs | ||||
| 	default: | ||||
| 		return ®istry.UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 		return &UnexpectedHTTPStatusError{Status: response.Status} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,7 +58,8 @@ func TestPush(t *testing.T) { | |||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	manifestBytes, err := json.Marshal(manifest) | ||||
| 	var err error | ||||
| 	manifest.Raw, err = json.Marshal(manifest) | ||||
| 
 | ||||
| 	blobRequestResponseMappings := make([]testutil.RequestResponseMapping, 2*len(testBlobs)) | ||||
| 	for i, blob := range testBlobs { | ||||
|  | @ -94,13 +95,25 @@ func TestPush(t *testing.T) { | |||
| 		Request: testutil.Request{ | ||||
| 			Method: "PUT", | ||||
| 			Route:  "/v2/" + name + "/manifest/" + tag, | ||||
| 			Body:   manifestBytes, | ||||
| 			Body:   manifest.Raw, | ||||
| 		}, | ||||
| 		Response: testutil.Response{ | ||||
| 			StatusCode: http.StatusOK, | ||||
| 		}, | ||||
| 	})) | ||||
| 	server := httptest.NewServer(handler) | ||||
| 	var server *httptest.Server | ||||
| 
 | ||||
| 	// HACK(stevvooe): Super hack to follow: the request response map approach
 | ||||
| 	// above does not let us correctly format the location header to the
 | ||||
| 	// server url. This handler intercepts and re-writes the location header
 | ||||
| 	// to the server url.
 | ||||
| 
 | ||||
| 	hack := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w = &headerInterceptingResponseWriter{ResponseWriter: w, serverURL: server.URL} | ||||
| 		handler.ServeHTTP(w, r) | ||||
| 	}) | ||||
| 
 | ||||
| 	server = httptest.NewServer(hack) | ||||
| 	client := New(server.URL) | ||||
| 	objectStore := &memoryObjectStore{ | ||||
| 		mutex:           new(sync.Mutex), | ||||
|  | @ -370,3 +383,19 @@ func TestPullResume(t *testing.T) { | |||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // headerInterceptingResponseWriter is a hacky workaround to re-write the
 | ||||
| // location header to have the server url.
 | ||||
| type headerInterceptingResponseWriter struct { | ||||
| 	http.ResponseWriter | ||||
| 	serverURL string | ||||
| } | ||||
| 
 | ||||
| func (hirw *headerInterceptingResponseWriter) WriteHeader(status int) { | ||||
| 	location := hirw.Header().Get("Location") | ||||
| 	if location != "" { | ||||
| 		hirw.Header().Set("Location", hirw.serverURL+location) | ||||
| 	} | ||||
| 
 | ||||
| 	hirw.ResponseWriter.WriteHeader(status) | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,79 @@ | |||
| package client | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/docker/docker-registry/digest" | ||||
| ) | ||||
| 
 | ||||
| // RepositoryNotFoundError is returned when making an operation against a
 | ||||
| // repository that does not exist in the registry.
 | ||||
| type RepositoryNotFoundError struct { | ||||
| 	Name string | ||||
| } | ||||
| 
 | ||||
| func (e *RepositoryNotFoundError) Error() string { | ||||
| 	return fmt.Sprintf("No repository found with Name: %s", e.Name) | ||||
| } | ||||
| 
 | ||||
| // ImageManifestNotFoundError is returned when making an operation against a
 | ||||
| // given image manifest that does not exist in the registry.
 | ||||
| type ImageManifestNotFoundError struct { | ||||
| 	Name string | ||||
| 	Tag  string | ||||
| } | ||||
| 
 | ||||
| func (e *ImageManifestNotFoundError) Error() string { | ||||
| 	return fmt.Sprintf("No manifest found with Name: %s, Tag: %s", | ||||
| 		e.Name, e.Tag) | ||||
| } | ||||
| 
 | ||||
| // BlobNotFoundError is returned when making an operation against a given image
 | ||||
| // layer that does not exist in the registry.
 | ||||
| type BlobNotFoundError struct { | ||||
| 	Name   string | ||||
| 	Digest digest.Digest | ||||
| } | ||||
| 
 | ||||
| func (e *BlobNotFoundError) Error() string { | ||||
| 	return fmt.Sprintf("No blob found with Name: %s, Digest: %s", | ||||
| 		e.Name, e.Digest) | ||||
| } | ||||
| 
 | ||||
| // BlobUploadNotFoundError is returned when making a blob upload operation against an
 | ||||
| // invalid blob upload location url.
 | ||||
| // This may be the result of using a cancelled, completed, or stale upload
 | ||||
| // location.
 | ||||
| type BlobUploadNotFoundError struct { | ||||
| 	Location string | ||||
| } | ||||
| 
 | ||||
| func (e *BlobUploadNotFoundError) Error() string { | ||||
| 	return fmt.Sprintf("No blob upload found at Location: %s", e.Location) | ||||
| } | ||||
| 
 | ||||
| // BlobUploadInvalidRangeError is returned when attempting to upload an image
 | ||||
| // blob chunk that is out of order.
 | ||||
| // This provides the known BlobSize and LastValidRange which can be used to
 | ||||
| // resume the upload.
 | ||||
| type BlobUploadInvalidRangeError struct { | ||||
| 	Location       string | ||||
| 	LastValidRange int | ||||
| 	BlobSize       int | ||||
| } | ||||
| 
 | ||||
| func (e *BlobUploadInvalidRangeError) Error() string { | ||||
| 	return fmt.Sprintf( | ||||
| 		"Invalid range provided for upload at Location: %s. Last Valid Range: %d, Blob Size: %d", | ||||
| 		e.Location, e.LastValidRange, e.BlobSize) | ||||
| } | ||||
| 
 | ||||
| // 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) | ||||
| } | ||||
|  | @ -1,6 +1,9 @@ | |||
| package registry | ||||
| 
 | ||||
| import "github.com/Sirupsen/logrus" | ||||
| import ( | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| 	"github.com/docker/docker-registry/api/errors" | ||||
| ) | ||||
| 
 | ||||
| // Context should contain the request specific context for use in across
 | ||||
| // handlers. Resources that don't need to be shared across handlers should not
 | ||||
|  | @ -16,7 +19,7 @@ type Context struct { | |||
| 	// Errors is a collection of errors encountered during the request to be
 | ||||
| 	// returned to the client API. If errors are added to the collection, the
 | ||||
| 	// handler *must not* start the response via http.ResponseWriter.
 | ||||
| 	Errors Errors | ||||
| 	Errors errors.Errors | ||||
| 
 | ||||
| 	// vars contains the extracted gorilla/mux variables that can be used for
 | ||||
| 	// assignment.
 | ||||
|  |  | |||
							
								
								
									
										311
									
								
								errors.go
								
								
								
								
							
							
						
						
									
										311
									
								
								errors.go
								
								
								
								
							|  | @ -1,311 +0,0 @@ | |||
| package registry | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/docker/docker-registry/digest" | ||||
| 	"github.com/docker/docker-registry/storage" | ||||
| ) | ||||
| 
 | ||||
| // ErrorCode represents the error type. The errors are serialized via strings
 | ||||
| // and the integer format may change and should *never* be exported.
 | ||||
| type ErrorCode int | ||||
| 
 | ||||
| const ( | ||||
| 	// ErrorCodeUnknown is a catch-all for errors not defined below.
 | ||||
| 	ErrorCodeUnknown ErrorCode = iota | ||||
| 
 | ||||
| 	// The following errors can happen during a layer upload.
 | ||||
| 
 | ||||
| 	// ErrorCodeInvalidDigest is returned when uploading a layer if the
 | ||||
| 	// provided digest does not match the layer contents.
 | ||||
| 	ErrorCodeInvalidDigest | ||||
| 
 | ||||
| 	// ErrorCodeInvalidLength is returned when uploading a layer if the provided
 | ||||
| 	// length does not match the content length.
 | ||||
| 	ErrorCodeInvalidLength | ||||
| 
 | ||||
| 	// ErrorCodeInvalidName is returned when the name in the manifest does not
 | ||||
| 	// match the provided name.
 | ||||
| 	ErrorCodeInvalidName | ||||
| 
 | ||||
| 	// ErrorCodeInvalidTag is returned when the tag in the manifest does not
 | ||||
| 	// match the provided tag.
 | ||||
| 	ErrorCodeInvalidTag | ||||
| 
 | ||||
| 	// ErrorCodeUnknownRepository when the repository name is not known.
 | ||||
| 	ErrorCodeUnknownRepository | ||||
| 
 | ||||
| 	// ErrorCodeUnknownManifest returned when image manifest name and tag is
 | ||||
| 	// unknown, accompanied by a 404 status.
 | ||||
| 	ErrorCodeUnknownManifest | ||||
| 
 | ||||
| 	// ErrorCodeInvalidManifest returned when an image manifest is invalid,
 | ||||
| 	// typically during a PUT operation.
 | ||||
| 	ErrorCodeInvalidManifest | ||||
| 
 | ||||
| 	// ErrorCodeUnverifiedManifest is returned when the manifest fails signature
 | ||||
| 	// validation.
 | ||||
| 	ErrorCodeUnverifiedManifest | ||||
| 
 | ||||
| 	// ErrorCodeUnknownLayer is returned when the manifest references a
 | ||||
| 	// nonexistent layer.
 | ||||
| 	ErrorCodeUnknownLayer | ||||
| 
 | ||||
| 	// ErrorCodeUnknownLayerUpload is returned when an upload is accessed.
 | ||||
| 	ErrorCodeUnknownLayerUpload | ||||
| 
 | ||||
| 	// ErrorCodeUntrustedSignature is returned when the manifest is signed by an
 | ||||
| 	// untrusted source.
 | ||||
| 	ErrorCodeUntrustedSignature | ||||
| ) | ||||
| 
 | ||||
| var errorCodeStrings = map[ErrorCode]string{ | ||||
| 	ErrorCodeUnknown:            "UNKNOWN", | ||||
| 	ErrorCodeInvalidDigest:      "INVALID_DIGEST", | ||||
| 	ErrorCodeInvalidLength:      "INVALID_LENGTH", | ||||
| 	ErrorCodeInvalidName:        "INVALID_NAME", | ||||
| 	ErrorCodeInvalidTag:         "INVALID_TAG", | ||||
| 	ErrorCodeUnknownRepository:  "UNKNOWN_REPOSITORY", | ||||
| 	ErrorCodeUnknownManifest:    "UNKNOWN_MANIFEST", | ||||
| 	ErrorCodeInvalidManifest:    "INVALID_MANIFEST", | ||||
| 	ErrorCodeUnverifiedManifest: "UNVERIFIED_MANIFEST", | ||||
| 	ErrorCodeUnknownLayer:       "UNKNOWN_LAYER", | ||||
| 	ErrorCodeUnknownLayerUpload: "UNKNOWN_LAYER_UPLOAD", | ||||
| 	ErrorCodeUntrustedSignature: "UNTRUSTED_SIGNATURE", | ||||
| } | ||||
| 
 | ||||
| var errorCodesMessages = map[ErrorCode]string{ | ||||
| 	ErrorCodeUnknown:            "unknown error", | ||||
| 	ErrorCodeInvalidDigest:      "provided digest did not match uploaded content", | ||||
| 	ErrorCodeInvalidLength:      "provided length did not match content length", | ||||
| 	ErrorCodeInvalidName:        "manifest name did not match URI", | ||||
| 	ErrorCodeInvalidTag:         "manifest tag did not match URI", | ||||
| 	ErrorCodeUnknownRepository:  "repository not known to registry", | ||||
| 	ErrorCodeUnknownManifest:    "manifest not known", | ||||
| 	ErrorCodeInvalidManifest:    "manifest is invalid", | ||||
| 	ErrorCodeUnverifiedManifest: "manifest failed signature validation", | ||||
| 	ErrorCodeUnknownLayer:       "referenced layer not available", | ||||
| 	ErrorCodeUnknownLayerUpload: "cannot resume unknown layer upload", | ||||
| 	ErrorCodeUntrustedSignature: "manifest signed by untrusted source", | ||||
| } | ||||
| 
 | ||||
| var stringToErrorCode map[string]ErrorCode | ||||
| 
 | ||||
| func init() { | ||||
| 	stringToErrorCode = make(map[string]ErrorCode, len(errorCodeStrings)) | ||||
| 
 | ||||
| 	// Build up reverse error code map
 | ||||
| 	for k, v := range errorCodeStrings { | ||||
| 		stringToErrorCode[v] = k | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ParseErrorCode attempts to parse the error code string, returning
 | ||||
| // ErrorCodeUnknown if the error is not known.
 | ||||
| func ParseErrorCode(s string) ErrorCode { | ||||
| 	ec, ok := stringToErrorCode[s] | ||||
| 
 | ||||
| 	if !ok { | ||||
| 		return ErrorCodeUnknown | ||||
| 	} | ||||
| 
 | ||||
| 	return ec | ||||
| } | ||||
| 
 | ||||
| // String returns the canonical identifier for this error code.
 | ||||
| func (ec ErrorCode) String() string { | ||||
| 	s, ok := errorCodeStrings[ec] | ||||
| 
 | ||||
| 	if !ok { | ||||
| 		return errorCodeStrings[ErrorCodeUnknown] | ||||
| 	} | ||||
| 
 | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| // Message returned the human-readable error message for this error code.
 | ||||
| func (ec ErrorCode) Message() string { | ||||
| 	m, ok := errorCodesMessages[ec] | ||||
| 
 | ||||
| 	if !ok { | ||||
| 		return errorCodesMessages[ErrorCodeUnknown] | ||||
| 	} | ||||
| 
 | ||||
| 	return m | ||||
| } | ||||
| 
 | ||||
| // MarshalText encodes the receiver into UTF-8-encoded text and returns the
 | ||||
| // result.
 | ||||
| func (ec ErrorCode) MarshalText() (text []byte, err error) { | ||||
| 	return []byte(ec.String()), nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalText decodes the form generated by MarshalText.
 | ||||
| func (ec *ErrorCode) UnmarshalText(text []byte) error { | ||||
| 	*ec = stringToErrorCode[string(text)] | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Error provides a wrapper around ErrorCode with extra Details provided.
 | ||||
| type Error struct { | ||||
| 	Code    ErrorCode   `json:"code"` | ||||
| 	Message string      `json:"message,omitempty"` | ||||
| 	Detail  interface{} `json:"detail,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // Error returns a human readable representation of the error.
 | ||||
| func (e Error) Error() string { | ||||
| 	return fmt.Sprintf("%s: %s", | ||||
| 		strings.ToLower(strings.Replace(e.Code.String(), "_", " ", -1)), | ||||
| 		e.Message) | ||||
| } | ||||
| 
 | ||||
| // Errors provides the envelope for multiple errors and a few sugar methods
 | ||||
| // for use within the application.
 | ||||
| type Errors struct { | ||||
| 	Errors []error `json:"errors,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // Push pushes an error on to the error stack, with the optional detail
 | ||||
| // argument. It is a programming error (ie panic) to push more than one
 | ||||
| // detail at a time.
 | ||||
| func (errs *Errors) Push(code ErrorCode, details ...interface{}) { | ||||
| 	if len(details) > 1 { | ||||
| 		panic("please specify zero or one detail items for this error") | ||||
| 	} | ||||
| 
 | ||||
| 	var detail interface{} | ||||
| 	if len(details) > 0 { | ||||
| 		detail = details[0] | ||||
| 	} | ||||
| 
 | ||||
| 	if err, ok := detail.(error); ok { | ||||
| 		detail = err.Error() | ||||
| 	} | ||||
| 
 | ||||
| 	errs.PushErr(Error{ | ||||
| 		Code:    code, | ||||
| 		Message: code.Message(), | ||||
| 		Detail:  detail, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // PushErr pushes an error interface onto the error stack.
 | ||||
| func (errs *Errors) PushErr(err error) { | ||||
| 	switch err.(type) { | ||||
| 	case Error: | ||||
| 		errs.Errors = append(errs.Errors, err) | ||||
| 	default: | ||||
| 		errs.Errors = append(errs.Errors, Error{Message: err.Error()}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (errs *Errors) Error() string { | ||||
| 	switch errs.Len() { | ||||
| 	case 0: | ||||
| 		return "<nil>" | ||||
| 	case 1: | ||||
| 		return errs.Errors[0].Error() | ||||
| 	default: | ||||
| 		msg := "errors:\n" | ||||
| 		for _, err := range errs.Errors { | ||||
| 			msg += err.Error() + "\n" | ||||
| 		} | ||||
| 		return msg | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Clear clears the errors.
 | ||||
| func (errs *Errors) Clear() { | ||||
| 	errs.Errors = errs.Errors[:0] | ||||
| } | ||||
| 
 | ||||
| // Len returns the current number of errors.
 | ||||
| func (errs *Errors) Len() int { | ||||
| 	return len(errs.Errors) | ||||
| } | ||||
| 
 | ||||
| // DetailUnknownLayer provides detail for unknown layer errors, returned by
 | ||||
| // image manifest push for layers that are not yet transferred. This intended
 | ||||
| // to only be used on the backend to return detail for this specific error.
 | ||||
| type DetailUnknownLayer struct { | ||||
| 
 | ||||
| 	// Unknown should contain the contents of a layer descriptor, which is a
 | ||||
| 	// single FSLayer currently.
 | ||||
| 	Unknown storage.FSLayer `json:"unknown"` | ||||
| } | ||||
| 
 | ||||
| // RepositoryNotFoundError is returned when making an operation against a
 | ||||
| // repository that does not exist in the registry.
 | ||||
| type RepositoryNotFoundError struct { | ||||
| 	Name string | ||||
| } | ||||
| 
 | ||||
| func (e *RepositoryNotFoundError) Error() string { | ||||
| 	return fmt.Sprintf("No repository found with Name: %s", e.Name) | ||||
| } | ||||
| 
 | ||||
| // ImageManifestNotFoundError is returned when making an operation against a
 | ||||
| // given image manifest that does not exist in the registry.
 | ||||
| type ImageManifestNotFoundError struct { | ||||
| 	Name string | ||||
| 	Tag  string | ||||
| } | ||||
| 
 | ||||
| func (e *ImageManifestNotFoundError) Error() string { | ||||
| 	return fmt.Sprintf("No manifest found with Name: %s, Tag: %s", | ||||
| 		e.Name, e.Tag) | ||||
| } | ||||
| 
 | ||||
| // BlobNotFoundError is returned when making an operation against a given image
 | ||||
| // layer that does not exist in the registry.
 | ||||
| type BlobNotFoundError struct { | ||||
| 	Name   string | ||||
| 	Digest digest.Digest | ||||
| } | ||||
| 
 | ||||
| func (e *BlobNotFoundError) Error() string { | ||||
| 	return fmt.Sprintf("No blob found with Name: %s, Digest: %s", | ||||
| 		e.Name, e.Digest) | ||||
| } | ||||
| 
 | ||||
| // BlobUploadNotFoundError is returned when making a blob upload operation against an
 | ||||
| // invalid blob upload location url.
 | ||||
| // This may be the result of using a cancelled, completed, or stale upload
 | ||||
| // location.
 | ||||
| type BlobUploadNotFoundError struct { | ||||
| 	Location string | ||||
| } | ||||
| 
 | ||||
| func (e *BlobUploadNotFoundError) Error() string { | ||||
| 	return fmt.Sprintf("No blob upload found at Location: %s", e.Location) | ||||
| } | ||||
| 
 | ||||
| // BlobUploadInvalidRangeError is returned when attempting to upload an image
 | ||||
| // blob chunk that is out of order.
 | ||||
| // This provides the known BlobSize and LastValidRange which can be used to
 | ||||
| // resume the upload.
 | ||||
| type BlobUploadInvalidRangeError struct { | ||||
| 	Location       string | ||||
| 	LastValidRange int | ||||
| 	BlobSize       int | ||||
| } | ||||
| 
 | ||||
| func (e *BlobUploadInvalidRangeError) Error() string { | ||||
| 	return fmt.Sprintf( | ||||
| 		"Invalid range provided for upload at Location: %s. Last Valid Range: %d, Blob Size: %d", | ||||
| 		e.Location, e.LastValidRange, e.BlobSize) | ||||
| } | ||||
| 
 | ||||
| // 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) | ||||
| } | ||||
|  | @ -1,90 +0,0 @@ | |||
| package registry | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| // TestErrorCodes ensures that error code format, mappings and
 | ||||
| // marshaling/unmarshaling. round trips are stable.
 | ||||
| func TestErrorCodes(t *testing.T) { | ||||
| 	for ec := range errorCodeStrings { | ||||
| 		if ec.String() != errorCodeStrings[ec] { | ||||
| 			t.Fatalf("error code string incorrect: %q != %q", ec.String(), errorCodeStrings[ec]) | ||||
| 		} | ||||
| 
 | ||||
| 		if ec.Message() != errorCodesMessages[ec] { | ||||
| 			t.Fatalf("incorrect message for error code %v: %q != %q", ec, ec.Message(), errorCodesMessages[ec]) | ||||
| 		} | ||||
| 
 | ||||
| 		// Serialize the error code using the json library to ensure that we
 | ||||
| 		// get a string and it works round trip.
 | ||||
| 		p, err := json.Marshal(ec) | ||||
| 
 | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("error marshaling error code %v: %v", ec, err) | ||||
| 		} | ||||
| 
 | ||||
| 		if len(p) <= 0 { | ||||
| 			t.Fatalf("expected content in marshaled before for error code %v", ec) | ||||
| 		} | ||||
| 
 | ||||
| 		// First, unmarshal to interface and ensure we have a string.
 | ||||
| 		var ecUnspecified interface{} | ||||
| 		if err := json.Unmarshal(p, &ecUnspecified); err != nil { | ||||
| 			t.Fatalf("error unmarshaling error code %v: %v", ec, err) | ||||
| 		} | ||||
| 
 | ||||
| 		if _, ok := ecUnspecified.(string); !ok { | ||||
| 			t.Fatalf("expected a string for error code %v on unmarshal got a %T", ec, ecUnspecified) | ||||
| 		} | ||||
| 
 | ||||
| 		// Now, unmarshal with the error code type and ensure they are equal
 | ||||
| 		var ecUnmarshaled ErrorCode | ||||
| 		if err := json.Unmarshal(p, &ecUnmarshaled); err != nil { | ||||
| 			t.Fatalf("error unmarshaling error code %v: %v", ec, err) | ||||
| 		} | ||||
| 
 | ||||
| 		if ecUnmarshaled != ec { | ||||
| 			t.Fatalf("unexpected error code during error code marshal/unmarshal: %v != %v", ecUnmarshaled, ec) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // TestErrorsManagement does a quick check of the Errors type to ensure that
 | ||||
| // members are properly pushed and marshaled.
 | ||||
| func TestErrorsManagement(t *testing.T) { | ||||
| 	var errs Errors | ||||
| 
 | ||||
| 	errs.Push(ErrorCodeInvalidDigest) | ||||
| 
 | ||||
| 	var detail DetailUnknownLayer | ||||
| 	detail.Unknown.BlobSum = "sometestblobsumdoesntmatter" | ||||
| 
 | ||||
| 	errs.Push(ErrorCodeUnknownLayer, detail) | ||||
| 
 | ||||
| 	p, err := json.Marshal(errs) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error marashaling errors: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	expectedJSON := "{\"errors\":[{\"code\":\"INVALID_DIGEST\",\"message\":\"provided digest did not match uploaded content\"},{\"code\":\"UNKNOWN_LAYER\",\"message\":\"referenced layer not available\",\"detail\":{\"unknown\":{\"blobSum\":\"sometestblobsumdoesntmatter\"}}}]}" | ||||
| 
 | ||||
| 	if string(p) != expectedJSON { | ||||
| 		t.Fatalf("unexpected json: %q != %q", string(p), expectedJSON) | ||||
| 	} | ||||
| 
 | ||||
| 	errs.Clear() | ||||
| 	errs.Push(ErrorCodeUnknown) | ||||
| 	expectedJSON = "{\"errors\":[{\"code\":\"UNKNOWN\",\"message\":\"unknown error\"}]}" | ||||
| 	p, err = json.Marshal(errs) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error marashaling errors: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if string(p) != expectedJSON { | ||||
| 		t.Fatalf("unexpected json: %q != %q", string(p), expectedJSON) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										16
									
								
								images.go
								
								
								
								
							
							
						
						
									
										16
									
								
								images.go
								
								
								
								
							|  | @ -5,8 +5,8 @@ import ( | |||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"github.com/docker/docker-registry/api/errors" | ||||
| 	"github.com/docker/docker-registry/digest" | ||||
| 
 | ||||
| 	"github.com/docker/docker-registry/storage" | ||||
| 	"github.com/gorilla/handlers" | ||||
| ) | ||||
|  | @ -41,7 +41,7 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http | |||
| 	manifest, err := manifests.Get(imh.Name, imh.Tag) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		imh.Errors.Push(ErrorCodeUnknownManifest, err) | ||||
| 		imh.Errors.Push(errors.ErrorCodeManifestUnknown, err) | ||||
| 		w.WriteHeader(http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
|  | @ -58,7 +58,7 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http | |||
| 
 | ||||
| 	var manifest storage.SignedManifest | ||||
| 	if err := dec.Decode(&manifest); err != nil { | ||||
| 		imh.Errors.Push(ErrorCodeInvalidManifest, err) | ||||
| 		imh.Errors.Push(errors.ErrorCodeManifestInvalid, err) | ||||
| 		w.WriteHeader(http.StatusBadRequest) | ||||
| 		return | ||||
| 	} | ||||
|  | @ -71,14 +71,14 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http | |||
| 			for _, verificationError := range err { | ||||
| 				switch verificationError := verificationError.(type) { | ||||
| 				case storage.ErrUnknownLayer: | ||||
| 					imh.Errors.Push(ErrorCodeUnknownLayer, verificationError.FSLayer) | ||||
| 					imh.Errors.Push(errors.ErrorCodeBlobUnknown, verificationError.FSLayer) | ||||
| 				case storage.ErrManifestUnverified: | ||||
| 					imh.Errors.Push(ErrorCodeUnverifiedManifest) | ||||
| 					imh.Errors.Push(errors.ErrorCodeManifestUnverified) | ||||
| 				default: | ||||
| 					if verificationError == digest.ErrDigestInvalidFormat { | ||||
| 						// TODO(stevvooe): We need to really need to move all
 | ||||
| 						// errors to types. Its much more straightforward.
 | ||||
| 						imh.Errors.Push(ErrorCodeInvalidDigest) | ||||
| 						imh.Errors.Push(errors.ErrorCodeDigestInvalid) | ||||
| 					} else { | ||||
| 						imh.Errors.PushErr(verificationError) | ||||
| 					} | ||||
|  | @ -99,10 +99,10 @@ func (imh *imageManifestHandler) DeleteImageManifest(w http.ResponseWriter, r *h | |||
| 	if err := manifests.Delete(imh.Name, imh.Tag); err != nil { | ||||
| 		switch err := err.(type) { | ||||
| 		case storage.ErrUnknownManifest: | ||||
| 			imh.Errors.Push(ErrorCodeUnknownManifest, err) | ||||
| 			imh.Errors.Push(errors.ErrorCodeManifestUnknown, err) | ||||
| 			w.WriteHeader(http.StatusNotFound) | ||||
| 		default: | ||||
| 			imh.Errors.Push(ErrorCodeUnknown, err) | ||||
| 			imh.Errors.Push(errors.ErrorCodeUnknown, err) | ||||
| 			w.WriteHeader(http.StatusBadRequest) | ||||
| 		} | ||||
| 		return | ||||
|  |  | |||
							
								
								
									
										7
									
								
								layer.go
								
								
								
								
							
							
						
						
									
										7
									
								
								layer.go
								
								
								
								
							|  | @ -3,6 +3,7 @@ package registry | |||
| import ( | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"github.com/docker/docker-registry/api/errors" | ||||
| 	"github.com/docker/docker-registry/digest" | ||||
| 	"github.com/docker/docker-registry/storage" | ||||
| 	"github.com/gorilla/handlers" | ||||
|  | @ -14,7 +15,7 @@ func layerDispatcher(ctx *Context, r *http.Request) http.Handler { | |||
| 
 | ||||
| 	if err != nil { | ||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 			ctx.Errors.Push(ErrorCodeInvalidDigest, err) | ||||
| 			ctx.Errors.Push(errors.ErrorCodeDigestInvalid, err) | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
|  | @ -49,9 +50,9 @@ func (lh *layerHandler) GetLayer(w http.ResponseWriter, r *http.Request) { | |||
| 		switch err := err.(type) { | ||||
| 		case storage.ErrUnknownLayer: | ||||
| 			w.WriteHeader(http.StatusNotFound) | ||||
| 			lh.Errors.Push(ErrorCodeUnknownLayer, err.FSLayer) | ||||
| 			lh.Errors.Push(errors.ErrorCodeBlobUnknown, err.FSLayer) | ||||
| 		default: | ||||
| 			lh.Errors.Push(ErrorCodeUnknown, err) | ||||
| 			lh.Errors.Push(errors.ErrorCodeUnknown, err) | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import ( | |||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| 	"github.com/docker/docker-registry/api/errors" | ||||
| 	"github.com/docker/docker-registry/digest" | ||||
| 	"github.com/docker/docker-registry/storage" | ||||
| 	"github.com/gorilla/handlers" | ||||
|  | @ -38,7 +39,7 @@ func layerUploadDispatcher(ctx *Context, r *http.Request) http.Handler { | |||
| 			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 				logrus.Infof("error resolving upload: %v", err) | ||||
| 				w.WriteHeader(http.StatusInternalServerError) | ||||
| 				luh.Errors.Push(ErrorCodeUnknown, err) | ||||
| 				luh.Errors.Push(errors.ErrorCodeUnknown, err) | ||||
| 			}) | ||||
| 		} | ||||
| 
 | ||||
|  | @ -66,7 +67,7 @@ func (luh *layerUploadHandler) StartLayerUpload(w http.ResponseWriter, r *http.R | |||
| 	upload, err := layers.Upload(luh.Name) | ||||
| 	if err != nil { | ||||
| 		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
 | ||||
| 		luh.Errors.Push(ErrorCodeUnknown, err) | ||||
| 		luh.Errors.Push(errors.ErrorCodeUnknown, err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -75,7 +76,7 @@ func (luh *layerUploadHandler) StartLayerUpload(w http.ResponseWriter, r *http.R | |||
| 
 | ||||
| 	if err := luh.layerUploadResponse(w, r); err != nil { | ||||
| 		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
 | ||||
| 		luh.Errors.Push(ErrorCodeUnknown, err) | ||||
| 		luh.Errors.Push(errors.ErrorCodeUnknown, err) | ||||
| 		return | ||||
| 	} | ||||
| 	w.WriteHeader(http.StatusAccepted) | ||||
|  | @ -85,12 +86,12 @@ func (luh *layerUploadHandler) StartLayerUpload(w http.ResponseWriter, r *http.R | |||
| func (luh *layerUploadHandler) GetUploadStatus(w http.ResponseWriter, r *http.Request) { | ||||
| 	if luh.Upload == nil { | ||||
| 		w.WriteHeader(http.StatusNotFound) | ||||
| 		luh.Errors.Push(ErrorCodeUnknownLayerUpload) | ||||
| 		luh.Errors.Push(errors.ErrorCodeBlobUploadUnknown) | ||||
| 	} | ||||
| 
 | ||||
| 	if err := luh.layerUploadResponse(w, r); err != nil { | ||||
| 		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
 | ||||
| 		luh.Errors.Push(ErrorCodeUnknown, err) | ||||
| 		luh.Errors.Push(errors.ErrorCodeUnknown, err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -102,7 +103,7 @@ func (luh *layerUploadHandler) GetUploadStatus(w http.ResponseWriter, r *http.Re | |||
| func (luh *layerUploadHandler) PutLayerChunk(w http.ResponseWriter, r *http.Request) { | ||||
| 	if luh.Upload == nil { | ||||
| 		w.WriteHeader(http.StatusNotFound) | ||||
| 		luh.Errors.Push(ErrorCodeUnknownLayerUpload) | ||||
| 		luh.Errors.Push(errors.ErrorCodeBlobUploadUnknown) | ||||
| 	} | ||||
| 
 | ||||
| 	var finished bool | ||||
|  | @ -119,14 +120,14 @@ func (luh *layerUploadHandler) PutLayerChunk(w http.ResponseWriter, r *http.Requ | |||
| 	if err := luh.maybeCompleteUpload(w, r); err != nil { | ||||
| 		if err != errNotReadyToComplete { | ||||
| 			w.WriteHeader(http.StatusInternalServerError) | ||||
| 			luh.Errors.Push(ErrorCodeUnknown, err) | ||||
| 			luh.Errors.Push(errors.ErrorCodeUnknown, err) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err := luh.layerUploadResponse(w, r); err != nil { | ||||
| 		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
 | ||||
| 		luh.Errors.Push(ErrorCodeUnknown, err) | ||||
| 		luh.Errors.Push(errors.ErrorCodeUnknown, err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -141,7 +142,7 @@ func (luh *layerUploadHandler) PutLayerChunk(w http.ResponseWriter, r *http.Requ | |||
| func (luh *layerUploadHandler) CancelLayerUpload(w http.ResponseWriter, r *http.Request) { | ||||
| 	if luh.Upload == nil { | ||||
| 		w.WriteHeader(http.StatusNotFound) | ||||
| 		luh.Errors.Push(ErrorCodeUnknownLayerUpload) | ||||
| 		luh.Errors.Push(errors.ErrorCodeBlobUploadUnknown) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  | @ -194,14 +195,14 @@ func (luh *layerUploadHandler) maybeCompleteUpload(w http.ResponseWriter, r *htt | |||
| func (luh *layerUploadHandler) completeUpload(w http.ResponseWriter, r *http.Request, size int64, dgst digest.Digest) { | ||||
| 	layer, err := luh.Upload.Finish(size, dgst) | ||||
| 	if err != nil { | ||||
| 		luh.Errors.Push(ErrorCodeUnknown, err) | ||||
| 		luh.Errors.Push(errors.ErrorCodeUnknown, err) | ||||
| 		w.WriteHeader(http.StatusInternalServerError) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	layerURL, err := luh.urlBuilder.forLayer(layer) | ||||
| 	if err != nil { | ||||
| 		luh.Errors.Push(ErrorCodeUnknown, err) | ||||
| 		luh.Errors.Push(errors.ErrorCodeUnknown, err) | ||||
| 		w.WriteHeader(http.StatusInternalServerError) | ||||
| 		return | ||||
| 	} | ||||
|  |  | |||
							
								
								
									
										3
									
								
								tags.go
								
								
								
								
							
							
						
						
									
										3
									
								
								tags.go
								
								
								
								
							|  | @ -4,6 +4,7 @@ import ( | |||
| 	"encoding/json" | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"github.com/docker/docker-registry/api/errors" | ||||
| 	"github.com/docker/docker-registry/storage" | ||||
| 	"github.com/gorilla/handlers" | ||||
| ) | ||||
|  | @ -39,7 +40,7 @@ func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) { | |||
| 		switch err := err.(type) { | ||||
| 		case storage.ErrUnknownRepository: | ||||
| 			w.WriteHeader(404) | ||||
| 			th.Errors.Push(ErrorCodeUnknownRepository, map[string]string{"name": th.Name}) | ||||
| 			th.Errors.Push(errors.ErrorCodeNameUnknown, map[string]string{"name": th.Name}) | ||||
| 		default: | ||||
| 			th.Errors.PushErr(err) | ||||
| 		} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue