mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
feat: propagate detailed error msgs to client (OCI dist-spec format) (#1681)
Signed-off-by: Alexei Dodon <adodon@cisco.com>
This commit is contained in:
parent
94429a82df
commit
247f6dcd3f
10 changed files with 292 additions and 200 deletions
2
.github/workflows/oci-conformance-action.yml
vendored
2
.github/workflows/oci-conformance-action.yml
vendored
|
@ -36,8 +36,6 @@ jobs:
|
||||||
echo "SERVER_URL=http://${IP}:8080" >> $GITHUB_ENV
|
echo "SERVER_URL=http://${IP}:8080" >> $GITHUB_ENV
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
# TODO: change to upstream once the foloowing PR is merged:
|
|
||||||
# https://github.com/opencontainers/distribution-spec/pull/436
|
|
||||||
repository: opencontainers/distribution-spec
|
repository: opencontainers/distribution-spec
|
||||||
ref: main
|
ref: main
|
||||||
path: distribution-spec
|
path: distribution-spec
|
||||||
|
|
|
@ -1,16 +1,57 @@
|
||||||
package errors
|
package errors
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
err error
|
||||||
|
details map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return e.err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Is(target error) bool {
|
||||||
|
return errors.Is(e.err, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) AddDetail(key, value string) *Error {
|
||||||
|
e.details[key] = value
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) GetDetails() map[string]string {
|
||||||
|
return e.details
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewError(err error) *Error {
|
||||||
|
return &Error{
|
||||||
|
err: err,
|
||||||
|
details: GetDetails(err), // preserve details if chained error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDetails(err error) map[string]string {
|
||||||
|
var internalErr *Error
|
||||||
|
details := make(map[string]string)
|
||||||
|
|
||||||
|
if errors.As(err, &internalErr) {
|
||||||
|
details = internalErr.GetDetails()
|
||||||
|
}
|
||||||
|
|
||||||
|
return details
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrBadConfig = errors.New("config: invalid config")
|
ErrBadConfig = errors.New("config: invalid config")
|
||||||
ErrCliBadConfig = errors.New("cli: bad config")
|
ErrCliBadConfig = errors.New("cli: bad config")
|
||||||
ErrRepoNotFound = errors.New("repository: not found")
|
ErrRepoNotFound = errors.New("repository: not found")
|
||||||
ErrRepoIsNotDir = errors.New("repository: not a directory")
|
|
||||||
ErrRepoBadVersion = errors.New("repository: unsupported layout version")
|
ErrRepoBadVersion = errors.New("repository: unsupported layout version")
|
||||||
ErrManifestNotFound = errors.New("manifest: not found")
|
ErrManifestNotFound = errors.New("manifest: not found")
|
||||||
ErrBadManifest = errors.New("manifest: invalid contents")
|
ErrBadManifest = errors.New("manifest: invalid contents")
|
||||||
ErrBadIndex = errors.New("index: invalid contents")
|
|
||||||
ErrUploadNotFound = errors.New("uploads: not found")
|
ErrUploadNotFound = errors.New("uploads: not found")
|
||||||
ErrBadUploadRange = errors.New("uploads: bad range")
|
ErrBadUploadRange = errors.New("uploads: bad range")
|
||||||
ErrBlobNotFound = errors.New("blob: not found")
|
ErrBlobNotFound = errors.New("blob: not found")
|
||||||
|
|
|
@ -36,7 +36,7 @@ import (
|
||||||
"zotregistry.io/zot/pkg/api/config"
|
"zotregistry.io/zot/pkg/api/config"
|
||||||
"zotregistry.io/zot/pkg/api/constants"
|
"zotregistry.io/zot/pkg/api/constants"
|
||||||
apiErr "zotregistry.io/zot/pkg/api/errors"
|
apiErr "zotregistry.io/zot/pkg/api/errors"
|
||||||
"zotregistry.io/zot/pkg/common"
|
zcommon "zotregistry.io/zot/pkg/common"
|
||||||
"zotregistry.io/zot/pkg/log"
|
"zotregistry.io/zot/pkg/log"
|
||||||
localCtx "zotregistry.io/zot/pkg/requestcontext"
|
localCtx "zotregistry.io/zot/pkg/requestcontext"
|
||||||
storageConstants "zotregistry.io/zot/pkg/storage/constants"
|
storageConstants "zotregistry.io/zot/pkg/storage/constants"
|
||||||
|
@ -463,7 +463,7 @@ func bearerAuthHandler(ctlr *Controller) mux.MiddlewareFunc {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctlr.Log.Error().Err(err).Msg("issue parsing Authorization header")
|
ctlr.Log.Error().Err(err).Msg("issue parsing Authorization header")
|
||||||
response.Header().Set("Content-Type", "application/json")
|
response.Header().Set("Content-Type", "application/json")
|
||||||
common.WriteJSON(response, http.StatusInternalServerError, apiErr.NewErrorList(apiErr.NewError(apiErr.UNSUPPORTED)))
|
zcommon.WriteJSON(response, http.StatusInternalServerError, apiErr.NewError(apiErr.UNSUPPORTED))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -472,8 +472,7 @@ func bearerAuthHandler(ctlr *Controller) mux.MiddlewareFunc {
|
||||||
response.Header().Set("Content-Type", "application/json")
|
response.Header().Set("Content-Type", "application/json")
|
||||||
response.Header().Set("WWW-Authenticate", permissions.WWWAuthenticateHeader)
|
response.Header().Set("WWW-Authenticate", permissions.WWWAuthenticateHeader)
|
||||||
|
|
||||||
common.WriteJSON(response, http.StatusUnauthorized,
|
zcommon.WriteJSON(response, http.StatusUnauthorized, apiErr.NewError(apiErr.UNAUTHORIZED))
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.UNAUTHORIZED)))
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -591,7 +590,7 @@ func getRelyingPartyArgs(cfg *config.Config, provider string) (
|
||||||
|
|
||||||
scopes := cfg.HTTP.Auth.OpenID.Providers[provider].Scopes
|
scopes := cfg.HTTP.Auth.OpenID.Providers[provider].Scopes
|
||||||
// openid scope must be the first one in list
|
// openid scope must be the first one in list
|
||||||
if !common.Contains(scopes, oidc.ScopeOpenID) && config.IsOpenIDSupported(provider) {
|
if !zcommon.Contains(scopes, oidc.ScopeOpenID) && config.IsOpenIDSupported(provider) {
|
||||||
scopes = append([]string{oidc.ScopeOpenID}, scopes...)
|
scopes = append([]string{oidc.ScopeOpenID}, scopes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,7 +662,7 @@ func authFail(w http.ResponseWriter, r *http.Request, realm string, delay int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
common.WriteJSON(w, http.StatusUnauthorized, apiErr.NewErrorList(apiErr.NewError(apiErr.UNAUTHORIZED)))
|
zcommon.WriteJSON(w, http.StatusUnauthorized, apiErr.NewError(apiErr.UNAUTHORIZED))
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAuthorizationHeaderEmpty(request *http.Request) bool {
|
func isAuthorizationHeaderEmpty(request *http.Request) bool {
|
||||||
|
|
|
@ -7,14 +7,8 @@ import (
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Description string `json:"description"`
|
Description string `json:"-"`
|
||||||
Detail interface{} `json:"detail,omitempty"`
|
Detail map[string]string `json:"detail"`
|
||||||
}
|
|
||||||
|
|
||||||
func (e Error) WithMessage(msg string) Error {
|
|
||||||
e.Message = msg
|
|
||||||
|
|
||||||
return e
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrorList struct {
|
type ErrorList struct {
|
||||||
|
@ -66,7 +60,7 @@ func (e ErrorCode) String() string {
|
||||||
return errMap[e]
|
return errMap[e]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewError(code ErrorCode, detail ...interface{}) Error {
|
func NewError(code ErrorCode) *Error {
|
||||||
errMap := map[ErrorCode]Error{
|
errMap := map[ErrorCode]Error{
|
||||||
BLOB_UNKNOWN: {
|
BLOB_UNKNOWN: {
|
||||||
Message: "blob unknown to registry",
|
Message: "blob unknown to registry",
|
||||||
|
@ -77,12 +71,12 @@ func NewError(code ErrorCode, detail ...interface{}) Error {
|
||||||
|
|
||||||
BLOB_UPLOAD_INVALID: {
|
BLOB_UPLOAD_INVALID: {
|
||||||
Message: "blob upload invalid",
|
Message: "blob upload invalid",
|
||||||
Description: `The blob upload encountered an error and can no longer proceed.`,
|
Description: "The blob upload encountered an error and can no longer proceed.",
|
||||||
},
|
},
|
||||||
|
|
||||||
BLOB_UPLOAD_UNKNOWN: {
|
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 started, this error code MAY be returned.`,
|
Description: "If a blob upload has been cancelled or was never started, this error code MAY be returned.",
|
||||||
},
|
},
|
||||||
|
|
||||||
DIGEST_INVALID: {
|
DIGEST_INVALID: {
|
||||||
|
@ -95,70 +89,67 @@ func NewError(code ErrorCode, detail ...interface{}) Error {
|
||||||
|
|
||||||
MANIFEST_BLOB_UNKNOWN: {
|
MANIFEST_BLOB_UNKNOWN: {
|
||||||
Message: "blob unknown to registry",
|
Message: "blob unknown to registry",
|
||||||
Description: `This error MAY be returned when a manifest blob is unknown
|
Description: "This error MAY be returned when a manifest blob is unknown to the registry.",
|
||||||
to the registry.`,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
MANIFEST_INVALID: {
|
MANIFEST_INVALID: {
|
||||||
Message: "manifest invalid",
|
Message: "manifest invalid",
|
||||||
Description: `During upload, manifests undergo several checks ensuring
|
Description: "During upload, manifests undergo several checks ensuring " +
|
||||||
validity. If those checks fail, this error MAY be returned, unless a more
|
"validity. If those checks fail, this error MAY be returned, unless a more " +
|
||||||
specific error is included. The detail will contain information the failed
|
"specific error is included. The detail will contain information the failed validation.",
|
||||||
validation.`,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
MANIFEST_UNKNOWN: {
|
MANIFEST_UNKNOWN: {
|
||||||
Message: "manifest unknown",
|
Message: "manifest unknown",
|
||||||
Description: `This error is returned when the manifest, identified by name
|
Description: "This error is returned when the manifest, identified by name " +
|
||||||
and tag is unknown to the repository.`,
|
"and tag is unknown to the repository.",
|
||||||
},
|
},
|
||||||
|
|
||||||
MANIFEST_UNVERIFIED: {
|
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.",
|
||||||
},
|
},
|
||||||
|
|
||||||
NAME_INVALID: {
|
NAME_INVALID: {
|
||||||
Message: "invalid repository name",
|
Message: "invalid repository name",
|
||||||
Description: `Invalid repository name encountered either during manifest
|
Description: "Invalid repository name encountered either during manifest " +
|
||||||
validation or any API operation.`,
|
"validation or any API operation.",
|
||||||
},
|
},
|
||||||
|
|
||||||
NAME_UNKNOWN: {
|
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 unknown to the registry.`,
|
Description: "This is returned if the name used during an operation is unknown to the registry.",
|
||||||
},
|
},
|
||||||
|
|
||||||
SIZE_INVALID: {
|
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 checked against the uploaded " +
|
Description: "When a layer is uploaded, the provided size will be checked against " +
|
||||||
"content. If they do not match, this error will be returned.",
|
"the uploaded content. If they do not match, this error will be returned.",
|
||||||
},
|
},
|
||||||
|
|
||||||
TAG_INVALID: {
|
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 does
|
Description: "During a manifest upload, if the tag in the manifest does " +
|
||||||
not match the uri tag, this error will be returned.`,
|
"not match the uri tag, this error will be returned.",
|
||||||
},
|
},
|
||||||
|
|
||||||
UNAUTHORIZED: {
|
UNAUTHORIZED: {
|
||||||
Message: "authentication required",
|
Message: "authentication required",
|
||||||
Description: `The access controller was unable to authenticate the client.
|
Description: "The access controller was unable to authenticate the client." +
|
||||||
Often this will be accompanied by a Www-Authenticate HTTP response header
|
"Often this will be accompanied by a Www-Authenticate HTTP response header " +
|
||||||
indicating how to authenticate.`,
|
"indicating how to authenticate.",
|
||||||
},
|
},
|
||||||
|
|
||||||
DENIED: {
|
DENIED: {
|
||||||
Message: "requested access to the resource is denied",
|
Message: "requested access to the resource is denied",
|
||||||
Description: `The access controller denied access for the operation on a
|
Description: "The access controller denied access for the operation on a resource.",
|
||||||
resource.`,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
UNSUPPORTED: {
|
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.",
|
||||||
},
|
},
|
||||||
|
|
||||||
INVALID_INDEX: {
|
INVALID_INDEX: {
|
||||||
|
@ -173,19 +164,24 @@ func NewError(code ErrorCode, detail ...interface{}) Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
err.Code = code.String()
|
err.Code = code.String()
|
||||||
err.Detail = detail
|
err.Detail = map[string]string{
|
||||||
|
"description": err.Description,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *Error) AddDetail(m map[string]string) *Error {
|
||||||
|
for k, v := range m {
|
||||||
|
err.Detail[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewErrorList(errors ...Error) ErrorList {
|
func NewErrorList(errors ...*Error) ErrorList {
|
||||||
errList := make([]*Error, 0)
|
var errList []*Error
|
||||||
err := Error{}
|
errList = append(errList, errors...)
|
||||||
|
|
||||||
for _, e := range errors {
|
|
||||||
err = e
|
|
||||||
errList = append(errList, &err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrorList{errList}
|
return ErrorList{errList}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,6 @@ import (
|
||||||
|
|
||||||
func TestUnknownCodeError(t *testing.T) {
|
func TestUnknownCodeError(t *testing.T) {
|
||||||
Convey("Retrieve a new error with unknown code", t, func() {
|
Convey("Retrieve a new error with unknown code", t, func() {
|
||||||
So(func() { _ = apiErr.NewError(123456789, nil) }, ShouldPanic)
|
So(func() { _ = apiErr.NewError(123456789) }, ShouldPanic)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -334,8 +334,8 @@ func (rh *RouteHandler) ListTags(response http.ResponseWriter, request *http.Req
|
||||||
|
|
||||||
tags, err := imgStore.GetImageTags(name)
|
tags, err := imgStore.GetImageTags(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(map[string]string{"name": name})
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -422,25 +422,27 @@ func (rh *RouteHandler) CheckManifest(response http.ResponseWriter, request *htt
|
||||||
|
|
||||||
reference, ok := vars["reference"]
|
reference, ok := vars["reference"]
|
||||||
if !ok || reference == "" {
|
if !ok || reference == "" {
|
||||||
zcommon.WriteJSON(response,
|
e := apiErr.NewError(apiErr.MANIFEST_INVALID).AddDetail(map[string]string{"reference": reference})
|
||||||
http.StatusNotFound,
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_INVALID, map[string]string{"reference": reference})))
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
content, digest, mediaType, err := getImageManifest(rh, imgStore, name, reference) //nolint:contextcheck
|
content, digest, mediaType, err := getImageManifest(rh, imgStore, name, reference) //nolint:contextcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
|
details["reference"] = reference
|
||||||
|
|
||||||
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"reference": reference})))
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrManifestNotFound) {
|
} else if errors.Is(err, zerr.ErrManifestNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
e := apiErr.NewError(apiErr.MANIFEST_UNKNOWN).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
zcommon.WriteJSON(response, http.StatusInternalServerError,
|
e := apiErr.NewError(apiErr.MANIFEST_INVALID).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_INVALID, map[string]string{"reference": reference})))
|
zcommon.WriteJSON(response, http.StatusInternalServerError, apiErr.NewErrorList(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -490,24 +492,27 @@ func (rh *RouteHandler) GetManifest(response http.ResponseWriter, request *http.
|
||||||
|
|
||||||
reference, ok := vars["reference"]
|
reference, ok := vars["reference"]
|
||||||
if !ok || reference == "" {
|
if !ok || reference == "" {
|
||||||
zcommon.WriteJSON(response,
|
err := apiErr.NewError(apiErr.MANIFEST_UNKNOWN).AddDetail(map[string]string{"reference": reference})
|
||||||
http.StatusNotFound,
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(err))
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
content, digest, mediaType, err := getImageManifest(rh, imgStore, name, reference) //nolint: contextcheck
|
content, digest, mediaType, err := getImageManifest(rh, imgStore, name, reference) //nolint: contextcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrRepoBadVersion) {
|
} else if errors.Is(err, zerr.ErrRepoBadVersion) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrManifestNotFound) {
|
} else if errors.Is(err, zerr.ErrManifestNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["reference"] = reference
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
|
e := apiErr.NewError(apiErr.MANIFEST_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -655,18 +660,16 @@ func (rh *RouteHandler) UpdateManifest(response http.ResponseWriter, request *ht
|
||||||
|
|
||||||
reference, ok := vars["reference"]
|
reference, ok := vars["reference"]
|
||||||
if !ok || reference == "" {
|
if !ok || reference == "" {
|
||||||
zcommon.WriteJSON(response,
|
err := apiErr.NewError(apiErr.MANIFEST_INVALID).AddDetail(map[string]string{"reference": reference})
|
||||||
http.StatusNotFound,
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(err))
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_INVALID, map[string]string{"reference": reference})))
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaType := request.Header.Get("Content-Type")
|
mediaType := request.Header.Get("Content-Type")
|
||||||
if !storageCommon.IsSupportedMediaType(mediaType) {
|
if !storageCommon.IsSupportedMediaType(mediaType) {
|
||||||
// response.WriteHeader(http.StatusUnsupportedMediaType)
|
err := apiErr.NewError(apiErr.MANIFEST_INVALID).AddDetail(map[string]string{"mediaType": mediaType})
|
||||||
zcommon.WriteJSON(response, http.StatusUnsupportedMediaType,
|
zcommon.WriteJSON(response, http.StatusUnsupportedMediaType, apiErr.NewErrorList(err))
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_INVALID, map[string]string{"mediaType": mediaType})))
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -683,25 +686,31 @@ func (rh *RouteHandler) UpdateManifest(response http.ResponseWriter, request *ht
|
||||||
|
|
||||||
digest, subjectDigest, err := imgStore.PutImageManifest(name, reference, mediaType, body)
|
digest, subjectDigest, err := imgStore.PutImageManifest(name, reference, mediaType, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrManifestNotFound) {
|
} else if errors.Is(err, zerr.ErrManifestNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["reference"] = reference
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
|
e := apiErr.NewError(apiErr.MANIFEST_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrBadManifest) {
|
} else if errors.Is(err, zerr.ErrBadManifest) {
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details["reference"] = reference
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_INVALID, map[string]string{"reference": reference})))
|
e := apiErr.NewError(apiErr.MANIFEST_INVALID).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrBlobNotFound) {
|
} else if errors.Is(err, zerr.ErrBlobNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details["blob"] = digest.String()
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UNKNOWN, map[string]string{"blob": digest.String()})))
|
e := apiErr.NewError(apiErr.BLOB_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrRepoBadVersion) {
|
} else if errors.Is(err, zerr.ErrRepoBadVersion) {
|
||||||
zcommon.WriteJSON(response, http.StatusInternalServerError,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.INVALID_INDEX, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.INVALID_INDEX).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusInternalServerError, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrImageLintAnnotations) {
|
} else if errors.Is(err, zerr.ErrImageLintAnnotations) {
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details["reference"] = reference
|
||||||
apiErr.NewErrorList(apiErr.NewError(
|
e := apiErr.NewError(apiErr.MANIFEST_INVALID).AddDetail(details)
|
||||||
apiErr.MANIFEST_INVALID, map[string]string{"reference": reference}).WithMessage(err.Error())))
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
// could be syscall.EMFILE (Err:0x18 too many opened files), etc
|
// could be syscall.EMFILE (Err:0x18 too many opened files), etc
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error: performing cleanup")
|
rh.c.Log.Error().Err(err).Msg("unexpected error: performing cleanup")
|
||||||
|
@ -782,15 +791,19 @@ func (rh *RouteHandler) DeleteManifest(response http.ResponseWriter, request *ht
|
||||||
|
|
||||||
manifestBlob, manifestDigest, mediaType, err := imgStore.GetImageManifest(name, reference)
|
manifestBlob, manifestDigest, mediaType, err := imgStore.GetImageManifest(name, reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrManifestNotFound) {
|
} else if errors.Is(err, zerr.ErrManifestNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["reference"] = reference
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
|
e := apiErr.NewError(apiErr.MANIFEST_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrBadManifest) {
|
} else if errors.Is(err, zerr.ErrBadManifest) {
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details["reference"] = reference
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.UNSUPPORTED, map[string]string{"reference": reference})))
|
e := apiErr.NewError(apiErr.UNSUPPORTED).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -800,19 +813,24 @@ func (rh *RouteHandler) DeleteManifest(response http.ResponseWriter, request *ht
|
||||||
}
|
}
|
||||||
|
|
||||||
err = imgStore.DeleteImageManifest(name, reference, detectCollision)
|
err = imgStore.DeleteImageManifest(name, reference, detectCollision)
|
||||||
if err != nil {
|
if err != nil { //nolint: dupl
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrManifestNotFound) {
|
} else if errors.Is(err, zerr.ErrManifestNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["reference"] = reference
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
|
e := apiErr.NewError(apiErr.MANIFEST_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrManifestConflict) {
|
} else if errors.Is(err, zerr.ErrManifestConflict) {
|
||||||
zcommon.WriteJSON(response, http.StatusConflict,
|
details["reference"] = reference
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.MANIFEST_INVALID, map[string]string{"reference": reference})))
|
e := apiErr.NewError(apiErr.MANIFEST_INVALID).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusConflict, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrBadManifest) {
|
} else if errors.Is(err, zerr.ErrBadManifest) {
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details["reference"] = reference
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.UNSUPPORTED, map[string]string{"reference": reference})))
|
e := apiErr.NewError(apiErr.UNSUPPORTED).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -868,17 +886,19 @@ func (rh *RouteHandler) CheckBlob(response http.ResponseWriter, request *http.Re
|
||||||
|
|
||||||
ok, blen, err := imgStore.CheckBlob(name, digest)
|
ok, blen, err := imgStore.CheckBlob(name, digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrBadBlobDigest) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrBadBlobDigest) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response,
|
details["digest"] = digest.String()
|
||||||
http.StatusBadRequest,
|
e := apiErr.NewError(apiErr.DIGEST_INVALID).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.DIGEST_INVALID, map[string]string{"digest": digest.String()})))
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrBlobNotFound) {
|
} else if errors.Is(err, zerr.ErrBlobNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["digest"] = digest.String()
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UNKNOWN,
|
e := apiErr.NewError(apiErr.BLOB_UNKNOWN).AddDetail(details)
|
||||||
map[string]string{"digest": digest.String()})))
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -888,8 +908,8 @@ func (rh *RouteHandler) CheckBlob(response http.ResponseWriter, request *http.Re
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UNKNOWN,
|
e := apiErr.NewError(apiErr.BLOB_UNKNOWN).AddDetail(map[string]string{"digest": digest.String()})
|
||||||
map[string]string{"digest": digest.String()})))
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1018,18 +1038,19 @@ func (rh *RouteHandler) GetBlob(response http.ResponseWriter, request *http.Requ
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrBadBlobDigest) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrBadBlobDigest) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response,
|
details["digest"] = digest.String()
|
||||||
http.StatusBadRequest,
|
e := apiErr.NewError(apiErr.DIGEST_INVALID).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.DIGEST_INVALID, map[string]string{"digest": digest.String()})))
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
zcommon.WriteJSON(response,
|
details["name"] = name
|
||||||
http.StatusNotFound,
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrBlobNotFound) {
|
} else if errors.Is(err, zerr.ErrBlobNotFound) {
|
||||||
zcommon.WriteJSON(response,
|
details["digest"] = digest.String()
|
||||||
http.StatusNotFound,
|
e := apiErr.NewError(apiErr.BLOB_UNKNOWN).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UNKNOWN, map[string]string{"digest": digest.String()})))
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -1087,22 +1108,23 @@ func (rh *RouteHandler) DeleteBlob(response http.ResponseWriter, request *http.R
|
||||||
|
|
||||||
err = imgStore.DeleteBlob(name, digest)
|
err = imgStore.DeleteBlob(name, digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrBadBlobDigest) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrBadBlobDigest) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response,
|
details["digest"] = digest.String()
|
||||||
http.StatusBadRequest,
|
e := apiErr.NewError(apiErr.DIGEST_INVALID).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.DIGEST_INVALID, map[string]string{"digest": digest.String()})))
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
zcommon.WriteJSON(response,
|
details["name"] = name
|
||||||
http.StatusNotFound,
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(map[string]string{"name": name})
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrBlobNotFound) {
|
} else if errors.Is(err, zerr.ErrBlobNotFound) {
|
||||||
zcommon.WriteJSON(response,
|
details["digest"] = digest.String()
|
||||||
http.StatusNotFound,
|
e := apiErr.NewError(apiErr.BLOB_UNKNOWN).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UNKNOWN, map[string]string{".String()": digest.String()})))
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrBlobReferenced) {
|
} else if errors.Is(err, zerr.ErrBlobReferenced) {
|
||||||
zcommon.WriteJSON(response,
|
details["digest"] = digest.String()
|
||||||
http.StatusMethodNotAllowed,
|
e := apiErr.NewError(apiErr.DENIED).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.DENIED, map[string]string{".String()": digest.String()})))
|
zcommon.WriteJSON(response, http.StatusMethodNotAllowed, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -1154,9 +1176,11 @@ func (rh *RouteHandler) CreateBlobUpload(response http.ResponseWriter, request *
|
||||||
if err != nil {
|
if err != nil {
|
||||||
upload, err := imgStore.NewBlobUpload(name)
|
upload, err := imgStore.NewBlobUpload(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrRepoNotFound) {
|
if errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -1211,8 +1235,15 @@ func (rh *RouteHandler) CreateBlobUpload(response http.ResponseWriter, request *
|
||||||
contentLength, err := strconv.ParseInt(request.Header.Get("Content-Length"), 10, 64)
|
contentLength, err := strconv.ParseInt(request.Header.Get("Content-Length"), 10, 64)
|
||||||
if err != nil || contentLength <= 0 {
|
if err != nil || contentLength <= 0 {
|
||||||
rh.c.Log.Warn().Str("actual", request.Header.Get("Content-Length")).Msg("invalid content length")
|
rh.c.Log.Warn().Str("actual", request.Header.Get("Content-Length")).Msg("invalid content length")
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details := map[string]string{"digest": digest.String()}
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID, map[string]string{"digest": digest.String()})))
|
|
||||||
|
if err != nil {
|
||||||
|
details["conversion error"] = err.Error()
|
||||||
|
} else {
|
||||||
|
details["Content-Length"] = request.Header.Get("Content-Length")
|
||||||
|
}
|
||||||
|
e := apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1241,9 +1272,11 @@ func (rh *RouteHandler) CreateBlobUpload(response http.ResponseWriter, request *
|
||||||
|
|
||||||
upload, err := imgStore.NewBlobUpload(name)
|
upload, err := imgStore.NewBlobUpload(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrRepoNotFound) {
|
if errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -1291,18 +1324,20 @@ func (rh *RouteHandler) GetBlobUpload(response http.ResponseWriter, request *htt
|
||||||
|
|
||||||
size, err := imgStore.GetBlobUpload(name, sessionID)
|
size, err := imgStore.GetBlobUpload(name, sessionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, zerr.ErrBadUploadRange) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
details := zerr.GetDetails(err)
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
//nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID})))
|
if errors.Is(err, zerr.ErrBadUploadRange) || errors.Is(err, zerr.ErrBadBlobDigest) {
|
||||||
} else if errors.Is(err, zerr.ErrBadBlobDigest) {
|
details["session_id"] = sessionID
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
e := apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID).AddDetail(details)
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID})))
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrUploadNotFound) {
|
} else if errors.Is(err, zerr.ErrUploadNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["session_id"] = sessionID
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID})))
|
e := apiErr.NewError(apiErr.BLOB_UPLOAD_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -1380,16 +1415,20 @@ func (rh *RouteHandler) PatchBlobUpload(response http.ResponseWriter, request *h
|
||||||
clen, err = imgStore.PutBlobChunk(name, sessionID, from, to, request.Body)
|
clen, err = imgStore.PutBlobChunk(name, sessionID, from, to, request.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil { //nolint: dupl
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrBadUploadRange) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrBadUploadRange) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response, http.StatusRequestedRangeNotSatisfiable,
|
details["session_id"] = sessionID
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID})))
|
e := apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusRequestedRangeNotSatisfiable, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrUploadNotFound) {
|
} else if errors.Is(err, zerr.ErrUploadNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["session_id"] = sessionID
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID})))
|
e := apiErr.NewError(apiErr.BLOB_UPLOAD_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
// could be io.ErrUnexpectedEOF, syscall.EMFILE (Err:0x18 too many opened files), etc
|
// could be io.ErrUnexpectedEOF, syscall.EMFILE (Err:0x18 too many opened files), etc
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error: removing .uploads/ files")
|
rh.c.Log.Error().Err(err).Msg("unexpected error: removing .uploads/ files")
|
||||||
|
@ -1500,16 +1539,20 @@ func (rh *RouteHandler) UpdateBlobUpload(response http.ResponseWriter, request *
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = imgStore.PutBlobChunk(name, sessionID, from, to, request.Body)
|
_, err = imgStore.PutBlobChunk(name, sessionID, from, to, request.Body)
|
||||||
if err != nil {
|
if err != nil { //nolint:dupl
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrBadUploadRange) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrBadUploadRange) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details["session_id"] = sessionID
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID})))
|
e := apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrUploadNotFound) {
|
} else if errors.Is(err, zerr.ErrUploadNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["session_id"] = sessionID
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID})))
|
e := apiErr.NewError(apiErr.BLOB_UPLOAD_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
// could be io.ErrUnexpectedEOF, syscall.EMFILE (Err:0x18 too many opened files), etc
|
// could be io.ErrUnexpectedEOF, syscall.EMFILE (Err:0x18 too many opened files), etc
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error: removing .uploads/ files")
|
rh.c.Log.Error().Err(err).Msg("unexpected error: removing .uploads/ files")
|
||||||
|
@ -1528,18 +1571,23 @@ func (rh *RouteHandler) UpdateBlobUpload(response http.ResponseWriter, request *
|
||||||
finish:
|
finish:
|
||||||
// blob chunks already transferred, just finish
|
// blob chunks already transferred, just finish
|
||||||
if err := imgStore.FinishBlobUpload(name, sessionID, request.Body, digest); err != nil {
|
if err := imgStore.FinishBlobUpload(name, sessionID, request.Body, digest); err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrBadBlobDigest) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrBadBlobDigest) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details["digest"] = digest.String()
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.DIGEST_INVALID, map[string]string{"digest": digest.String()})))
|
e := apiErr.NewError(apiErr.DIGEST_INVALID).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrBadUploadRange) {
|
} else if errors.Is(err, zerr.ErrBadUploadRange) {
|
||||||
zcommon.WriteJSON(response, http.StatusBadRequest,
|
details["session_id"] = sessionID
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID})))
|
e := apiErr.NewError(apiErr.BLOB_UPLOAD_INVALID).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusBadRequest, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
} else if errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrUploadNotFound) {
|
} else if errors.Is(err, zerr.ErrUploadNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["session_id"] = sessionID
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID})))
|
e := apiErr.NewError(apiErr.BLOB_UPLOAD_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
// could be io.ErrUnexpectedEOF, syscall.EMFILE (Err:0x18 too many opened files), etc
|
// could be io.ErrUnexpectedEOF, syscall.EMFILE (Err:0x18 too many opened files), etc
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error: removing .uploads/ files")
|
rh.c.Log.Error().Err(err).Msg("unexpected error: removing .uploads/ files")
|
||||||
|
@ -1591,12 +1639,15 @@ func (rh *RouteHandler) DeleteBlobUpload(response http.ResponseWriter, request *
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := imgStore.DeleteBlobUpload(name, sessionID); err != nil {
|
if err := imgStore.DeleteBlobUpload(name, sessionID); err != nil {
|
||||||
|
details := zerr.GetDetails(err)
|
||||||
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["name"] = name
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.NAME_UNKNOWN, map[string]string{"name": name})))
|
e := apiErr.NewError(apiErr.NAME_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else if errors.Is(err, zerr.ErrUploadNotFound) {
|
} else if errors.Is(err, zerr.ErrUploadNotFound) {
|
||||||
zcommon.WriteJSON(response, http.StatusNotFound,
|
details["session_id"] = sessionID
|
||||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID})))
|
e := apiErr.NewError(apiErr.BLOB_UPLOAD_UNKNOWN).AddDetail(details)
|
||||||
|
zcommon.WriteJSON(response, http.StatusNotFound, apiErr.NewErrorList(e))
|
||||||
} else {
|
} else {
|
||||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
|
|
@ -105,7 +105,7 @@ func (linter *Linter) CheckMandatoryAnnotations(repo string, manifestDigest godi
|
||||||
string(manifestDigest), string(configDigest), missingAnnotations)
|
string(manifestDigest), string(configDigest), missingAnnotations)
|
||||||
linter.log.Error().Msg(msg)
|
linter.log.Error().Msg(msg)
|
||||||
|
|
||||||
return false, fmt.Errorf("%s: %w", msg, zerr.ErrImageLintAnnotations)
|
return false, zerr.NewError(zerr.ErrImageLintAnnotations).AddDetail("missingAnnotations", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"path"
|
"path"
|
||||||
|
@ -27,7 +28,6 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
manifestWithEmptyLayersErrMsg = "layers: Array must have at least 1 items"
|
manifestWithEmptyLayersErrMsg = "layers: Array must have at least 1 items"
|
||||||
|
|
||||||
cosignSignatureTagSuffix = "sig"
|
cosignSignatureTagSuffix = "sig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ func ValidateManifest(imgStore storageTypes.ImageStore, repo, reference, mediaTy
|
||||||
if err := ValidateManifestSchema(body); err != nil {
|
if err := ValidateManifestSchema(body); err != nil {
|
||||||
log.Error().Err(err).Msg("OCIv1 image manifest schema validation failed")
|
log.Error().Err(err).Msg("OCIv1 image manifest schema validation failed")
|
||||||
|
|
||||||
return "", zerr.ErrBadManifest
|
return "", zerr.NewError(zerr.ErrBadManifest).AddDetail("jsonSchemaValidation", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(body, &manifest); err != nil {
|
if err := json.Unmarshal(body, &manifest); err != nil {
|
||||||
|
@ -125,7 +125,7 @@ func ValidateManifest(imgStore storageTypes.ImageStore, repo, reference, mediaTy
|
||||||
if err := ValidateImageIndexSchema(body); err != nil {
|
if err := ValidateImageIndexSchema(body); err != nil {
|
||||||
log.Error().Err(err).Msg("OCIv1 image index manifest schema validation failed")
|
log.Error().Err(err).Msg("OCIv1 image index manifest schema validation failed")
|
||||||
|
|
||||||
return "", err
|
return "", zerr.NewError(zerr.ErrBadManifest).AddDetail("jsonSchemaValidation", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var indexManifest ispec.Index
|
var indexManifest ispec.Index
|
||||||
|
@ -219,8 +219,10 @@ func CheckIfIndexNeedsUpdate(index *ispec.Index, desc *ispec.Descriptor,
|
||||||
log.Error().Err(err).
|
log.Error().Err(err).
|
||||||
Str("old mediaType", manifest.MediaType).
|
Str("old mediaType", manifest.MediaType).
|
||||||
Str("new mediaType", desc.MediaType).Msg("cannot change media-type")
|
Str("new mediaType", desc.MediaType).Msg("cannot change media-type")
|
||||||
|
reason := fmt.Sprintf("changing manifest media-type from \"%s\" to \"%s\" is disallowed",
|
||||||
|
manifest.MediaType, desc.MediaType)
|
||||||
|
|
||||||
return false, "", err
|
return false, "", zerr.NewError(err).AddDetail("reason", reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
oldDesc := *desc
|
oldDesc := *desc
|
||||||
|
@ -780,7 +782,7 @@ func IsNonDistributable(mediaType string) bool {
|
||||||
func ValidateManifestSchema(buf []byte) error {
|
func ValidateManifestSchema(buf []byte) error {
|
||||||
if err := schema.ValidatorMediaTypeManifest.Validate(bytes.NewBuffer(buf)); err != nil {
|
if err := schema.ValidatorMediaTypeManifest.Validate(bytes.NewBuffer(buf)); err != nil {
|
||||||
if !IsEmptyLayersError(err) {
|
if !IsEmptyLayersError(err) {
|
||||||
return zerr.ErrBadManifest
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -789,7 +791,7 @@ func ValidateManifestSchema(buf []byte) error {
|
||||||
|
|
||||||
func ValidateImageIndexSchema(buf []byte) error {
|
func ValidateImageIndexSchema(buf []byte) error {
|
||||||
if err := schema.ValidatorMediaTypeImageIndex.Validate(bytes.NewBuffer(buf)); err != nil {
|
if err := schema.ValidatorMediaTypeImageIndex.Validate(bytes.NewBuffer(buf)); err != nil {
|
||||||
return zerr.ErrBadManifest
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -3,6 +3,7 @@ package storage_test
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ import (
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
|
||||||
"zotregistry.io/zot/errors"
|
zerr "zotregistry.io/zot/errors"
|
||||||
"zotregistry.io/zot/pkg/extensions/monitoring"
|
"zotregistry.io/zot/pkg/extensions/monitoring"
|
||||||
"zotregistry.io/zot/pkg/log"
|
"zotregistry.io/zot/pkg/log"
|
||||||
"zotregistry.io/zot/pkg/storage"
|
"zotregistry.io/zot/pkg/storage"
|
||||||
|
@ -74,6 +75,10 @@ func TestValidateManifest(t *testing.T) {
|
||||||
|
|
||||||
_, _, err = imgStore.PutImageManifest("test", "1.0", ispec.MediaTypeImageManifest, body)
|
_, _, err = imgStore.PutImageManifest("test", "1.0", ispec.MediaTypeImageManifest, body)
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
|
var internalErr *zerr.Error
|
||||||
|
So(errors.As(err, &internalErr), ShouldBeTrue)
|
||||||
|
So(internalErr.GetDetails(), ShouldContainKey, "jsonSchemaValidation")
|
||||||
|
So(internalErr.GetDetails()["jsonSchemaValidation"], ShouldEqual, "[schemaVersion: Must be less than or equal to 2]")
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("manifest with non-distributable layers", func() {
|
Convey("manifest with non-distributable layers", func() {
|
||||||
|
@ -169,7 +174,7 @@ func TestGetReferrersErrors(t *testing.T) {
|
||||||
return indexBuf, nil
|
return indexBuf, nil
|
||||||
},
|
},
|
||||||
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
||||||
return []byte{}, errors.ErrBlobNotFound
|
return []byte{}, zerr.ErrBlobNotFound
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +193,7 @@ func TestGetReferrersErrors(t *testing.T) {
|
||||||
return indexBuf, nil
|
return indexBuf, nil
|
||||||
},
|
},
|
||||||
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
||||||
return []byte{}, errors.ErrBadBlob
|
return []byte{}, zerr.ErrBadBlob
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +368,7 @@ func TestGetImageIndexErrors(t *testing.T) {
|
||||||
Convey("Trigger GetBlobContent error", t, func(c C) {
|
Convey("Trigger GetBlobContent error", t, func(c C) {
|
||||||
imgStore := &mocks.MockedImageStore{
|
imgStore := &mocks.MockedImageStore{
|
||||||
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
||||||
return []byte{}, errors.ErrBlobNotFound
|
return []byte{}, zerr.ErrBlobNotFound
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue