mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
check: add unit tests to cover the new code, fix linter errors
This commit is contained in:
parent
f9a1a0fe48
commit
58040f4562
5 changed files with 157 additions and 45 deletions
|
@ -54,10 +54,11 @@ func (e ErrorCode) String() string {
|
|||
DENIED: "DENIED",
|
||||
UNSUPPORTED: "UNSUPPORTED",
|
||||
}
|
||||
|
||||
return m[e]
|
||||
}
|
||||
|
||||
func NewError(code ErrorCode, detail ...interface{}) Error {
|
||||
func NewError(code ErrorCode, detail ...interface{}) Error { //nolint (interfacer)
|
||||
var errMap = map[ErrorCode]Error{
|
||||
BLOB_UNKNOWN: {
|
||||
Message: "blob unknown to registry",
|
||||
|
@ -166,9 +167,11 @@ func NewError(code ErrorCode, detail ...interface{}) Error {
|
|||
|
||||
func NewErrorList(errors ...Error) ErrorList {
|
||||
el := make([]*Error, 0)
|
||||
er := Error{}
|
||||
|
||||
for _, e := range errors {
|
||||
el = append(el, &e)
|
||||
er = e
|
||||
el = append(el, &er)
|
||||
}
|
||||
|
||||
return ErrorList{el}
|
||||
|
|
|
@ -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, NewErrorList(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, NewErrorList(NewError(MANIFEST_INVALID, map[string]string{"reference": reference})))
|
||||
WriteJSON(w, http.StatusInternalServerError,
|
||||
NewErrorList(NewError(MANIFEST_INVALID, 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, NewErrorList(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, NewErrorList(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, NewErrorList(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)
|
||||
|
@ -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, NewErrorList(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, NewErrorList(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, NewErrorList(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, NewErrorList(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, NewErrorList(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, NewErrorList(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)
|
||||
|
@ -587,6 +598,18 @@ func (rh *RouteHandler) CreateBlobUpload(w http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
// blob mounts not allowed since we don't have access control yet, and this
|
||||
// may be a uncommon use case, but remain compliant
|
||||
if _, ok := r.URL.Query()["mount"]; ok {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := r.URL.Query()["from"]; ok {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// a full blob upload if "digest" is present
|
||||
digests, ok := r.URL.Query()["digest"]
|
||||
if ok {
|
||||
|
@ -607,16 +630,18 @@ func (rh *RouteHandler) CreateBlobUpload(w http.ResponseWriter, r *http.Request)
|
|||
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 {
|
||||
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")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
WriteJSON(w, http.StatusBadRequest,
|
||||
NewErrorList(NewError(BLOB_UPLOAD_INVALID, map[string]string{"digest": digest})))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
size, err := rh.c.ImageStore.FullBlobUpload(name, r.Body, digest)
|
||||
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)
|
||||
|
@ -631,19 +656,10 @@ func (rh *RouteHandler) CreateBlobUpload(w http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Location", fmt.Sprintf("/v2/%s/blobs/%s", name, digest))
|
||||
w.Header().Set(BlobUploadUUID, sessionID)
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
return
|
||||
}
|
||||
|
||||
// blob mounts not allowed since we don't have access control yet, and this
|
||||
// may be a uncommon use case, but remain compliant
|
||||
if _, ok := r.URL.Query()["mount"]; ok {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := r.URL.Query()["from"]; ok {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -697,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, NewErrorList(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, NewErrorList(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, NewErrorList(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, NewErrorList(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)
|
||||
|
@ -794,11 +814,14 @@ func (rh *RouteHandler) PatchBlobUpload(w http.ResponseWriter, r *http.Request)
|
|||
if err != nil {
|
||||
switch err {
|
||||
case errors.ErrBadUploadRange:
|
||||
WriteJSON(w, http.StatusRequestedRangeNotSatisfiable, NewErrorList(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, NewErrorList(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, NewErrorList(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)
|
||||
|
@ -900,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, NewErrorList(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, NewErrorList(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, NewErrorList(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)
|
||||
|
@ -923,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, NewErrorList(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, NewErrorList(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, NewErrorList(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, NewErrorList(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)
|
||||
|
@ -973,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, NewErrorList(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, NewErrorList(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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -727,6 +727,7 @@ func (is *ImageStore) FullBlobUpload(repo string, body io.Reader, digest string)
|
|||
digester := sha256.New()
|
||||
mw := io.MultiWriter(f, digester)
|
||||
n, err := io.Copy(mw, body)
|
||||
|
||||
if err != nil {
|
||||
return "", -1, err
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue