From 292e30bc6110956df9271de1d69c38f46d10046c Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 4 Sep 2023 17:52:12 +0200 Subject: [PATCH 1/2] registry/api: move all errors to "errcode" package Signed-off-by: Sebastiaan van Stijn --- registry/api/errcode/register.go | 151 +++++++++++++++++++++++++ registry/api/v2/descriptors.go | 102 ++++++++--------- registry/api/v2/errors.go | 158 --------------------------- registry/api/v2/errors_deprecated.go | 86 +++++++++++++++ registry/client/blob_writer_test.go | 9 +- registry/client/repository_test.go | 3 +- registry/handlers/api_test.go | 32 +++--- registry/handlers/app.go | 4 +- registry/handlers/blob.go | 9 +- registry/handlers/blobupload.go | 31 +++--- registry/handlers/catalog.go | 5 +- registry/handlers/manifests.go | 37 +++---- registry/handlers/tags.go | 5 +- 13 files changed, 352 insertions(+), 280 deletions(-) delete mode 100644 registry/api/v2/errors.go create mode 100644 registry/api/v2/errors_deprecated.go diff --git a/registry/api/errcode/register.go b/registry/api/errcode/register.go index 4674d49c9..49bbf0f82 100644 --- a/registry/api/errcode/register.go +++ b/registry/api/errcode/register.go @@ -75,6 +75,157 @@ var ( }) ) +const errGroup = "registry.api.v2" + +var ( + // ErrorCodeDigestInvalid is returned when uploading a blob if the + // provided digest does not match the blob contents. + ErrorCodeDigestInvalid = Register(errGroup, ErrorDescriptor{ + 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.`, + HTTPStatusCode: http.StatusBadRequest, + }) + + // ErrorCodeSizeInvalid is returned when uploading a blob if the provided + ErrorCodeSizeInvalid = Register(errGroup, ErrorDescriptor{ + 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.`, + HTTPStatusCode: http.StatusBadRequest, + }) + + // ErrorCodeRangeInvalid is returned when uploading a blob if the provided + // content range is invalid. + ErrorCodeRangeInvalid = Register(errGroup, ErrorDescriptor{ + Value: "RANGE_INVALID", + Message: "invalid content range", + Description: `When a layer is uploaded, the provided range is checked + against the uploaded chunk. This error is returned if the range is + out of order.`, + HTTPStatusCode: http.StatusRequestedRangeNotSatisfiable, + }) + + // ErrorCodeNameInvalid is returned when the name in the manifest does not + // match the provided name. + ErrorCodeNameInvalid = Register(errGroup, ErrorDescriptor{ + Value: "NAME_INVALID", + Message: "invalid repository name", + Description: `Invalid repository name encountered either during + manifest validation or any API operation.`, + HTTPStatusCode: http.StatusBadRequest, + }) + + // ErrorCodeTagInvalid is returned when the tag in the manifest does not + // match the provided tag. + ErrorCodeTagInvalid = Register(errGroup, ErrorDescriptor{ + 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.`, + HTTPStatusCode: http.StatusBadRequest, + }) + + // ErrorCodeNameUnknown when the repository name is not known. + ErrorCodeNameUnknown = Register(errGroup, ErrorDescriptor{ + 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.`, + HTTPStatusCode: http.StatusNotFound, + }) + + // ErrorCodeManifestUnknown returned when image manifest is unknown. + ErrorCodeManifestUnknown = Register(errGroup, ErrorDescriptor{ + Value: "MANIFEST_UNKNOWN", + Message: "manifest unknown", + Description: `This error is returned when the manifest, identified by + name and tag is unknown to the repository.`, + HTTPStatusCode: http.StatusNotFound, + }) + + // 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 = Register(errGroup, ErrorDescriptor{ + 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. The detail will contain information + the failed validation.`, + HTTPStatusCode: http.StatusBadRequest, + }) + + // ErrorCodeManifestUnverified is returned when the manifest fails + // signature verification. + ErrorCodeManifestUnverified = Register(errGroup, ErrorDescriptor{ + Value: "MANIFEST_UNVERIFIED", + Message: "manifest failed signature verification", + Description: `During manifest upload, if the manifest fails signature + verification, this error will be returned.`, + HTTPStatusCode: http.StatusBadRequest, + }) + + // ErrorCodeManifestBlobUnknown is returned when a manifest blob is + // unknown to the registry. + ErrorCodeManifestBlobUnknown = Register(errGroup, ErrorDescriptor{ + Value: "MANIFEST_BLOB_UNKNOWN", + Message: "blob unknown to registry", + Description: `This error may be returned when a manifest blob is + unknown to the registry.`, + HTTPStatusCode: http.StatusBadRequest, + }) + + // 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 = Register(errGroup, ErrorDescriptor{ + 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.`, + HTTPStatusCode: http.StatusNotFound, + }) + + // ErrorCodeBlobUploadUnknown is returned when an upload is unknown. + ErrorCodeBlobUploadUnknown = Register(errGroup, ErrorDescriptor{ + 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.`, + HTTPStatusCode: http.StatusNotFound, + }) + + // ErrorCodeBlobUploadInvalid is returned when an upload is invalid. + ErrorCodeBlobUploadInvalid = Register(errGroup, ErrorDescriptor{ + Value: "BLOB_UPLOAD_INVALID", + Message: "blob upload invalid", + Description: `The blob upload encountered an error and can no + longer proceed.`, + HTTPStatusCode: http.StatusNotFound, + }) + + // ErrorCodePaginationNumberInvalid is returned when the `n` parameter is + // not an integer, or `n` is negative. + ErrorCodePaginationNumberInvalid = Register(errGroup, ErrorDescriptor{ + Value: "PAGINATION_NUMBER_INVALID", + Message: "invalid number of results requested", + Description: `Returned when the "n" parameter (number of results + to return) is not an integer, "n" is negative or "n" is bigger than + the maximum allowed.`, + HTTPStatusCode: http.StatusBadRequest, + }) +) + var ( nextCode = 1000 registerLock sync.Mutex diff --git a/registry/api/v2/descriptors.go b/registry/api/v2/descriptors.go index d0e9620e3..63f51cab8 100644 --- a/registry/api/v2/descriptors.go +++ b/registry/api/v2/descriptors.go @@ -143,7 +143,7 @@ var ( Format: errorsBody, }, ErrorCodes: []errcode.ErrorCode{ - ErrorCodePaginationNumberInvalid, + errcode.ErrorCodePaginationNumberInvalid, }, } @@ -164,7 +164,7 @@ var ( Format: errorsBody, }, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameUnknown, + errcode.ErrorCodeNameUnknown, }, } @@ -550,8 +550,8 @@ var routeDescriptors = []RouteDescriptor{ Description: "The name or reference was invalid.", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameInvalid, - ErrorCodeTagInvalid, + errcode.ErrorCodeNameInvalid, + errcode.ErrorCodeTagInvalid, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -609,11 +609,11 @@ var routeDescriptors = []RouteDescriptor{ Format: errorsBody, }, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameInvalid, - ErrorCodeTagInvalid, - ErrorCodeManifestInvalid, - ErrorCodeManifestUnverified, - ErrorCodeBlobUnknown, + errcode.ErrorCodeNameInvalid, + errcode.ErrorCodeTagInvalid, + errcode.ErrorCodeManifestInvalid, + errcode.ErrorCodeManifestUnverified, + errcode.ErrorCodeBlobUnknown, }, }, unauthorizedResponseDescriptor, @@ -625,7 +625,7 @@ var routeDescriptors = []RouteDescriptor{ Description: "One or more layers may be missing during a manifest upload. If so, the missing layers will be enumerated in the error response.", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeBlobUnknown, + errcode.ErrorCodeBlobUnknown, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -678,8 +678,8 @@ var routeDescriptors = []RouteDescriptor{ Description: "The specified `name` or `reference` were invalid and the delete was unable to proceed.", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameInvalid, - ErrorCodeTagInvalid, + errcode.ErrorCodeNameInvalid, + errcode.ErrorCodeTagInvalid, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -695,8 +695,8 @@ var routeDescriptors = []RouteDescriptor{ Description: "The specified `name` or `reference` are unknown to the registry and the delete was unable to proceed. Clients can assume the manifest or tag was already deleted if this response is returned.", StatusCode: http.StatusNotFound, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameUnknown, - ErrorCodeManifestUnknown, + errcode.ErrorCodeNameUnknown, + errcode.ErrorCodeManifestUnknown, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -775,8 +775,8 @@ var routeDescriptors = []RouteDescriptor{ Description: "There was a problem with the request that needs to be addressed by the client, such as an invalid `name` or `tag`.", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameInvalid, - ErrorCodeDigestInvalid, + errcode.ErrorCodeNameInvalid, + errcode.ErrorCodeDigestInvalid, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -791,8 +791,8 @@ var routeDescriptors = []RouteDescriptor{ Format: errorsBody, }, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameUnknown, - ErrorCodeBlobUnknown, + errcode.ErrorCodeNameUnknown, + errcode.ErrorCodeBlobUnknown, }, }, unauthorizedResponseDescriptor, @@ -847,8 +847,8 @@ var routeDescriptors = []RouteDescriptor{ Description: "There was a problem with the request that needs to be addressed by the client, such as an invalid `name` or `tag`.", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameInvalid, - ErrorCodeDigestInvalid, + errcode.ErrorCodeNameInvalid, + errcode.ErrorCodeDigestInvalid, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -858,8 +858,8 @@ var routeDescriptors = []RouteDescriptor{ { StatusCode: http.StatusNotFound, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameUnknown, - ErrorCodeBlobUnknown, + errcode.ErrorCodeNameUnknown, + errcode.ErrorCodeBlobUnknown, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -910,8 +910,8 @@ var routeDescriptors = []RouteDescriptor{ Name: "Invalid Name or Digest", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeDigestInvalid, - ErrorCodeNameInvalid, + errcode.ErrorCodeDigestInvalid, + errcode.ErrorCodeNameInvalid, }, }, { @@ -922,8 +922,8 @@ var routeDescriptors = []RouteDescriptor{ Format: errorsBody, }, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameUnknown, - ErrorCodeBlobUnknown, + errcode.ErrorCodeNameUnknown, + errcode.ErrorCodeBlobUnknown, }, }, { @@ -1010,8 +1010,8 @@ var routeDescriptors = []RouteDescriptor{ Name: "Invalid Name or Digest", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeDigestInvalid, - ErrorCodeNameInvalid, + errcode.ErrorCodeDigestInvalid, + errcode.ErrorCodeNameInvalid, }, }, { @@ -1065,8 +1065,8 @@ var routeDescriptors = []RouteDescriptor{ Name: "Invalid Name or Digest", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeDigestInvalid, - ErrorCodeNameInvalid, + errcode.ErrorCodeDigestInvalid, + errcode.ErrorCodeNameInvalid, }, }, unauthorizedResponseDescriptor, @@ -1122,8 +1122,8 @@ var routeDescriptors = []RouteDescriptor{ Name: "Invalid Name or Digest", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeDigestInvalid, - ErrorCodeNameInvalid, + errcode.ErrorCodeDigestInvalid, + errcode.ErrorCodeNameInvalid, }, }, { @@ -1187,9 +1187,9 @@ var routeDescriptors = []RouteDescriptor{ Description: "There was an error processing the upload and it must be restarted.", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeDigestInvalid, - ErrorCodeNameInvalid, - ErrorCodeBlobUploadInvalid, + errcode.ErrorCodeDigestInvalid, + errcode.ErrorCodeNameInvalid, + errcode.ErrorCodeBlobUploadInvalid, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -1200,7 +1200,7 @@ var routeDescriptors = []RouteDescriptor{ Description: "The upload is unknown to the registry. The upload must be restarted.", StatusCode: http.StatusNotFound, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeBlobUploadUnknown, + errcode.ErrorCodeBlobUploadUnknown, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -1262,9 +1262,9 @@ var routeDescriptors = []RouteDescriptor{ Description: "There was an error processing the upload and it must be restarted.", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeDigestInvalid, - ErrorCodeNameInvalid, - ErrorCodeBlobUploadInvalid, + errcode.ErrorCodeDigestInvalid, + errcode.ErrorCodeNameInvalid, + errcode.ErrorCodeBlobUploadInvalid, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -1275,7 +1275,7 @@ var routeDescriptors = []RouteDescriptor{ Description: "The upload is unknown to the registry. The upload must be restarted.", StatusCode: http.StatusNotFound, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeBlobUploadUnknown, + errcode.ErrorCodeBlobUploadUnknown, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -1344,9 +1344,9 @@ var routeDescriptors = []RouteDescriptor{ Description: "There was an error processing the upload and it must be restarted.", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeDigestInvalid, - ErrorCodeNameInvalid, - ErrorCodeBlobUploadInvalid, + errcode.ErrorCodeDigestInvalid, + errcode.ErrorCodeNameInvalid, + errcode.ErrorCodeBlobUploadInvalid, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -1357,7 +1357,7 @@ var routeDescriptors = []RouteDescriptor{ Description: "The upload is unknown to the registry. The upload must be restarted.", StatusCode: http.StatusNotFound, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeBlobUploadUnknown, + errcode.ErrorCodeBlobUploadUnknown, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -1438,9 +1438,9 @@ var routeDescriptors = []RouteDescriptor{ Description: "There was an error processing the upload and it must be restarted.", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeDigestInvalid, - ErrorCodeNameInvalid, - ErrorCodeBlobUploadInvalid, + errcode.ErrorCodeDigestInvalid, + errcode.ErrorCodeNameInvalid, + errcode.ErrorCodeBlobUploadInvalid, errcode.ErrorCodeUnsupported, }, Body: BodyDescriptor{ @@ -1452,7 +1452,7 @@ var routeDescriptors = []RouteDescriptor{ Description: "The upload is unknown to the registry. The upload must be restarted.", StatusCode: http.StatusNotFound, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeBlobUploadUnknown, + errcode.ErrorCodeBlobUploadUnknown, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -1497,8 +1497,8 @@ var routeDescriptors = []RouteDescriptor{ Description: "An error was encountered processing the delete. The client may ignore this error.", StatusCode: http.StatusBadRequest, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeNameInvalid, - ErrorCodeBlobUploadInvalid, + errcode.ErrorCodeNameInvalid, + errcode.ErrorCodeBlobUploadInvalid, }, Body: BodyDescriptor{ ContentType: "application/json", @@ -1509,7 +1509,7 @@ var routeDescriptors = []RouteDescriptor{ Description: "The upload is unknown to the registry. The client may ignore this error and assume the upload has been deleted.", StatusCode: http.StatusNotFound, ErrorCodes: []errcode.ErrorCode{ - ErrorCodeBlobUploadUnknown, + errcode.ErrorCodeBlobUploadUnknown, }, Body: BodyDescriptor{ ContentType: "application/json", diff --git a/registry/api/v2/errors.go b/registry/api/v2/errors.go deleted file mode 100644 index dcbabfca0..000000000 --- a/registry/api/v2/errors.go +++ /dev/null @@ -1,158 +0,0 @@ -package v2 - -import ( - "net/http" - - "github.com/distribution/distribution/v3/registry/api/errcode" -) - -const errGroup = "registry.api.v2" - -var ( - // ErrorCodeDigestInvalid is returned when uploading a blob if the - // provided digest does not match the blob contents. - ErrorCodeDigestInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{ - 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.`, - HTTPStatusCode: http.StatusBadRequest, - }) - - // ErrorCodeSizeInvalid is returned when uploading a blob if the provided - ErrorCodeSizeInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{ - 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.`, - HTTPStatusCode: http.StatusBadRequest, - }) - - // ErrorCodeRangeInvalid is returned when uploading a blob if the provided - // content range is invalid. - ErrorCodeRangeInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{ - Value: "RANGE_INVALID", - Message: "invalid content range", - Description: `When a layer is uploaded, the provided range is checked - against the uploaded chunk. This error is returned if the range is - out of order.`, - HTTPStatusCode: http.StatusRequestedRangeNotSatisfiable, - }) - - // ErrorCodeNameInvalid is returned when the name in the manifest does not - // match the provided name. - ErrorCodeNameInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{ - Value: "NAME_INVALID", - Message: "invalid repository name", - Description: `Invalid repository name encountered either during - manifest validation or any API operation.`, - HTTPStatusCode: http.StatusBadRequest, - }) - - // ErrorCodeTagInvalid is returned when the tag in the manifest does not - // match the provided tag. - ErrorCodeTagInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{ - 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.`, - HTTPStatusCode: http.StatusBadRequest, - }) - - // ErrorCodeNameUnknown when the repository name is not known. - ErrorCodeNameUnknown = errcode.Register(errGroup, errcode.ErrorDescriptor{ - 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.`, - HTTPStatusCode: http.StatusNotFound, - }) - - // ErrorCodeManifestUnknown returned when image manifest is unknown. - ErrorCodeManifestUnknown = errcode.Register(errGroup, errcode.ErrorDescriptor{ - Value: "MANIFEST_UNKNOWN", - Message: "manifest unknown", - Description: `This error is returned when the manifest, identified by - name and tag is unknown to the repository.`, - HTTPStatusCode: http.StatusNotFound, - }) - - // 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(errGroup, errcode.ErrorDescriptor{ - 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. The detail will contain information - the failed validation.`, - HTTPStatusCode: http.StatusBadRequest, - }) - - // ErrorCodeManifestUnverified is returned when the manifest fails - // signature verification. - ErrorCodeManifestUnverified = errcode.Register(errGroup, errcode.ErrorDescriptor{ - Value: "MANIFEST_UNVERIFIED", - Message: "manifest failed signature verification", - Description: `During manifest upload, if the manifest fails signature - verification, this error will be returned.`, - HTTPStatusCode: http.StatusBadRequest, - }) - - // ErrorCodeManifestBlobUnknown is returned when a manifest blob is - // unknown to the registry. - ErrorCodeManifestBlobUnknown = errcode.Register(errGroup, errcode.ErrorDescriptor{ - Value: "MANIFEST_BLOB_UNKNOWN", - Message: "blob unknown to registry", - Description: `This error may be returned when a manifest blob is - unknown to the registry.`, - HTTPStatusCode: http.StatusBadRequest, - }) - - // 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(errGroup, errcode.ErrorDescriptor{ - 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.`, - HTTPStatusCode: http.StatusNotFound, - }) - - // ErrorCodeBlobUploadUnknown is returned when an upload is unknown. - ErrorCodeBlobUploadUnknown = errcode.Register(errGroup, errcode.ErrorDescriptor{ - 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.`, - HTTPStatusCode: http.StatusNotFound, - }) - - // ErrorCodeBlobUploadInvalid is returned when an upload is invalid. - ErrorCodeBlobUploadInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{ - Value: "BLOB_UPLOAD_INVALID", - Message: "blob upload invalid", - Description: `The blob upload encountered an error and can no - longer proceed.`, - HTTPStatusCode: http.StatusNotFound, - }) - - // ErrorCodePaginationNumberInvalid is returned when the `n` parameter is - // not an integer, or `n` is negative. - ErrorCodePaginationNumberInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{ - Value: "PAGINATION_NUMBER_INVALID", - Message: "invalid number of results requested", - Description: `Returned when the "n" parameter (number of results - to return) is not an integer, "n" is negative or "n" is bigger than - the maximum allowed.`, - HTTPStatusCode: http.StatusBadRequest, - }) -) diff --git a/registry/api/v2/errors_deprecated.go b/registry/api/v2/errors_deprecated.go new file mode 100644 index 000000000..b8ba2b253 --- /dev/null +++ b/registry/api/v2/errors_deprecated.go @@ -0,0 +1,86 @@ +package v2 + +import "github.com/distribution/distribution/v3/registry/api/errcode" + +var ( + // ErrorCodeDigestInvalid is returned when uploading a blob if the + // provided digest does not match the blob contents. + // + // Deprecated: use [errcode.ErrorCodeDigestInvalid]. + ErrorCodeDigestInvalid = errcode.ErrorCodeDigestInvalid + + // ErrorCodeSizeInvalid is returned when uploading a blob if the provided + // + // Deprecated: use [errcode.ErrorCodeSizeInvalid]. + ErrorCodeSizeInvalid = errcode.ErrorCodeSizeInvalid + + // ErrorCodeRangeInvalid is returned when uploading a blob if the provided + // content range is invalid. + // + // Deprecated: use [errcode.ErrorCodeRangeInvalid]. + ErrorCodeRangeInvalid = errcode.ErrorCodeRangeInvalid + + // ErrorCodeNameInvalid is returned when the name in the manifest does not + // match the provided name. + // + // Deprecated: use [errcode.ErrorCodeNameInvalid]. + ErrorCodeNameInvalid = errcode.ErrorCodeNameInvalid + + // ErrorCodeTagInvalid is returned when the tag in the manifest does not + // match the provided tag. + // + // Deprecated: use [errcode.ErrorCodeTagInvalid]. + ErrorCodeTagInvalid = errcode.ErrorCodeTagInvalid + + // ErrorCodeNameUnknown when the repository name is not known. + // + // Deprecated: use [errcode.ErrorCodeNameUnknown]. + ErrorCodeNameUnknown = errcode.ErrorCodeNameUnknown + + // ErrorCodeManifestUnknown returned when image manifest is unknown. + // + // Deprecated: use [errcode.ErrorCodeManifestUnknown]. + ErrorCodeManifestUnknown = errcode.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. + // + // Deprecated: use [errcode.ErrorCodeManifestInvalid]. + ErrorCodeManifestInvalid = errcode.ErrorCodeManifestInvalid + + // ErrorCodeManifestUnverified is returned when the manifest fails + // signature verification. + // + // Deprecated: use [errcode.ErrorCodeManifestUnverified]. + ErrorCodeManifestUnverified = errcode.ErrorCodeManifestUnverified + + // ErrorCodeManifestBlobUnknown is returned when a manifest blob is + // unknown to the registry. + // + // Deprecated: use [errcode.ErrorCodeManifestBlobUnknown]. + ErrorCodeManifestBlobUnknown = errcode.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. + // + // Deprecated: use [errcode.ErrorCodeBlobUnknown]. + ErrorCodeBlobUnknown = errcode.ErrorCodeBlobUnknown + + // ErrorCodeBlobUploadUnknown is returned when an upload is unknown. + // + // Deprecated: use [errcode.ErrorCodeBlobUploadUnknown]. + ErrorCodeBlobUploadUnknown = errcode.ErrorCodeBlobUploadUnknown + + // ErrorCodeBlobUploadInvalid is returned when an upload is invalid. + // + // Deprecated: use [errcode.ErrorCodeBlobUploadInvalid]. + ErrorCodeBlobUploadInvalid = errcode.ErrorCodeBlobUploadInvalid + + // ErrorCodePaginationNumberInvalid is returned when the `n` parameter is + // not an integer, or `n` is negative. + // + // Deprecated: use [errcode.ErrorCodePaginationNumberInvalid]. + ErrorCodePaginationNumberInvalid = errcode.ErrorCodePaginationNumberInvalid +) diff --git a/registry/client/blob_writer_test.go b/registry/client/blob_writer_test.go index d9947aaed..134c3e05d 100644 --- a/registry/client/blob_writer_test.go +++ b/registry/client/blob_writer_test.go @@ -9,7 +9,6 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/registry/api/errcode" - v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/testutil" ) @@ -175,8 +174,8 @@ func TestUploadReadFrom(t *testing.T) { if !ok { t.Fatalf("Not an 'Error' type: %#v", uploadErr[0]) } - if v2Err.Code != v2.ErrorCodeBlobUploadInvalid { - t.Fatalf("Unexpected error code: %s, expected %d", v2Err.Code.String(), v2.ErrorCodeBlobUploadInvalid) + if v2Err.Code != errcode.ErrorCodeBlobUploadInvalid { + t.Fatalf("Unexpected error code: %s, expected %d", v2Err.Code.String(), errcode.ErrorCodeBlobUploadInvalid) } if expected := "blob upload invalid"; v2Err.Message != expected { t.Fatalf("Unexpected error message: %q, expected %q", v2Err.Message, expected) @@ -463,8 +462,8 @@ func TestUploadWrite(t *testing.T) { if !ok { t.Fatalf("Not an 'Error' type: %#v", uploadErr[0]) } - if v2Err.Code != v2.ErrorCodeBlobUploadInvalid { - t.Fatalf("Unexpected error code: %s, expected %d", v2Err.Code.String(), v2.ErrorCodeBlobUploadInvalid) + if v2Err.Code != errcode.ErrorCodeBlobUploadInvalid { + t.Fatalf("Unexpected error code: %s, expected %d", v2Err.Code.String(), errcode.ErrorCodeBlobUploadInvalid) } if expected := "blob upload invalid"; v2Err.Message != expected { t.Fatalf("Unexpected error message: %q, expected %q", v2Err.Message, expected) diff --git a/registry/client/repository_test.go b/registry/client/repository_test.go index 4cda067a9..fee5bd17c 100644 --- a/registry/client/repository_test.go +++ b/registry/client/repository_test.go @@ -21,7 +21,6 @@ import ( "github.com/distribution/distribution/v3/manifest" "github.com/distribution/distribution/v3/manifest/ocischema" "github.com/distribution/distribution/v3/registry/api/errcode" - v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/testutil" "github.com/distribution/distribution/v3/uuid" "github.com/distribution/reference" @@ -1440,7 +1439,7 @@ func TestObtainsErrorForMissingTag(t *testing.T) { var m testutil.RequestResponseMap var errors errcode.Errors - errors = append(errors, v2.ErrorCodeManifestUnknown.WithDetail("unknown manifest")) + errors = append(errors, errcode.ErrorCodeManifestUnknown.WithDetail("unknown manifest")) errBytes, err := json.Marshal(errors) if err != nil { t.Fatal(err) diff --git a/registry/handlers/api_test.go b/registry/handlers/api_test.go index c921ce9ac..21dfae9d8 100644 --- a/registry/handlers/api_test.go +++ b/registry/handlers/api_test.go @@ -340,7 +340,7 @@ func TestCatalogAPI(t *testing.T) { defer resp.Body.Close() checkResponse(t, "issuing catalog api check", resp, http.StatusBadRequest) - checkBodyHasErrorCodes(t, "invalid number of results requested", resp, v2.ErrorCodePaginationNumberInvalid) + checkBodyHasErrorCodes(t, "invalid number of results requested", resp, errcode.ErrorCodePaginationNumberInvalid) // ----------------------------------- // Case No. 6: request n > maxentries but <= total catalog @@ -361,7 +361,7 @@ func TestCatalogAPI(t *testing.T) { defer resp.Body.Close() checkResponse(t, "issuing catalog api check", resp, http.StatusBadRequest) - checkBodyHasErrorCodes(t, "invalid number of results requested", resp, v2.ErrorCodePaginationNumberInvalid) + checkBodyHasErrorCodes(t, "invalid number of results requested", resp, errcode.ErrorCodePaginationNumberInvalid) // ----------------------------------- // Case No. 7: n = 0 @@ -410,7 +410,7 @@ func TestCatalogAPI(t *testing.T) { defer resp.Body.Close() checkResponse(t, "issuing catalog api check", resp, http.StatusBadRequest) - checkBodyHasErrorCodes(t, "invalid number of results requested", resp, v2.ErrorCodePaginationNumberInvalid) + checkBodyHasErrorCodes(t, "invalid number of results requested", resp, errcode.ErrorCodePaginationNumberInvalid) // ----------------------------------- // Case No. 9: n = 5, max = 5, total catalog = 4 @@ -505,13 +505,13 @@ func TestTagsAPI(t *testing.T) { name: "negative n query parameter", queryParams: url.Values{"n": []string{"-1"}}, expectedStatusCode: http.StatusBadRequest, - expectedBodyErr: &v2.ErrorCodePaginationNumberInvalid, + expectedBodyErr: &errcode.ErrorCodePaginationNumberInvalid, }, { name: "non integer n query parameter", queryParams: url.Values{"n": []string{"foo"}}, expectedStatusCode: http.StatusBadRequest, - expectedBodyErr: &v2.ErrorCodePaginationNumberInvalid, + expectedBodyErr: &errcode.ErrorCodePaginationNumberInvalid, }, { name: "1st page", @@ -901,7 +901,7 @@ func testBlobAPI(t *testing.T, env *testEnv, args blobArgs) *testEnv { defer resp.Body.Close() checkResponse(t, "bad layer push", resp, http.StatusBadRequest) - checkBodyHasErrorCodes(t, "bad layer push", resp, v2.ErrorCodeDigestInvalid) + checkBodyHasErrorCodes(t, "bad layer push", resp, errcode.ErrorCodeDigestInvalid) // ----------------------------------------- // Do layer push with an empty body and correct digest @@ -1343,7 +1343,7 @@ func TestManifestAPI_DeleteTag_Unknown(t *testing.T) { defer resp.Body.Close() checkResponse(t, msg, resp, http.StatusNotFound) - checkBodyHasErrorCodes(t, msg, resp, v2.ErrorCodeManifestUnknown) + checkBodyHasErrorCodes(t, msg, resp, errcode.ErrorCodeManifestUnknown) } func TestManifestAPI_DeleteTag_ReadOnly(t *testing.T) { @@ -1443,7 +1443,7 @@ func TestGetManifestWithStorageError(t *testing.T) { defer env1.Shutdown() repo, _ := reference.WithName(repositoryWithManifestNotFound) - testManifestWithStorageError(t, env1, repo, http.StatusNotFound, v2.ErrorCodeManifestUnknown) + testManifestWithStorageError(t, env1, repo, http.StatusNotFound, errcode.ErrorCodeManifestUnknown) repo, _ = reference.WithName(repositoryWithGenericStorageError) testManifestWithStorageError(t, env1, repo, http.StatusInternalServerError, errcode.ErrorCodeUnknown) @@ -1529,7 +1529,7 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Name defer resp.Body.Close() checkResponse(t, "getting non-existent manifest", resp, http.StatusNotFound) - checkBodyHasErrorCodes(t, "getting non-existent manifest", resp, v2.ErrorCodeManifestUnknown) + checkBodyHasErrorCodes(t, "getting non-existent manifest", resp, errcode.ErrorCodeManifestUnknown) tagsURL, err := env.builder.BuildTagsURL(imageName) if err != nil { @@ -1544,7 +1544,7 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Name // Check that we get an unknown repository error when asking for tags checkResponse(t, "getting unknown manifest tags", resp, http.StatusNotFound) - checkBodyHasErrorCodes(t, "getting unknown manifest tags", resp, v2.ErrorCodeNameUnknown) + checkBodyHasErrorCodes(t, "getting unknown manifest tags", resp, errcode.ErrorCodeNameUnknown) // -------------------------------- // Attempt to push manifest with missing config and missing layers @@ -1575,10 +1575,10 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Name resp = putManifest(t, "putting missing config manifest", manifestURL, schema2.MediaTypeManifest, manifest) defer resp.Body.Close() checkResponse(t, "putting missing config manifest", resp, http.StatusBadRequest) - _, p, counts := checkBodyHasErrorCodes(t, "putting missing config manifest", resp, v2.ErrorCodeManifestBlobUnknown) + _, p, counts := checkBodyHasErrorCodes(t, "putting missing config manifest", resp, errcode.ErrorCodeManifestBlobUnknown) expectedCounts := map[errcode.ErrorCode]int{ - v2.ErrorCodeManifestBlobUnknown: 3, + errcode.ErrorCodeManifestBlobUnknown: 3, } if !reflect.DeepEqual(counts, expectedCounts) { @@ -1617,10 +1617,10 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Name resp = putManifest(t, "putting missing layer manifest", manifestURL, schema2.MediaTypeManifest, manifest) defer resp.Body.Close() checkResponse(t, "putting missing layer manifest", resp, http.StatusBadRequest) - _, p, counts = checkBodyHasErrorCodes(t, "getting unknown manifest tags", resp, v2.ErrorCodeManifestBlobUnknown) + _, p, counts = checkBodyHasErrorCodes(t, "getting unknown manifest tags", resp, errcode.ErrorCodeManifestBlobUnknown) expectedCounts = map[errcode.ErrorCode]int{ - v2.ErrorCodeManifestBlobUnknown: 2, + errcode.ErrorCodeManifestBlobUnknown: 2, } if !reflect.DeepEqual(counts, expectedCounts) { @@ -1841,10 +1841,10 @@ func testManifestAPIManifestList(t *testing.T, env *testEnv, args manifestArgs) resp := putManifest(t, "putting missing manifest manifestlist", manifestURL, manifestlist.MediaTypeManifestList, manifestList) defer resp.Body.Close() checkResponse(t, "putting missing manifest manifestlist", resp, http.StatusBadRequest) - _, p, counts := checkBodyHasErrorCodes(t, "putting missing manifest manifestlist", resp, v2.ErrorCodeManifestBlobUnknown) + _, p, counts := checkBodyHasErrorCodes(t, "putting missing manifest manifestlist", resp, errcode.ErrorCodeManifestBlobUnknown) expectedCounts := map[errcode.ErrorCode]int{ - v2.ErrorCodeManifestBlobUnknown: 1, + errcode.ErrorCodeManifestBlobUnknown: 1, } if !reflect.DeepEqual(counts, expectedCounts) { diff --git a/registry/handlers/app.go b/registry/handlers/app.go index 7ea9f4351..0207227bd 100644 --- a/registry/handlers/app.go +++ b/registry/handlers/app.go @@ -661,9 +661,9 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler { switch err := err.(type) { case distribution.ErrRepositoryUnknown: - context.Errors = append(context.Errors, v2.ErrorCodeNameUnknown.WithDetail(err)) + context.Errors = append(context.Errors, errcode.ErrorCodeNameUnknown.WithDetail(err)) case distribution.ErrRepositoryNameInvalid: - context.Errors = append(context.Errors, v2.ErrorCodeNameInvalid.WithDetail(err)) + context.Errors = append(context.Errors, errcode.ErrorCodeNameInvalid.WithDetail(err)) case errcode.Error: context.Errors = append(context.Errors, err) } diff --git a/registry/handlers/blob.go b/registry/handlers/blob.go index ee89a098d..4d50754d7 100644 --- a/registry/handlers/blob.go +++ b/registry/handlers/blob.go @@ -6,7 +6,6 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/context" "github.com/distribution/distribution/v3/registry/api/errcode" - v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/gorilla/handlers" "github.com/opencontainers/go-digest" ) @@ -18,12 +17,12 @@ func blobDispatcher(ctx *Context, r *http.Request) http.Handler { if err == errDigestNotAvailable { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx.Errors = append(ctx.Errors, v2.ErrorCodeDigestInvalid.WithDetail(err)) + ctx.Errors = append(ctx.Errors, errcode.ErrorCodeDigestInvalid.WithDetail(err)) }) } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx.Errors = append(ctx.Errors, v2.ErrorCodeDigestInvalid.WithDetail(err)) + ctx.Errors = append(ctx.Errors, errcode.ErrorCodeDigestInvalid.WithDetail(err)) }) } @@ -59,7 +58,7 @@ func (bh *blobHandler) GetBlob(w http.ResponseWriter, r *http.Request) { desc, err := blobs.Stat(bh, bh.Digest) if err != nil { if err == distribution.ErrBlobUnknown { - bh.Errors = append(bh.Errors, v2.ErrorCodeBlobUnknown.WithDetail(bh.Digest)) + bh.Errors = append(bh.Errors, errcode.ErrorCodeBlobUnknown.WithDetail(bh.Digest)) } else { bh.Errors = append(bh.Errors, errcode.ErrorCodeUnknown.WithDetail(err)) } @@ -85,7 +84,7 @@ func (bh *blobHandler) DeleteBlob(w http.ResponseWriter, r *http.Request) { bh.Errors = append(bh.Errors, errcode.ErrorCodeUnsupported) return case distribution.ErrBlobUnknown: - bh.Errors = append(bh.Errors, v2.ErrorCodeBlobUnknown) + bh.Errors = append(bh.Errors, errcode.ErrorCodeBlobUnknown) return default: bh.Errors = append(bh.Errors, err) diff --git a/registry/handlers/blobupload.go b/registry/handlers/blobupload.go index ee7626cb7..09c00bb8c 100644 --- a/registry/handlers/blobupload.go +++ b/registry/handlers/blobupload.go @@ -9,7 +9,6 @@ import ( "github.com/distribution/distribution/v3" dcontext "github.com/distribution/distribution/v3/context" "github.com/distribution/distribution/v3/registry/api/errcode" - v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/registry/storage" "github.com/distribution/reference" "github.com/gorilla/handlers" @@ -110,7 +109,7 @@ func (buh *blobUploadHandler) GetUploadStatus(w http.ResponseWriter, r *http.Req upload, err := blobs.Resume(buh, buh.UUID) if err != nil { if err == distribution.ErrBlobUploadUnknown { - buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadUnknown.WithDetail(err)) + buh.Errors = append(buh.Errors, errcode.ErrorCodeBlobUploadUnknown.WithDetail(err)) } else { buh.Errors = append(buh.Errors, errcode.ErrorCodeUnknown.WithDetail(err)) } @@ -131,7 +130,7 @@ func (buh *blobUploadHandler) GetUploadStatus(w http.ResponseWriter, r *http.Req // PatchBlobData writes data to an upload. func (buh *blobUploadHandler) PatchBlobData(w http.ResponseWriter, r *http.Request) { if buh.Upload == nil { - buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadUnknown) + buh.Errors = append(buh.Errors, errcode.ErrorCodeBlobUploadUnknown) return } @@ -151,7 +150,7 @@ func (buh *blobUploadHandler) PatchBlobData(w http.ResponseWriter, r *http.Reque return } if start > end || start != buh.Upload.Size() { - buh.Errors = append(buh.Errors, v2.ErrorCodeRangeInvalid) + buh.Errors = append(buh.Errors, errcode.ErrorCodeRangeInvalid) return } @@ -161,7 +160,7 @@ func (buh *blobUploadHandler) PatchBlobData(w http.ResponseWriter, r *http.Reque return } if clInt != (end-start)+1 { - buh.Errors = append(buh.Errors, v2.ErrorCodeSizeInvalid) + buh.Errors = append(buh.Errors, errcode.ErrorCodeSizeInvalid) return } } @@ -186,7 +185,7 @@ func (buh *blobUploadHandler) PatchBlobData(w http.ResponseWriter, r *http.Reque // url of the blob. func (buh *blobUploadHandler) PutBlobUploadComplete(w http.ResponseWriter, r *http.Request) { if buh.Upload == nil { - buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadUnknown) + buh.Errors = append(buh.Errors, errcode.ErrorCodeBlobUploadUnknown) return } defer buh.Upload.Close() @@ -195,14 +194,14 @@ func (buh *blobUploadHandler) PutBlobUploadComplete(w http.ResponseWriter, r *ht if dgstStr == "" { // no digest? return error, but allow retry. - buh.Errors = append(buh.Errors, v2.ErrorCodeDigestInvalid.WithDetail("digest missing")) + buh.Errors = append(buh.Errors, errcode.ErrorCodeDigestInvalid.WithDetail("digest missing")) return } dgst, err := digest.Parse(dgstStr) if err != nil { // no digest? return error, but allow retry. - buh.Errors = append(buh.Errors, v2.ErrorCodeDigestInvalid.WithDetail("digest parsing failed")) + buh.Errors = append(buh.Errors, errcode.ErrorCodeDigestInvalid.WithDetail("digest parsing failed")) return } @@ -221,7 +220,7 @@ func (buh *blobUploadHandler) PutBlobUploadComplete(w http.ResponseWriter, r *ht if err != nil { switch err := err.(type) { case distribution.ErrBlobInvalidDigest: - buh.Errors = append(buh.Errors, v2.ErrorCodeDigestInvalid.WithDetail(err)) + buh.Errors = append(buh.Errors, errcode.ErrorCodeDigestInvalid.WithDetail(err)) case errcode.Error: buh.Errors = append(buh.Errors, err) default: @@ -231,7 +230,7 @@ func (buh *blobUploadHandler) PutBlobUploadComplete(w http.ResponseWriter, r *ht case distribution.ErrUnsupported: buh.Errors = append(buh.Errors, errcode.ErrorCodeUnsupported) case distribution.ErrBlobInvalidLength, distribution.ErrBlobDigestUnsupported: - buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadInvalid.WithDetail(err)) + buh.Errors = append(buh.Errors, errcode.ErrorCodeBlobUploadInvalid.WithDetail(err)) default: dcontext.GetLogger(buh).Errorf("unknown error completing upload: %v", err) buh.Errors = append(buh.Errors, errcode.ErrorCodeUnknown.WithDetail(err)) @@ -256,7 +255,7 @@ func (buh *blobUploadHandler) PutBlobUploadComplete(w http.ResponseWriter, r *ht // CancelBlobUpload cancels an in-progress upload of a blob. func (buh *blobUploadHandler) CancelBlobUpload(w http.ResponseWriter, r *http.Request) { if buh.Upload == nil { - buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadUnknown) + buh.Errors = append(buh.Errors, errcode.ErrorCodeBlobUploadUnknown) return } defer buh.Upload.Close() @@ -275,7 +274,7 @@ func (buh *blobUploadHandler) ResumeBlobUpload(ctx *Context, r *http.Request) ht if err != nil { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { dcontext.GetLogger(ctx).Infof("error resolving upload: %v", err) - buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadInvalid.WithDetail(err)) + buh.Errors = append(buh.Errors, errcode.ErrorCodeBlobUploadInvalid.WithDetail(err)) }) } buh.State = state @@ -283,14 +282,14 @@ func (buh *blobUploadHandler) ResumeBlobUpload(ctx *Context, r *http.Request) ht if state.Name != ctx.Repository.Named().Name() { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { dcontext.GetLogger(ctx).Infof("mismatched repository name in upload state: %q != %q", state.Name, buh.Repository.Named().Name()) - buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadInvalid.WithDetail(err)) + buh.Errors = append(buh.Errors, errcode.ErrorCodeBlobUploadInvalid.WithDetail(err)) }) } if state.UUID != buh.UUID { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { dcontext.GetLogger(ctx).Infof("mismatched uuid in upload state: %q != %q", state.UUID, buh.UUID) - buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadInvalid.WithDetail(err)) + buh.Errors = append(buh.Errors, errcode.ErrorCodeBlobUploadInvalid.WithDetail(err)) }) } @@ -300,7 +299,7 @@ func (buh *blobUploadHandler) ResumeBlobUpload(ctx *Context, r *http.Request) ht dcontext.GetLogger(ctx).Errorf("error resolving upload: %v", err) if err == distribution.ErrBlobUploadUnknown { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadUnknown.WithDetail(err)) + buh.Errors = append(buh.Errors, errcode.ErrorCodeBlobUploadUnknown.WithDetail(err)) }) } @@ -313,7 +312,7 @@ func (buh *blobUploadHandler) ResumeBlobUpload(ctx *Context, r *http.Request) ht if size := upload.Size(); size != buh.State.Offset { dcontext.GetLogger(ctx).Errorf("upload resumed at wrong offset: %d != %d", size, buh.State.Offset) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - buh.Errors = append(buh.Errors, v2.ErrorCodeRangeInvalid.WithDetail(err)) + buh.Errors = append(buh.Errors, errcode.ErrorCodeRangeInvalid.WithDetail(err)) }) } return nil diff --git a/registry/handlers/catalog.go b/registry/handlers/catalog.go index 0e6338789..a6a1da0e3 100644 --- a/registry/handlers/catalog.go +++ b/registry/handlers/catalog.go @@ -9,7 +9,6 @@ import ( "strconv" "github.com/distribution/distribution/v3/registry/api/errcode" - v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/registry/storage/driver" "github.com/gorilla/handlers" ) @@ -47,13 +46,13 @@ func (ch *catalogHandler) GetCatalog(w http.ResponseWriter, r *http.Request) { if n := q.Get("n"); n != "" { parsedMax, err := strconv.Atoi(n) if err != nil || parsedMax < 0 { - ch.Errors = append(ch.Errors, v2.ErrorCodePaginationNumberInvalid.WithDetail(map[string]string{"n": n})) + ch.Errors = append(ch.Errors, errcode.ErrorCodePaginationNumberInvalid.WithDetail(map[string]string{"n": n})) return } // if a client requests more than it's allowed to receive if parsedMax > maximumConfiguredEntries { - ch.Errors = append(ch.Errors, v2.ErrorCodePaginationNumberInvalid.WithDetail(map[string]int{"n": parsedMax})) + ch.Errors = append(ch.Errors, errcode.ErrorCodePaginationNumberInvalid.WithDetail(map[string]int{"n": parsedMax})) return } entries = parsedMax diff --git a/registry/handlers/manifests.go b/registry/handlers/manifests.go index c5e2d0647..d7bbab926 100644 --- a/registry/handlers/manifests.go +++ b/registry/handlers/manifests.go @@ -13,7 +13,6 @@ import ( "github.com/distribution/distribution/v3/manifest/ocischema" "github.com/distribution/distribution/v3/manifest/schema2" "github.com/distribution/distribution/v3/registry/api/errcode" - v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/registry/auth" "github.com/distribution/distribution/v3/registry/storage/driver" "github.com/distribution/reference" @@ -119,7 +118,7 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request) desc, err := tags.Get(imh, imh.Tag) if err != nil { if _, ok := err.(distribution.ErrTagUnknown); ok { - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err)) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestUnknown.WithDetail(err)) } else { imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err)) } @@ -140,7 +139,7 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request) manifest, err := manifests.Get(imh, imh.Digest, options...) if err != nil { if _, ok := err.(distribution.ErrManifestUnknownRevision); ok { - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err)) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestUnknown.WithDetail(err)) } else { imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err)) } @@ -160,11 +159,11 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request) } if manifestType == ociSchema && !supports[ociSchema] { - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithMessage("OCI manifest found, but accept header does not support OCI manifests")) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestUnknown.WithMessage("OCI manifest found, but accept header does not support OCI manifests")) return } if manifestType == ociImageIndexSchema && !supports[ociImageIndexSchema] { - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithMessage("OCI index found, but accept header does not support OCI indexes")) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestUnknown.WithMessage("OCI index found, but accept header does not support OCI indexes")) return } @@ -183,14 +182,14 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request) } if manifestDigest == "" { - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestUnknown) return } manifest, err = manifests.Get(imh, manifestDigest) if err != nil { if _, ok := err.(distribution.ErrManifestUnknownRevision); ok { - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err)) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestUnknown.WithDetail(err)) } else { imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err)) } @@ -198,7 +197,7 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request) } if _, isSchema2 := manifest.(*schema2.DeserializedManifest); isSchema2 && !supports[manifestSchema2] { - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithMessage("Schema 2 manifest not supported by client")) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestInvalid.WithMessage("Schema 2 manifest not supported by client")) return } else { imh.Digest = manifestDigest @@ -238,27 +237,27 @@ func (imh *manifestHandler) PutManifest(w http.ResponseWriter, r *http.Request) var jsonBuf bytes.Buffer if err := copyFullPayload(imh, w, r, &jsonBuf, maxManifestBodySize, "image manifest PUT"); err != nil { // copyFullPayload reports the error if necessary - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err.Error())) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestInvalid.WithDetail(err.Error())) return } mediaType := r.Header.Get("Content-Type") manifest, desc, err := distribution.UnmarshalManifest(mediaType, jsonBuf.Bytes()) if err != nil { - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err)) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestInvalid.WithDetail(err)) return } if imh.Digest != "" { if desc.Digest != imh.Digest { dcontext.GetLogger(imh).Errorf("payload digest does not match: %q != %q", desc.Digest, imh.Digest) - imh.Errors = append(imh.Errors, v2.ErrorCodeDigestInvalid) + imh.Errors = append(imh.Errors, errcode.ErrorCodeDigestInvalid) return } } else if imh.Tag != "" { imh.Digest = desc.Digest } else { - imh.Errors = append(imh.Errors, v2.ErrorCodeTagInvalid.WithDetail("no tag or digest specified")) + imh.Errors = append(imh.Errors, errcode.ErrorCodeTagInvalid.WithDetail("no tag or digest specified")) return } @@ -297,14 +296,14 @@ func (imh *manifestHandler) PutManifest(w http.ResponseWriter, r *http.Request) for _, verificationError := range err { switch verificationError := verificationError.(type) { case distribution.ErrManifestBlobUnknown: - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestBlobUnknown.WithDetail(verificationError.Digest)) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestBlobUnknown.WithDetail(verificationError.Digest)) case distribution.ErrManifestNameInvalid: - imh.Errors = append(imh.Errors, v2.ErrorCodeNameInvalid.WithDetail(err)) + imh.Errors = append(imh.Errors, errcode.ErrorCodeNameInvalid.WithDetail(err)) case distribution.ErrManifestUnverified: - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnverified) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestUnverified) default: if verificationError == digest.ErrDigestInvalidFormat { - imh.Errors = append(imh.Errors, v2.ErrorCodeDigestInvalid) + imh.Errors = append(imh.Errors, errcode.ErrorCodeDigestInvalid) } else { imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown, verificationError) } @@ -434,7 +433,7 @@ func (imh *manifestHandler) DeleteManifest(w http.ResponseWriter, r *http.Reques if err := tagService.Untag(imh.Context, imh.Tag); err != nil { switch err.(type) { case distribution.ErrTagUnknown, driver.PathNotFoundError: - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err)) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestUnknown.WithDetail(err)) default: imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err)) } @@ -455,10 +454,10 @@ func (imh *manifestHandler) DeleteManifest(w http.ResponseWriter, r *http.Reques switch err { case digest.ErrDigestUnsupported: case digest.ErrDigestInvalidFormat: - imh.Errors = append(imh.Errors, v2.ErrorCodeDigestInvalid) + imh.Errors = append(imh.Errors, errcode.ErrorCodeDigestInvalid) return case distribution.ErrBlobUnknown: - imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown) + imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestUnknown) return case distribution.ErrUnsupported: imh.Errors = append(imh.Errors, errcode.ErrorCodeUnsupported) diff --git a/registry/handlers/tags.go b/registry/handlers/tags.go index 035b326f4..ec0eb2337 100644 --- a/registry/handlers/tags.go +++ b/registry/handlers/tags.go @@ -8,7 +8,6 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/registry/api/errcode" - v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/gorilla/handlers" ) @@ -42,7 +41,7 @@ func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) { if err != nil { switch err := err.(type) { case distribution.ErrRepositoryUnknown: - th.Errors = append(th.Errors, v2.ErrorCodeNameUnknown.WithDetail(map[string]string{"name": th.Repository.Named().Name()})) + th.Errors = append(th.Errors, errcode.ErrorCodeNameUnknown.WithDetail(map[string]string{"name": th.Repository.Named().Name()})) case errcode.Error: th.Errors = append(th.Errors, err) default: @@ -71,7 +70,7 @@ func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) { if n := q.Get("n"); n != "" { maxEntries, err := strconv.Atoi(n) if err != nil || maxEntries < 0 { - th.Errors = append(th.Errors, v2.ErrorCodePaginationNumberInvalid.WithDetail(map[string]string{"n": n})) + th.Errors = append(th.Errors, errcode.ErrorCodePaginationNumberInvalid.WithDetail(map[string]string{"n": n})) return } From 0104adf4a8c0471244863bb9aa53089dc4cbfa74 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 4 Sep 2023 17:54:48 +0200 Subject: [PATCH 2/2] registry/api/errcode: split Register to internal / exported Use the non-exported function to all errors; there's currently no external consumers of this function (perhaps it should be deprecated). Signed-off-by: Sebastiaan van Stijn --- registry/api/errcode/errors_test.go | 6 ++-- registry/api/errcode/register.go | 46 ++++++++++++++++------------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/registry/api/errcode/errors_test.go b/registry/api/errcode/errors_test.go index 105d9f7ce..dacdb6a2c 100644 --- a/registry/api/errcode/errors_test.go +++ b/registry/api/errcode/errors_test.go @@ -10,21 +10,21 @@ import ( // TestErrorsManagement does a quick check of the Errors type to ensure that // members are properly pushed and marshaled. -var ErrorCodeTest1 = Register("test.errors", ErrorDescriptor{ +var ErrorCodeTest1 = register("test.errors", ErrorDescriptor{ Value: "TEST1", Message: "test error 1", Description: `Just a test message #1.`, HTTPStatusCode: http.StatusInternalServerError, }) -var ErrorCodeTest2 = Register("test.errors", ErrorDescriptor{ +var ErrorCodeTest2 = register("test.errors", ErrorDescriptor{ Value: "TEST2", Message: "test error 2", Description: `Just a test message #2.`, HTTPStatusCode: http.StatusNotFound, }) -var ErrorCodeTest3 = Register("test.errors", ErrorDescriptor{ +var ErrorCodeTest3 = register("test.errors", ErrorDescriptor{ Value: "TEST3", Message: "Sorry %q isn't valid", Description: `Just a test message #3.`, diff --git a/registry/api/errcode/register.go b/registry/api/errcode/register.go index 49bbf0f82..6030c36a5 100644 --- a/registry/api/errcode/register.go +++ b/registry/api/errcode/register.go @@ -16,7 +16,7 @@ var ( var ( // ErrorCodeUnknown is a generic error that can be used as a last // resort if there is no situation-specific error message that can be used - ErrorCodeUnknown = Register("errcode", ErrorDescriptor{ + ErrorCodeUnknown = register("errcode", ErrorDescriptor{ Value: "UNKNOWN", Message: "unknown error", Description: `Generic error returned when the error does not have an @@ -25,7 +25,7 @@ var ( }) // ErrorCodeUnsupported is returned when an operation is not supported. - ErrorCodeUnsupported = Register("errcode", ErrorDescriptor{ + ErrorCodeUnsupported = register("errcode", ErrorDescriptor{ Value: "UNSUPPORTED", Message: "The operation is unsupported.", Description: `The operation was unsupported due to a missing @@ -35,7 +35,7 @@ var ( // ErrorCodeUnauthorized is returned if a request requires // authentication. - ErrorCodeUnauthorized = Register("errcode", ErrorDescriptor{ + ErrorCodeUnauthorized = register("errcode", ErrorDescriptor{ Value: "UNAUTHORIZED", Message: "authentication required", Description: `The access controller was unable to authenticate @@ -47,7 +47,7 @@ var ( // ErrorCodeDenied is returned if a client does not have sufficient // permission to perform an action. - ErrorCodeDenied = Register("errcode", ErrorDescriptor{ + ErrorCodeDenied = register("errcode", ErrorDescriptor{ Value: "DENIED", Message: "requested access to the resource is denied", Description: `The access controller denied access for the @@ -57,7 +57,7 @@ var ( // ErrorCodeUnavailable provides a common error to report unavailability // of a service or endpoint. - ErrorCodeUnavailable = Register("errcode", ErrorDescriptor{ + ErrorCodeUnavailable = register("errcode", ErrorDescriptor{ Value: "UNAVAILABLE", Message: "service unavailable", Description: "Returned when a service is not available", @@ -66,7 +66,7 @@ var ( // ErrorCodeTooManyRequests is returned if a client attempts too many // times to contact a service endpoint. - ErrorCodeTooManyRequests = Register("errcode", ErrorDescriptor{ + ErrorCodeTooManyRequests = register("errcode", ErrorDescriptor{ Value: "TOOMANYREQUESTS", Message: "too many requests", Description: `Returned when a client attempts to contact a @@ -80,7 +80,7 @@ const errGroup = "registry.api.v2" var ( // ErrorCodeDigestInvalid is returned when uploading a blob if the // provided digest does not match the blob contents. - ErrorCodeDigestInvalid = Register(errGroup, ErrorDescriptor{ + ErrorCodeDigestInvalid = register(errGroup, ErrorDescriptor{ Value: "DIGEST_INVALID", Message: "provided digest did not match uploaded content", Description: `When a blob is uploaded, the registry will check that @@ -92,7 +92,7 @@ var ( }) // ErrorCodeSizeInvalid is returned when uploading a blob if the provided - ErrorCodeSizeInvalid = Register(errGroup, ErrorDescriptor{ + ErrorCodeSizeInvalid = register(errGroup, ErrorDescriptor{ Value: "SIZE_INVALID", Message: "provided length did not match content length", Description: `When a layer is uploaded, the provided size will be @@ -103,7 +103,7 @@ var ( // ErrorCodeRangeInvalid is returned when uploading a blob if the provided // content range is invalid. - ErrorCodeRangeInvalid = Register(errGroup, ErrorDescriptor{ + ErrorCodeRangeInvalid = register(errGroup, ErrorDescriptor{ Value: "RANGE_INVALID", Message: "invalid content range", Description: `When a layer is uploaded, the provided range is checked @@ -114,7 +114,7 @@ var ( // ErrorCodeNameInvalid is returned when the name in the manifest does not // match the provided name. - ErrorCodeNameInvalid = Register(errGroup, ErrorDescriptor{ + ErrorCodeNameInvalid = register(errGroup, ErrorDescriptor{ Value: "NAME_INVALID", Message: "invalid repository name", Description: `Invalid repository name encountered either during @@ -124,7 +124,7 @@ var ( // ErrorCodeTagInvalid is returned when the tag in the manifest does not // match the provided tag. - ErrorCodeTagInvalid = Register(errGroup, ErrorDescriptor{ + ErrorCodeTagInvalid = register(errGroup, ErrorDescriptor{ Value: "TAG_INVALID", Message: "manifest tag did not match URI", Description: `During a manifest upload, if the tag in the manifest @@ -133,7 +133,7 @@ var ( }) // ErrorCodeNameUnknown when the repository name is not known. - ErrorCodeNameUnknown = Register(errGroup, ErrorDescriptor{ + ErrorCodeNameUnknown = register(errGroup, ErrorDescriptor{ Value: "NAME_UNKNOWN", Message: "repository name not known to registry", Description: `This is returned if the name used during an operation is @@ -142,7 +142,7 @@ var ( }) // ErrorCodeManifestUnknown returned when image manifest is unknown. - ErrorCodeManifestUnknown = Register(errGroup, ErrorDescriptor{ + ErrorCodeManifestUnknown = register(errGroup, ErrorDescriptor{ Value: "MANIFEST_UNKNOWN", Message: "manifest unknown", Description: `This error is returned when the manifest, identified by @@ -153,7 +153,7 @@ var ( // 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 = Register(errGroup, ErrorDescriptor{ + ErrorCodeManifestInvalid = register(errGroup, ErrorDescriptor{ Value: "MANIFEST_INVALID", Message: "manifest invalid", Description: `During upload, manifests undergo several checks ensuring @@ -165,7 +165,7 @@ var ( // ErrorCodeManifestUnverified is returned when the manifest fails // signature verification. - ErrorCodeManifestUnverified = Register(errGroup, ErrorDescriptor{ + ErrorCodeManifestUnverified = register(errGroup, ErrorDescriptor{ Value: "MANIFEST_UNVERIFIED", Message: "manifest failed signature verification", Description: `During manifest upload, if the manifest fails signature @@ -175,7 +175,7 @@ var ( // ErrorCodeManifestBlobUnknown is returned when a manifest blob is // unknown to the registry. - ErrorCodeManifestBlobUnknown = Register(errGroup, ErrorDescriptor{ + ErrorCodeManifestBlobUnknown = register(errGroup, ErrorDescriptor{ Value: "MANIFEST_BLOB_UNKNOWN", Message: "blob unknown to registry", Description: `This error may be returned when a manifest blob is @@ -186,7 +186,7 @@ var ( // 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 = Register(errGroup, ErrorDescriptor{ + ErrorCodeBlobUnknown = register(errGroup, ErrorDescriptor{ Value: "BLOB_UNKNOWN", Message: "blob unknown to registry", Description: `This error may be returned when a blob is unknown to the @@ -197,7 +197,7 @@ var ( }) // ErrorCodeBlobUploadUnknown is returned when an upload is unknown. - ErrorCodeBlobUploadUnknown = Register(errGroup, ErrorDescriptor{ + ErrorCodeBlobUploadUnknown = register(errGroup, ErrorDescriptor{ Value: "BLOB_UPLOAD_UNKNOWN", Message: "blob upload unknown to registry", Description: `If a blob upload has been cancelled or was never @@ -206,7 +206,7 @@ var ( }) // ErrorCodeBlobUploadInvalid is returned when an upload is invalid. - ErrorCodeBlobUploadInvalid = Register(errGroup, ErrorDescriptor{ + ErrorCodeBlobUploadInvalid = register(errGroup, ErrorDescriptor{ Value: "BLOB_UPLOAD_INVALID", Message: "blob upload invalid", Description: `The blob upload encountered an error and can no @@ -216,7 +216,7 @@ var ( // ErrorCodePaginationNumberInvalid is returned when the `n` parameter is // not an integer, or `n` is negative. - ErrorCodePaginationNumberInvalid = Register(errGroup, ErrorDescriptor{ + ErrorCodePaginationNumberInvalid = register(errGroup, ErrorDescriptor{ Value: "PAGINATION_NUMBER_INVALID", Message: "invalid number of results requested", Description: `Returned when the "n" parameter (number of results @@ -234,6 +234,12 @@ var ( // Register will make the passed-in error known to the environment and // return a new ErrorCode func Register(group string, descriptor ErrorDescriptor) ErrorCode { + return register(group, descriptor) +} + +// register will make the passed-in error known to the environment and +// return a new ErrorCode +func register(group string, descriptor ErrorDescriptor) ErrorCode { registerLock.Lock() defer registerLock.Unlock()