0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-04-08 02:54:41 -05:00

Merge pull request #70 from rchincha/compl

fix compliance
This commit is contained in:
Ramkumar Chinchani 2020-01-31 13:47:35 -08:00 committed by GitHub
commit 92241d17cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 300 additions and 58 deletions

View file

@ -58,7 +58,7 @@ func bearerAuthHandler(c *Controller) mux.MiddlewareFunc {
if err != nil {
c.Log.Error().Err(err).Msg("issue parsing Authorization header")
w.Header().Set("Content-Type", "application/json")
WriteJSON(w, http.StatusInternalServerError, NewError(UNSUPPORTED))
WriteJSON(w, http.StatusInternalServerError, NewErrorList(NewError(UNSUPPORTED)))
return
}
if !permissions.Allowed {
@ -218,5 +218,5 @@ func authFail(w http.ResponseWriter, realm string, delay int) {
time.Sleep(time.Duration(delay) * time.Second)
w.Header().Set("WWW-Authenticate", realm)
w.Header().Set("Content-Type", "application/json")
WriteJSON(w, http.StatusUnauthorized, NewError(UNAUTHORIZED))
WriteJSON(w, http.StatusUnauthorized, NewErrorList(NewError(UNAUTHORIZED)))
}

View file

@ -1,9 +1,11 @@
package api
import "github.com/anuvu/zot/errors"
import (
"github.com/anuvu/zot/errors"
)
type Error struct {
Code ErrorCode `json:"code"`
Code string `json:"code"`
Message string `json:"message"`
Description string `json:"description"`
Detail interface{} `json:"detail,omitempty"`
@ -34,7 +36,29 @@ const (
UNSUPPORTED
)
func NewError(code ErrorCode, detail ...interface{}) Error {
func (e ErrorCode) String() string {
m := map[ErrorCode]string{
BLOB_UNKNOWN: "BLOB_UNKNOWN",
BLOB_UPLOAD_INVALID: "BLOB_UPLOAD_INVALID",
BLOB_UPLOAD_UNKNOWN: "BLOB_UPLOAD_UNKNOWN",
DIGEST_INVALID: "DIGEST_INVALID",
MANIFEST_BLOB_UNKNOWN: "MANIFEST_BLOB_UNKNOWN",
MANIFEST_INVALID: "MANIFEST_INVALID",
MANIFEST_UNKNOWN: "MANIFEST_UNKNOWN",
MANIFEST_UNVERIFIED: "MANIFEST_UNVERIFIED",
NAME_INVALID: "NAME_INVALID",
NAME_UNKNOWN: "NAME_UNKNOWN",
SIZE_INVALID: "SIZE_INVALID",
TAG_INVALID: "TAG_INVALID",
UNAUTHORIZED: "UNAUTHORIZED",
DENIED: "DENIED",
UNSUPPORTED: "UNSUPPORTED",
}
return m[e]
}
func NewError(code ErrorCode, detail ...interface{}) Error { //nolint (interfacer)
var errMap = map[ErrorCode]Error{
BLOB_UNKNOWN: {
Message: "blob unknown to registry",
@ -135,8 +159,20 @@ func NewError(code ErrorCode, detail ...interface{}) Error {
panic(errors.ErrUnknownCode)
}
e.Code = code
e.Code = code.String()
e.Detail = detail
return e
}
func NewErrorList(errors ...Error) ErrorList {
el := make([]*Error, 0)
er := Error{}
for _, e := range errors {
er = e
el = append(el, &er)
}
return ErrorList{el}
}

View file

@ -167,7 +167,7 @@ func (rh *RouteHandler) ListTags(w http.ResponseWriter, r *http.Request) {
tags, err := rh.c.ImageStore.GetImageTags(name)
if err != nil {
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
return
}
@ -235,7 +235,7 @@ func (rh *RouteHandler) CheckManifest(w http.ResponseWriter, r *http.Request) {
reference, ok := vars["reference"]
if !ok || reference == "" {
WriteJSON(w, http.StatusNotFound, NewError(MANIFEST_INVALID, map[string]string{"reference": reference}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(MANIFEST_INVALID, map[string]string{"reference": reference})))
return
}
@ -243,10 +243,12 @@ func (rh *RouteHandler) CheckManifest(w http.ResponseWriter, r *http.Request) {
if err != nil {
switch err {
case errors.ErrManifestNotFound:
WriteJSON(w, http.StatusNotFound, NewError(MANIFEST_UNKNOWN, map[string]string{"reference": reference}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
WriteJSON(w, http.StatusInternalServerError, NewError(MANIFEST_INVALID, map[string]string{"reference": reference}))
WriteJSON(w, http.StatusInternalServerError,
NewErrorList(NewError(MANIFEST_INVALID, map[string]string{"reference": reference})))
}
return
@ -285,7 +287,7 @@ func (rh *RouteHandler) GetManifest(w http.ResponseWriter, r *http.Request) {
reference, ok := vars["reference"]
if !ok || reference == "" {
WriteJSON(w, http.StatusNotFound, NewError(MANIFEST_UNKNOWN, map[string]string{"reference": reference}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
return
}
@ -293,11 +295,14 @@ func (rh *RouteHandler) GetManifest(w http.ResponseWriter, r *http.Request) {
if err != nil {
switch err {
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrRepoBadVersion:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrManifestNotFound:
WriteJSON(w, http.StatusNotFound, NewError(MANIFEST_UNKNOWN, map[string]string{"reference": reference}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -334,7 +339,7 @@ func (rh *RouteHandler) UpdateManifest(w http.ResponseWriter, r *http.Request) {
reference, ok := vars["reference"]
if !ok || reference == "" {
WriteJSON(w, http.StatusNotFound, NewError(MANIFEST_INVALID, map[string]string{"reference": reference}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(MANIFEST_INVALID, map[string]string{"reference": reference})))
return
}
@ -356,13 +361,17 @@ func (rh *RouteHandler) UpdateManifest(w http.ResponseWriter, r *http.Request) {
if err != nil {
switch err {
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrManifestNotFound:
WriteJSON(w, http.StatusNotFound, NewError(MANIFEST_UNKNOWN, map[string]string{"reference": reference}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
case errors.ErrBadManifest:
WriteJSON(w, http.StatusBadRequest, NewError(MANIFEST_INVALID, map[string]string{"reference": reference}))
WriteJSON(w, http.StatusBadRequest,
NewErrorList(NewError(MANIFEST_INVALID, map[string]string{"reference": reference})))
case errors.ErrBlobNotFound:
WriteJSON(w, http.StatusBadRequest, NewError(BLOB_UNKNOWN, map[string]string{"blob": digest}))
WriteJSON(w, http.StatusBadRequest,
NewErrorList(NewError(BLOB_UNKNOWN, map[string]string{"blob": digest})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -404,9 +413,11 @@ func (rh *RouteHandler) DeleteManifest(w http.ResponseWriter, r *http.Request) {
if err != nil {
switch err {
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrManifestNotFound:
WriteJSON(w, http.StatusNotFound, NewError(MANIFEST_UNKNOWN, map[string]string{"reference": reference}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(MANIFEST_UNKNOWN, map[string]string{"reference": reference})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -449,11 +460,11 @@ func (rh *RouteHandler) CheckBlob(w http.ResponseWriter, r *http.Request) {
if err != nil {
switch err {
case errors.ErrBadBlobDigest:
WriteJSON(w, http.StatusBadRequest, NewError(DIGEST_INVALID, map[string]string{"digest": digest}))
WriteJSON(w, http.StatusBadRequest, NewErrorList(NewError(DIGEST_INVALID, map[string]string{"digest": digest})))
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrBlobNotFound:
WriteJSON(w, http.StatusNotFound, NewError(BLOB_UNKNOWN, map[string]string{"digest": digest}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(BLOB_UNKNOWN, map[string]string{"digest": digest})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -463,7 +474,7 @@ func (rh *RouteHandler) CheckBlob(w http.ResponseWriter, r *http.Request) {
}
if !ok {
WriteJSON(w, http.StatusNotFound, NewError(BLOB_UNKNOWN, map[string]string{"digest": digest}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(BLOB_UNKNOWN, map[string]string{"digest": digest})))
return
}
@ -503,11 +514,11 @@ func (rh *RouteHandler) GetBlob(w http.ResponseWriter, r *http.Request) {
if err != nil {
switch err {
case errors.ErrBadBlobDigest:
WriteJSON(w, http.StatusBadRequest, NewError(DIGEST_INVALID, map[string]string{"digest": digest}))
WriteJSON(w, http.StatusBadRequest, NewErrorList(NewError(DIGEST_INVALID, map[string]string{"digest": digest})))
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrBlobNotFound:
WriteJSON(w, http.StatusNotFound, NewError(BLOB_UNKNOWN, map[string]string{"digest": digest}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(BLOB_UNKNOWN, map[string]string{"digest": digest})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -550,11 +561,11 @@ func (rh *RouteHandler) DeleteBlob(w http.ResponseWriter, r *http.Request) {
if err != nil {
switch err {
case errors.ErrBadBlobDigest:
WriteJSON(w, http.StatusBadRequest, NewError(DIGEST_INVALID, map[string]string{"digest": digest}))
WriteJSON(w, http.StatusBadRequest, NewErrorList(NewError(DIGEST_INVALID, map[string]string{"digest": digest})))
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrBlobNotFound:
WriteJSON(w, http.StatusNotFound, NewError(BLOB_UNKNOWN, map[string]string{"digest": digest}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(BLOB_UNKNOWN, map[string]string{"digest": digest})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -599,11 +610,64 @@ func (rh *RouteHandler) CreateBlobUpload(w http.ResponseWriter, r *http.Request)
return
}
// a full blob upload if "digest" is present
digests, ok := r.URL.Query()["digest"]
if ok {
if len(digests) != 1 {
w.WriteHeader(http.StatusBadRequest)
return
}
digest := digests[0]
if contentType := r.Header.Get("Content-Type"); contentType != BinaryMediaType {
rh.c.Log.Warn().Str("actual", contentType).Str("expected", BinaryMediaType).Msg("invalid media type")
w.WriteHeader(http.StatusUnsupportedMediaType)
return
}
rh.c.Log.Info().Int64("r.ContentLength", r.ContentLength).Msg("DEBUG")
var contentLength int64
var err error
if contentLength, err = strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64); err != nil || contentLength <= 0 {
rh.c.Log.Warn().Str("actual", r.Header.Get("Content-Length")).Msg("invalid content length")
WriteJSON(w, http.StatusBadRequest,
NewErrorList(NewError(BLOB_UPLOAD_INVALID, map[string]string{"digest": digest})))
return
}
sessionID, size, err := rh.c.ImageStore.FullBlobUpload(name, r.Body, digest)
if err != nil {
rh.c.Log.Error().Err(err).Int64("actual", size).Int64("expected", contentLength).Msg("failed full upload")
w.WriteHeader(http.StatusInternalServerError)
return
}
if size != contentLength {
rh.c.Log.Warn().Int64("actual", size).Int64("expected", contentLength).Msg("invalid content length")
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Location", fmt.Sprintf("/v2/%s/blobs/%s", name, digest))
w.Header().Set(BlobUploadUUID, sessionID)
w.WriteHeader(http.StatusCreated)
return
}
u, err := rh.c.ImageStore.NewBlobUpload(name)
if err != nil {
switch err {
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound, NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -649,13 +713,17 @@ func (rh *RouteHandler) GetBlobUpload(w http.ResponseWriter, r *http.Request) {
if err != nil {
switch err {
case errors.ErrBadUploadRange:
WriteJSON(w, http.StatusBadRequest, NewError(BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID}))
WriteJSON(w, http.StatusBadRequest,
NewErrorList(NewError(BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID})))
case errors.ErrBadBlobDigest:
WriteJSON(w, http.StatusBadRequest, NewError(BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID}))
WriteJSON(w, http.StatusBadRequest,
NewErrorList(NewError(BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID})))
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrUploadNotFound:
WriteJSON(w, http.StatusNotFound, NewError(BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -746,11 +814,14 @@ func (rh *RouteHandler) PatchBlobUpload(w http.ResponseWriter, r *http.Request)
if err != nil {
switch err {
case errors.ErrBadUploadRange:
WriteJSON(w, http.StatusBadRequest, NewError(BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID}))
WriteJSON(w, http.StatusRequestedRangeNotSatisfiable,
NewErrorList(NewError(BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID})))
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrUploadNotFound:
WriteJSON(w, http.StatusNotFound, NewError(BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -852,11 +923,14 @@ func (rh *RouteHandler) UpdateBlobUpload(w http.ResponseWriter, r *http.Request)
if err != nil {
switch err {
case errors.ErrBadUploadRange:
WriteJSON(w, http.StatusBadRequest, NewError(BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID}))
WriteJSON(w, http.StatusBadRequest,
NewErrorList(NewError(BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID})))
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrUploadNotFound:
WriteJSON(w, http.StatusNotFound, NewError(BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -875,13 +949,17 @@ finish:
if err := rh.c.ImageStore.FinishBlobUpload(name, sessionID, r.Body, digest); err != nil {
switch err {
case errors.ErrBadBlobDigest:
WriteJSON(w, http.StatusBadRequest, NewError(DIGEST_INVALID, map[string]string{"digest": digest}))
WriteJSON(w, http.StatusBadRequest,
NewErrorList(NewError(DIGEST_INVALID, map[string]string{"digest": digest})))
case errors.ErrBadUploadRange:
WriteJSON(w, http.StatusBadRequest, NewError(BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID}))
WriteJSON(w, http.StatusBadRequest,
NewErrorList(NewError(BLOB_UPLOAD_INVALID, map[string]string{"session_id": sessionID})))
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrUploadNotFound:
WriteJSON(w, http.StatusNotFound, NewError(BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)
@ -925,9 +1003,11 @@ func (rh *RouteHandler) DeleteBlobUpload(w http.ResponseWriter, r *http.Request)
if err := rh.c.ImageStore.DeleteBlobUpload(name, sessionID); err != nil {
switch err {
case errors.ErrRepoNotFound:
WriteJSON(w, http.StatusNotFound, NewError(NAME_UNKNOWN, map[string]string{"name": name}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(NAME_UNKNOWN, map[string]string{"name": name})))
case errors.ErrUploadNotFound:
WriteJSON(w, http.StatusNotFound, NewError(BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID}))
WriteJSON(w, http.StatusNotFound,
NewErrorList(NewError(BLOB_UPLOAD_UNKNOWN, map[string]string{"session_id": sessionID})))
default:
rh.c.Log.Error().Err(err).Msg("unexpected error")
w.WriteHeader(http.StatusInternalServerError)

View file

@ -169,6 +169,44 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
So(resp.StatusCode(), ShouldEqual, 200)
})
Convey("Monolithic blob upload with body", func() {
Print("\nMonolithic blob upload")
// create content
content := []byte("this is a blob")
digest := godigest.FromBytes(content)
So(digest, ShouldNotBeNil)
// setting invalid URL params should fail
resp, err := resty.R().
SetQueryParam("digest", digest.String()).
SetQueryParam("from", digest.String()).
SetHeader("Content-Type", "application/octet-stream").
SetBody(content).
Post(baseURL + "/v2/repo2/blobs/uploads/")
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 405)
// setting a "?digest=<>" but without body should fail
resp, err = resty.R().
SetQueryParam("digest", digest.String()).
SetHeader("Content-Type", "application/octet-stream").
Post(baseURL + "/v2/repo2/blobs/uploads/")
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 400)
// set a "?digest=<>"
resp, err = resty.R().
SetQueryParam("digest", digest.String()).
SetHeader("Content-Type", "application/octet-stream").
SetBody(content).
Post(baseURL + "/v2/repo2/blobs/uploads/")
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 201)
loc := Location(baseURL, resp)
So(loc, ShouldNotBeEmpty)
// blob reference should be accessible
resp, err = resty.R().Get(loc)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
})
Convey("Monolithic blob upload with multiple name components", func() {
Print("\nMonolithic blob upload with multiple name components")
resp, err := resty.R().Post(baseURL + "/v2/repo10/repo20/repo30/blobs/uploads/")
@ -258,7 +296,7 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
resp, err = resty.R().SetHeader("Content-Type", "application/octet-stream").
SetHeader("Content-Range", contentRange).SetBody(chunk1).Patch(loc)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 400)
So(resp.StatusCode(), ShouldEqual, 416)
So(resp.String(), ShouldNotBeEmpty)
chunk2 := []byte("this is the second chunk")
@ -326,7 +364,7 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
resp, err = resty.R().SetHeader("Content-Type", "application/octet-stream").
SetHeader("Content-Range", contentRange).SetBody(chunk1).Patch(loc)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 400)
So(resp.StatusCode(), ShouldEqual, 416)
So(resp.String(), ShouldNotBeEmpty)
chunk2 := []byte("this is the second chunk")
@ -450,6 +488,23 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
So(d, ShouldNotBeEmpty)
So(d, ShouldEqual, digest.String())
content = []byte("this is a blob")
digest = godigest.FromBytes(content)
So(digest, ShouldNotBeNil)
// create a manifest with same blob but a different tag
m = ispec.Manifest{Layers: []ispec.Descriptor{{Digest: digest}}}
content, err = json.Marshal(m)
So(err, ShouldBeNil)
digest = godigest.FromBytes(content)
So(digest, ShouldNotBeNil)
resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
SetBody(content).Put(baseURL + "/v2/repo7/manifests/test:2.0")
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 201)
d = resp.Header().Get(api.DistContentDigestKey)
So(d, ShouldNotBeEmpty)
So(d, ShouldEqual, digest.String())
// check/get by tag
resp, err = resty.R().Head(baseURL + "/v2/repo7/manifests/test:1.0")
So(err, ShouldBeNil)
@ -475,6 +530,10 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
resp, err = resty.R().Delete(baseURL + "/v2/repo7/manifests/" + digest.String())
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 202)
// delete manifest by digest
resp, err = resty.R().Delete(baseURL + "/v2/repo7/manifests/" + digest.String())
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 202)
// delete again should fail
resp, err = resty.R().Delete(baseURL + "/v2/repo7/manifests/" + digest.String())
So(err, ShouldBeNil)
@ -488,6 +547,13 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 404)
So(resp.Body(), ShouldNotBeEmpty)
resp, err = resty.R().Head(baseURL + "/v2/repo7/manifests/test:2.0")
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 404)
resp, err = resty.R().Get(baseURL + "/v2/repo7/manifests/test:2.0")
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 404)
So(resp.Body(), ShouldNotBeEmpty)
// check/get by reference
resp, err = resty.R().Head(baseURL + "/v2/repo7/manifests/" + digest.String())
So(err, ShouldBeNil)

View file

@ -1,7 +1,9 @@
package storage
import (
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
@ -477,14 +479,6 @@ func (is *ImageStore) DeleteImageManifest(repo string, reference string) error {
break
}
v, ok := m.Annotations[ispec.AnnotationRefName]
if ok && v == reference {
digest = m.Digest
found = true
break
}
}
if !found {
@ -701,6 +695,62 @@ func (is *ImageStore) FinishBlobUpload(repo string, uuid string, body io.Reader,
return err
}
// FullBlobUpload handles a full blob upload, and no partial session is created
func (is *ImageStore) FullBlobUpload(repo string, body io.Reader, digest string) (string, int64, error) {
if err := is.InitRepo(repo); err != nil {
return "", -1, err
}
dstDigest, err := godigest.Parse(digest)
if err != nil {
is.log.Error().Err(err).Str("digest", digest).Msg("failed to parse digest")
return "", -1, errors.ErrBadBlobDigest
}
u, err := guuid.NewV4()
if err != nil {
return "", -1, err
}
uuid := u.String()
src := is.BlobUploadPath(repo, uuid)
f, err := os.Create(src)
if err != nil {
is.log.Error().Err(err).Str("blob", src).Msg("failed to open blob")
return "", -1, errors.ErrUploadNotFound
}
defer f.Close()
digester := sha256.New()
mw := io.MultiWriter(f, digester)
n, err := io.Copy(mw, body)
if err != nil {
return "", -1, err
}
srcDigest := godigest.NewDigestFromEncoded(godigest.SHA256, fmt.Sprintf("%x", digester.Sum(nil)))
if srcDigest != dstDigest {
is.log.Error().Str("srcDigest", srcDigest.String()).
Str("dstDigest", dstDigest.String()).Msg("actual digest not equal to expected digest")
return "", -1, errors.ErrBadBlobDigest
}
dir := path.Join(is.rootDir, repo)
dir = path.Join(dir, "blobs")
dir = path.Join(dir, dstDigest.Algorithm().String())
_ = os.MkdirAll(dir, 0755)
dst := is.BlobPath(repo, dstDigest)
// move the blob from uploads to final dest
_ = os.Rename(src, dst)
return uuid, n, err
}
// DeleteBlobUpload deletes an existing blob upload that is currently in progress.
func (is *ImageStore) DeleteBlobUpload(repo string, uuid string) error {
blobUploadPath := is.BlobUploadPath(repo, uuid)

View file

@ -58,6 +58,16 @@ func TestAPIs(t *testing.T) {
So(v, ShouldBeEmpty)
})
Convey("Full blob upload", func() {
body := []byte("this is a blob")
buf := bytes.NewBuffer(body)
d := godigest.FromBytes(body)
u, n, err := il.FullBlobUpload("test", buf, d.String())
So(err, ShouldBeNil)
So(n, ShouldEqual, len(body))
So(u, ShouldNotBeEmpty)
})
Convey("New blob upload", func() {
v, err := il.NewBlobUpload("test")
So(err, ShouldBeNil)