Round 3 - Add Register function

Signed-off-by: Doug Davis <dug@us.ibm.com>
This commit is contained in:
Doug Davis 2015-05-26 18:16:45 -07:00
parent 00b1e8fca0
commit b8b16b78f4
7 changed files with 187 additions and 251 deletions

View file

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"strings" "strings"
"sync"
) )
// ErrorCode represents the error type. The errors are serialized via strings // ErrorCode represents the error type. The errors are serialized via strings
@ -36,49 +37,70 @@ type ErrorDescriptor struct {
var ( var (
errorCodeToDescriptors = map[ErrorCode]ErrorDescriptor{} errorCodeToDescriptors = map[ErrorCode]ErrorDescriptor{}
idToDescriptors = map[string]ErrorDescriptor{} idToDescriptors = map[string]ErrorDescriptor{}
groupToDescriptors = map[string][]ErrorDescriptor{}
) )
const ( // ErrorCodeUnknown is a generic error that can be used as a last
// ErrorCodeUnknown is a catch-all for errors not defined below. // resort if there is no situation-specific error message that can be used
ErrorCodeUnknown ErrorCode = 10000 + iota var ErrorCodeUnknown = Register("registry.api.errcode", ErrorDescriptor{
) Value: "UNKNOWN",
Message: "unknown error",
var errorDescriptors = []ErrorDescriptor{ Description: `Generic error returned when the error does not have an
{
Code: ErrorCodeUnknown,
Value: "UNKNOWN",
Message: "unknown error",
Description: `Generic error returned when the error does not have an
API classification.`, API classification.`,
HTTPStatusCode: http.StatusInternalServerError, HTTPStatusCode: http.StatusInternalServerError,
}, })
}
// LoadErrors will register a new set of Errors into the system var nextCode = 1000
func LoadErrors(errs []ErrorDescriptor) { var registerLock sync.Mutex
for _, descriptor := range errs {
if _, ok := idToDescriptors[descriptor.Value]; ok {
panic(fmt.Sprintf("ErrorValue %s is already registered", descriptor.Value))
}
if _, ok := errorCodeToDescriptors[descriptor.Code]; ok {
panic(fmt.Sprintf("ErrorCode %d is already registered", descriptor.Code))
}
errorCodeToDescriptors[descriptor.Code] = descriptor // Register will make the passed-in error known to the environment and
idToDescriptors[descriptor.Value] = descriptor // return a new ErrorCode
func Register(group string, descriptor ErrorDescriptor) ErrorCode {
registerLock.Lock()
defer registerLock.Unlock()
code := ErrorCode(nextCode)
descriptor.Code = code
if _, ok := idToDescriptors[descriptor.Value]; ok {
panic(fmt.Sprintf("ErrorValue %s is already registered", descriptor.Value))
} }
} if _, ok := errorCodeToDescriptors[descriptor.Code]; ok {
panic(fmt.Sprintf("ErrorCode %d is already registered", descriptor.Code))
// 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 groupToDescriptors[group] = append(groupToDescriptors[group], descriptor)
errorCodeToDescriptors[code] = descriptor
idToDescriptors[descriptor.Value] = descriptor
nextCode++
return code
}
// ParseErrorCode returns the value by the string error code.
// `ErrorCodeUnknown` will be returned if the error is not known.
func ParseErrorCode(value string) ErrorCode {
ed, ok := idToDescriptors[value]
if ok {
return ed.Code
}
return ErrorCodeUnknown
}
// GetGroupNames returns the list of Error group names that are registered
func GetGroupNames() []string {
keys := []string{}
for k := range groupToDescriptors {
keys = append(keys, k)
}
return keys
}
// GetErrorCodeGroup returns the named group of error descriptors
func GetErrorCodeGroup(name string) []ErrorDescriptor {
return groupToDescriptors[name]
} }
// Descriptor returns the descriptor for the error code. // Descriptor returns the descriptor for the error code.
@ -183,8 +205,3 @@ func (errs Errors) Error() string {
func (errs Errors) Len() int { func (errs Errors) Len() int {
return len(errs) return len(errs)
} }
// init loads the default errors that are part of the errcode package
func init() {
LoadErrors(errorDescriptors)
}

View file

@ -2,67 +2,86 @@ package errcode
import ( import (
"encoding/json" "encoding/json"
// "reflect" "net/http"
"reflect"
"testing" "testing"
// "github.com/docker/distribution/digest"
) )
// TestErrorCodes ensures that error code format, mappings and // TestErrorCodes ensures that error code format, mappings and
// marshaling/unmarshaling. round trips are stable. // marshaling/unmarshaling. round trips are stable.
func TestErrorCodes(t *testing.T) { func TestErrorCodes(t *testing.T) {
for _, desc := range errorDescriptors { if len(errorCodeToDescriptors) == 0 {
if desc.Code.String() != desc.Value { t.Fatal("errors aren't loaded!")
t.Fatalf("error code string incorrect: %q != %q", desc.Code.String(), desc.Value) }
for ec, desc := range errorCodeToDescriptors {
if ec != desc.Code {
t.Fatalf("error code in descriptor isn't correct, %q != %q", ec, desc.Code)
} }
if desc.Code.Message() != desc.Message { if idToDescriptors[desc.Value].Code != ec {
t.Fatalf("incorrect message for error code %v: %q != %q", desc.Code, desc.Code.Message(), desc.Message) t.Fatalf("error code in idToDesc isn't correct, %q != %q", idToDescriptors[desc.Value].Code, ec)
} }
// Serialize the error code using the json library to ensure that we if ec.Message() != desc.Message {
// get a string and it works round trip. t.Fatalf("ec.Message doesn't mtach desc.Message: %q != %q", ec.Message(), desc.Message)
p, err := json.Marshal(desc.Code) }
// Test (de)serializing the ErrorCode
p, err := json.Marshal(ec)
if err != nil { if err != nil {
t.Fatalf("error marshaling error code %v: %v", desc.Code, err) t.Fatalf("couldn't marshal ec %v: %v", ec, err)
} }
if len(p) <= 0 { if len(p) <= 0 {
t.Fatalf("expected content in marshaled before for error code %v", desc.Code) t.Fatalf("expected content in marshaled before for error code %v", ec)
} }
// First, unmarshal to interface and ensure we have a string. // First, unmarshal to interface and ensure we have a string.
var ecUnspecified interface{} var ecUnspecified interface{}
if err := json.Unmarshal(p, &ecUnspecified); err != nil { if err := json.Unmarshal(p, &ecUnspecified); err != nil {
t.Fatalf("error unmarshaling error code %v: %v", desc.Code, err) t.Fatalf("error unmarshaling error code %v: %v", ec, err)
} }
if _, ok := ecUnspecified.(string); !ok { if _, ok := ecUnspecified.(string); !ok {
t.Fatalf("expected a string for error code %v on unmarshal got a %T", desc.Code, ecUnspecified) 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 // Now, unmarshal with the error code type and ensure they are equal
var ecUnmarshaled ErrorCode var ecUnmarshaled ErrorCode
if err := json.Unmarshal(p, &ecUnmarshaled); err != nil { if err := json.Unmarshal(p, &ecUnmarshaled); err != nil {
t.Fatalf("error unmarshaling error code %v: %v", desc.Code, err) t.Fatalf("error unmarshaling error code %v: %v", ec, err)
} }
if ecUnmarshaled != desc.Code { if ecUnmarshaled != ec {
t.Fatalf("unexpected error code during error code marshal/unmarshal: %v != %v", ecUnmarshaled, desc.Code) 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 // TestErrorsManagement does a quick check of the Errors type to ensure that
// members are properly pushed and marshaled. // members are properly pushed and marshaled.
/* var ErrorCodeTest1 = Register("v2.errors", ErrorDescriptor{
Value: "TEST1",
Message: "test error 1",
Description: `Just a test message #1.`,
HTTPStatusCode: http.StatusInternalServerError,
})
var ErrorCodeTest2 = Register("v2.errors", ErrorDescriptor{
Value: "TEST2",
Message: "test error 2",
Description: `Just a test message #2.`,
HTTPStatusCode: http.StatusNotFound,
})
func TestErrorsManagement(t *testing.T) { func TestErrorsManagement(t *testing.T) {
var errs Errors var errs Errors
errs.Push(ErrorCodeDigestInvalid) errs = append(errs, NewError(ErrorCodeTest1))
errs.Push(ErrorCodeBlobUnknown, errs = append(errs, NewError(ErrorCodeTest2,
map[string]digest.Digest{"digest": "sometestblobsumdoesntmatter"}) map[string]interface{}{"digest": "sometestblobsumdoesntmatter"}))
p, err := json.Marshal(errs) p, err := json.Marshal(errs)
@ -70,15 +89,25 @@ func TestErrorsManagement(t *testing.T) {
t.Fatalf("error marashaling errors: %v", err) 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\"}}]}" expectedJSON := "[{\"code\":\"TEST1\"},{\"code\":\"TEST2\",\"detail\":{\"digest\":\"sometestblobsumdoesntmatter\"}}]"
if string(p) != expectedJSON { if string(p) != expectedJSON {
t.Fatalf("unexpected json: %q != %q", string(p), expectedJSON) t.Fatalf("unexpected json: %q != %q", string(p), expectedJSON)
} }
errs.Clear() // Now test the reverse
errs.Push(ErrorCodeUnknown) var unmarshaled Errors
expectedJSON = "{\"errors\":[{\"code\":\"UNKNOWN\",\"message\":\"unknown error\"}]}" if err := json.Unmarshal(p, &unmarshaled); err != nil {
t.Fatalf("unexpected error unmarshaling error envelope: %v", err)
}
if !reflect.DeepEqual(unmarshaled, errs) {
t.Fatalf("errors not equal after round trip:\nunmarshaled:\n%#v\n\nerrs:\n%#v", unmarshaled, errs)
}
// Test again with a single value this time
errs = Errors{NewError(ErrorCodeUnknown)}
expectedJSON = "[{\"code\":\"UNKNOWN\"}]"
p, err = json.Marshal(errs) p, err = json.Marshal(errs)
if err != nil { if err != nil {
@ -88,80 +117,15 @@ func TestErrorsManagement(t *testing.T) {
if string(p) != expectedJSON { if string(p) != expectedJSON {
t.Fatalf("unexpected json: %q != %q", string(p), expectedJSON) t.Fatalf("unexpected json: %q != %q", string(p), expectedJSON)
} }
}
// TestMarshalUnmarshal ensures that api errors can round trip through json // Now test the reverse
// without losing information. unmarshaled = nil
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 { if err := json.Unmarshal(p, &unmarshaled); err != nil {
t.Fatalf("unexpected error unmarshaling error envelope: %v", err) t.Fatalf("unexpected error unmarshaling error envelope: %v", err)
} }
if !reflect.DeepEqual(unmarshaled, errors) { if !reflect.DeepEqual(unmarshaled, errs) {
t.Fatalf("errors not equal after round trip: %#v != %#v", unmarshaled, errors) t.Fatalf("errors not equal after round trip:\nunmarshaled:\n%#v\n\nerrs:\n%#v", unmarshaled, errs)
} }
} }
*/

View file

@ -172,13 +172,8 @@ const (
var APIDescriptor = struct { var APIDescriptor = struct {
// RouteDescriptors provides a list of the routes available in the API. // RouteDescriptors provides a list of the routes available in the API.
RouteDescriptors []RouteDescriptor RouteDescriptors []RouteDescriptor
// ErrorDescriptors provides a list of the error codes and their
// associated documentation and metadata.
ErrorDescriptors []errcode.ErrorDescriptor
}{ }{
RouteDescriptors: routeDescriptors, RouteDescriptors: routeDescriptors,
ErrorDescriptors: errorDescriptors,
} }
// RouteDescriptor describes a route specified by name. // RouteDescriptor describes a route specified by name.

View file

@ -6,81 +6,28 @@ import (
"github.com/docker/distribution/registry/api/errcode" "github.com/docker/distribution/registry/api/errcode"
) )
const ( var (
// ErrorCodeUnsupported is returned when an operation is not supported. // ErrorCodeUnsupported is returned when an operation is not supported.
ErrorCodeUnsupported = iota ErrorCodeUnsupported = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
// ErrorCodeUnauthorized is returned if a request is not authorized.
ErrorCodeUnauthorized
// 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
// ErrorCodeManifestBlobUnknown is returned when a manifest blob is
// unknown to the registry.
ErrorCodeManifestBlobUnknown
// 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
// ErrorCodeBlobUploadInvalid is returned when an upload is invalid.
ErrorCodeBlobUploadInvalid
)
// ErrorDescriptors provides a list of HTTP API Error codes that may be
// encountered when interacting with the registry API.
var errorDescriptors = []errcode.ErrorDescriptor{
{
Code: ErrorCodeUnsupported,
Value: "UNSUPPORTED", Value: "UNSUPPORTED",
Message: "The operation is unsupported.", Message: "The operation is unsupported.",
Description: `The operation was unsupported due to a missing Description: `The operation was unsupported due to a missing
implementation or invalid set of parameters.`, implementation or invalid set of parameters.`,
}, })
{
Code: ErrorCodeUnauthorized, // ErrorCodeUnauthorized is returned if a request is not authorized.
ErrorCodeUnauthorized = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "UNAUTHORIZED", Value: "UNAUTHORIZED",
Message: "access to the requested resource is not authorized", Message: "access to the requested resource is not authorized",
Description: `The access controller denied access for the operation on Description: `The access controller denied access for the operation on
a resource. Often this will be accompanied by a 401 Unauthorized a resource. Often this will be accompanied by a 401 Unauthorized
response status.`, response status.`,
HTTPStatusCode: http.StatusForbidden, HTTPStatusCode: http.StatusForbidden,
}, })
{
Code: ErrorCodeDigestInvalid, // ErrorCodeDigestInvalid is returned when uploading a blob if the
// provided digest does not match the blob contents.
ErrorCodeDigestInvalid = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "DIGEST_INVALID", Value: "DIGEST_INVALID",
Message: "provided digest did not match uploaded content", Message: "provided digest did not match uploaded content",
Description: `When a blob is uploaded, the registry will check that Description: `When a blob is uploaded, the registry will check that
@ -89,50 +36,60 @@ var errorDescriptors = []errcode.ErrorDescriptor{
invalid digest string. This error may also be returned when a manifest invalid digest string. This error may also be returned when a manifest
includes an invalid layer digest.`, includes an invalid layer digest.`,
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
}, })
{
Code: ErrorCodeSizeInvalid, // ErrorCodeSizeInvalid is returned when uploading a blob if the provided
ErrorCodeSizeInvalid = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "SIZE_INVALID", Value: "SIZE_INVALID",
Message: "provided length did not match content length", Message: "provided length did not match content length",
Description: `When a layer is uploaded, the provided size will be Description: `When a layer is uploaded, the provided size will be
checked against the uploaded content. If they do not match, this error checked against the uploaded content. If they do not match, this error
will be returned.`, will be returned.`,
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
}, })
{
Code: ErrorCodeNameInvalid, // ErrorCodeNameInvalid is returned when the name in the manifest does not
// match the provided name.
ErrorCodeNameInvalid = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "NAME_INVALID", Value: "NAME_INVALID",
Message: "invalid repository name", Message: "invalid repository name",
Description: `Invalid repository name encountered either during Description: `Invalid repository name encountered either during
manifest validation or any API operation.`, manifest validation or any API operation.`,
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
}, })
{
Code: ErrorCodeTagInvalid, // ErrorCodeTagInvalid is returned when the tag in the manifest does not
// match the provided tag.
ErrorCodeTagInvalid = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "TAG_INVALID", Value: "TAG_INVALID",
Message: "manifest tag did not match URI", Message: "manifest tag did not match URI",
Description: `During a manifest upload, if the tag in the manifest Description: `During a manifest upload, if the tag in the manifest
does not match the uri tag, this error will be returned.`, does not match the uri tag, this error will be returned.`,
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
}, })
{
Code: ErrorCodeNameUnknown, // ErrorCodeNameUnknown when the repository name is not known.
ErrorCodeNameUnknown = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "NAME_UNKNOWN", Value: "NAME_UNKNOWN",
Message: "repository name not known to registry", Message: "repository name not known to registry",
Description: `This is returned if the name used during an operation is Description: `This is returned if the name used during an operation is
unknown to the registry.`, unknown to the registry.`,
HTTPStatusCode: http.StatusNotFound, HTTPStatusCode: http.StatusNotFound,
}, })
{
Code: ErrorCodeManifestUnknown, // ErrorCodeManifestUnknown returned when image manifest is unknown.
ErrorCodeManifestUnknown = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "MANIFEST_UNKNOWN", Value: "MANIFEST_UNKNOWN",
Message: "manifest unknown", Message: "manifest unknown",
Description: `This error is returned when the manifest, identified by Description: `This error is returned when the manifest, identified by
name and tag is unknown to the repository.`, name and tag is unknown to the repository.`,
HTTPStatusCode: http.StatusNotFound, HTTPStatusCode: http.StatusNotFound,
}, })
{
Code: ErrorCodeManifestInvalid, // 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 = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "MANIFEST_INVALID", Value: "MANIFEST_INVALID",
Message: "manifest invalid", Message: "manifest invalid",
Description: `During upload, manifests undergo several checks ensuring Description: `During upload, manifests undergo several checks ensuring
@ -140,25 +97,32 @@ var errorDescriptors = []errcode.ErrorDescriptor{
more specific error is included. The detail will contain information more specific error is included. The detail will contain information
the failed validation.`, the failed validation.`,
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
}, })
{
Code: ErrorCodeManifestUnverified, // ErrorCodeManifestUnverified is returned when the manifest fails
// signature verfication.
ErrorCodeManifestUnverified = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "MANIFEST_UNVERIFIED", Value: "MANIFEST_UNVERIFIED",
Message: "manifest failed signature verification", Message: "manifest failed signature verification",
Description: `During manifest upload, if the manifest fails signature Description: `During manifest upload, if the manifest fails signature
verification, this error will be returned.`, verification, this error will be returned.`,
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
}, })
{
Code: ErrorCodeManifestBlobUnknown, // ErrorCodeManifestBlobUnknown is returned when a manifest blob is
// unknown to the registry.
ErrorCodeManifestBlobUnknown = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "MANIFEST_BLOB_UNKNOWN", Value: "MANIFEST_BLOB_UNKNOWN",
Message: "blob unknown to registry", Message: "blob unknown to registry",
Description: `This error may be returned when a manifest blob is Description: `This error may be returned when a manifest blob is
unknown to the registry.`, unknown to the registry.`,
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
}, })
{
Code: ErrorCodeBlobUnknown, // 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 = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "BLOB_UNKNOWN", Value: "BLOB_UNKNOWN",
Message: "blob unknown to registry", Message: "blob unknown to registry",
Description: `This error may be returned when a blob is unknown to the Description: `This error may be returned when a blob is unknown to the
@ -166,27 +130,23 @@ var errorDescriptors = []errcode.ErrorDescriptor{
standard get or if a manifest references an unknown layer during standard get or if a manifest references an unknown layer during
upload.`, upload.`,
HTTPStatusCode: http.StatusNotFound, HTTPStatusCode: http.StatusNotFound,
}, })
{ // ErrorCodeBlobUploadUnknown is returned when an upload is unknown.
Code: ErrorCodeBlobUploadUnknown, ErrorCodeBlobUploadUnknown = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "BLOB_UPLOAD_UNKNOWN", Value: "BLOB_UPLOAD_UNKNOWN",
Message: "blob upload unknown to registry", Message: "blob upload unknown to registry",
Description: `If a blob upload has been cancelled or was never Description: `If a blob upload has been cancelled or was never
started, this error code may be returned.`, started, this error code may be returned.`,
HTTPStatusCode: http.StatusNotFound, HTTPStatusCode: http.StatusNotFound,
}, })
{
Code: ErrorCodeBlobUploadInvalid, // ErrorCodeBlobUploadInvalid is returned when an upload is invalid.
ErrorCodeBlobUploadInvalid = errcode.Register("registry.api.v2", errcode.ErrorDescriptor{
Value: "BLOB_UPLOAD_INVALID", Value: "BLOB_UPLOAD_INVALID",
Message: "blob upload invalid", Message: "blob upload invalid",
Description: `The blob upload encountered an error and can no Description: `The blob upload encountered an error and can no
longer proceed.`, longer proceed.`,
HTTPStatusCode: http.StatusNotFound, HTTPStatusCode: http.StatusNotFound,
}, })
} )
// init registers our errors with the errcode system
func init() {
errcode.LoadErrors(errorDescriptors)
}

View file

@ -452,6 +452,7 @@ func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Cont
if err != nil { if err != nil {
switch err := err.(type) { switch err := err.(type) {
case auth.Challenge: case auth.Challenge:
// NOTE(duglin):
// Since err.ServeHTTP will set the HTTP status code for us // Since err.ServeHTTP will set the HTTP status code for us
// we need to set the content-type here. The serveJSON // we need to set the content-type here. The serveJSON
// func will try to do it but it'll be too late at that point. // func will try to do it but it'll be too late at that point.

View file

@ -186,11 +186,9 @@ func TestNewApp(t *testing.T) {
t.Fatalf("unexpected status code during request: %v", err) t.Fatalf("unexpected status code during request: %v", err)
} }
/* if req.Header.Get("Content-Type") != "application/json; charset=utf-8" {
if req.Header.Get("Content-Type") != "application/json; charset=utf-8" { t.Fatalf("unexpected content-type: %v != %v", req.Header.Get("Content-Type"), "application/json; charset=utf-8")
t.Fatalf("unexpected content-type: %v != %v", req.Header.Get("Content-Type"), "application/json; charset=utf-8") }
}
*/
expectedAuthHeader := "Bearer realm=\"realm-test\",service=\"service-test\"" expectedAuthHeader := "Bearer realm=\"realm-test\",service=\"service-test\""
if e, a := expectedAuthHeader, req.Header.Get("WWW-Authenticate"); e != a { if e, a := expectedAuthHeader, req.Header.Get("WWW-Authenticate"); e != a {

View file

@ -2,9 +2,10 @@ package handlers
import ( import (
"encoding/json" "encoding/json"
"github.com/docker/distribution/registry/api/errcode"
"io" "io"
"net/http" "net/http"
"github.com/docker/distribution/registry/api/errcode"
) )
// serveJSON marshals v and sets the content-type header to // serveJSON marshals v and sets the content-type header to