0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00
zot/pkg/api/routes_test.go
Andrei Aaron bd9ad998cd Fix file handlers not being closed after calls to ImageStore.GetBlob
This is to fixes hitting the FD limit when reading blobs from the disk in the graphql API

Signed-off-by: Andrei Aaron <andaaron@cisco.com>
2022-08-19 09:22:13 -07:00

1360 lines
36 KiB
Go

//go:build sync && scrub && metrics && search && ui_base && lint
// +build sync,scrub,metrics,search,ui_base,lint
package api_test
import (
"bytes"
"context"
"errors"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/gorilla/mux"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
. "github.com/smartystreets/goconvey/convey"
zerr "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/storage"
"zotregistry.io/zot/pkg/test"
"zotregistry.io/zot/pkg/test/mocks"
)
var ErrUnexpectedError = errors.New("error: unexpected error")
func TestRoutes(t *testing.T) {
Convey("Make a new controller", t, func() {
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
ctlr.Config.Storage.Commit = true
err := test.CopyFiles("../../test/data", ctlr.Config.Storage.RootDirectory)
if err != nil {
panic(err)
}
go startServer(ctlr)
defer stopServer(ctlr)
test.WaitTillServerReady(baseURL)
rthdlr := api.NewRouteHandler(ctlr)
// NOTE: the url or method itself doesn't matter below since we are calling the handlers directly,
// so path routing is bypassed
Convey("Get manifest", func() {
// overwrite controller storage
ctlr.StoreController.DefaultStore = &mocks.MockedImageStore{
GetImageManifestFn: func(repo string, reference string) ([]byte, string, string, error) {
return []byte{}, "", "", zerr.ErrRepoBadVersion
},
}
request, _ := http.NewRequestWithContext(context.TODO(), "GET", baseURL, nil)
request = mux.SetURLVars(request, map[string]string{
"name": "test",
"reference": "b8b1231908844a55c251211c7a67ae3c809fb86a081a8eeb4a715e6d7d65625c",
})
response := httptest.NewRecorder()
rthdlr.GetManifest(response, request)
resp := response.Result()
defer resp.Body.Close()
So(resp, ShouldNotBeNil)
So(resp.StatusCode, ShouldEqual, http.StatusNotFound)
})
Convey("UpdateManifest ", func() {
testUpdateManifest := func(urlVars map[string]string, ism *mocks.MockedImageStore) int {
ctlr.StoreController.DefaultStore = ism
str := []byte("test")
request, _ := http.NewRequestWithContext(context.TODO(), "PUT", baseURL, bytes.NewBuffer(str))
request = mux.SetURLVars(request, urlVars)
request.Header.Add("Content-Type", ispec.MediaTypeImageManifest)
response := httptest.NewRecorder()
rthdlr.UpdateManifest(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
// repo not found
statusCode := testUpdateManifest(
map[string]string{
"name": "test",
"reference": "reference",
},
&mocks.MockedImageStore{
PutImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) {
return "", zerr.ErrRepoNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
// ErrManifestNotFound
statusCode = testUpdateManifest(
map[string]string{
"name": "test",
"reference": "reference",
},
&mocks.MockedImageStore{
PutImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) {
return "", zerr.ErrManifestNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
// ErrBadManifest
statusCode = testUpdateManifest(
map[string]string{
"name": "test",
"reference": "reference",
},
&mocks.MockedImageStore{
PutImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) {
return "", zerr.ErrBadManifest
},
})
So(statusCode, ShouldEqual, http.StatusBadRequest)
// ErrBlobNotFound
statusCode = testUpdateManifest(
map[string]string{
"name": "test",
"reference": "reference",
},
&mocks.MockedImageStore{
PutImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) {
return "", zerr.ErrBlobNotFound
},
})
So(statusCode, ShouldEqual, http.StatusBadRequest)
// ErrRepoBadVersion
statusCode = testUpdateManifest(
map[string]string{
"name": "test",
"reference": "reference",
},
&mocks.MockedImageStore{
PutImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) {
return "", zerr.ErrRepoBadVersion
},
})
So(statusCode, ShouldEqual, http.StatusInternalServerError)
})
Convey("DeleteManifest", func() {
testDeleteManifest := func(headers map[string]string, urlVars map[string]string, ism *mocks.MockedImageStore) int {
ctlr.StoreController.DefaultStore = ism
request, _ := http.NewRequestWithContext(context.Background(), "DELETE", baseURL, nil)
request = mux.SetURLVars(request, urlVars)
for k, v := range headers {
request.Header.Add(k, v)
}
response := httptest.NewRecorder()
rthdlr.DeleteManifest(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
// ErrRepoNotFound
statusCode := testDeleteManifest(
map[string]string{},
map[string]string{
"name": "ErrManifestNotFound",
"reference": "reference",
},
&mocks.MockedImageStore{
DeleteImageManifestFn: func(repo, reference string) error {
return zerr.ErrRepoNotFound
},
},
)
So(statusCode, ShouldEqual, http.StatusBadRequest)
// ErrManifestNotFound
statusCode = testDeleteManifest(
map[string]string{},
map[string]string{
"name": "ErrManifestNotFound",
"reference": "reference",
},
&mocks.MockedImageStore{
DeleteImageManifestFn: func(repo, reference string) error {
return zerr.ErrManifestNotFound
},
},
)
So(statusCode, ShouldEqual, http.StatusNotFound)
// ErrUnexpectedError
statusCode = testDeleteManifest(
map[string]string{},
map[string]string{
"name": "ErrUnexpectedError",
"reference": "reference",
},
&mocks.MockedImageStore{
DeleteImageManifestFn: func(repo, reference string) error {
return ErrUnexpectedError
},
},
)
So(statusCode, ShouldEqual, http.StatusInternalServerError)
// ErrBadManifest
statusCode = testDeleteManifest(
map[string]string{},
map[string]string{
"name": "ErrBadManifest",
"reference": "reference",
},
&mocks.MockedImageStore{
DeleteImageManifestFn: func(repo, reference string) error {
return zerr.ErrBadManifest
},
},
)
So(statusCode, ShouldEqual, http.StatusBadRequest)
})
Convey("DeleteBlob", func() {
testDeleteBlob := func(urlVars map[string]string, ism *mocks.MockedImageStore) int {
ctlr.StoreController.DefaultStore = ism
request, _ := http.NewRequestWithContext(context.TODO(), "DELETE", baseURL, nil)
request = mux.SetURLVars(request, urlVars)
response := httptest.NewRecorder()
rthdlr.DeleteBlob(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
// ErrUnexpectedError
statusCode := testDeleteBlob(
map[string]string{
"name": "ErrUnexpectedError",
"digest": "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621",
},
&mocks.MockedImageStore{
DeleteBlobFn: func(repo, digest string) error {
return ErrUnexpectedError
},
})
So(statusCode, ShouldEqual, http.StatusInternalServerError)
statusCode = testDeleteBlob(
map[string]string{
"name": "ErrBadBlobDigest",
"digest": "sha256:7b8437f04f83f084b7ed68ad8c4a4947e12fc4e1b006b38129bac89114ec3621",
},
&mocks.MockedImageStore{
DeleteBlobFn: func(repo, digest string) error {
return zerr.ErrBadBlobDigest
},
})
So(statusCode, ShouldEqual, http.StatusBadRequest)
// ErrBlobNotFound
statusCode = testDeleteBlob(
map[string]string{
"name": "ErrBlobNotFound",
"digest": "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621",
},
&mocks.MockedImageStore{
DeleteBlobFn: func(repo, digest string) error {
return zerr.ErrBlobNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
// ErrRepoNotFound
statusCode = testDeleteBlob(
map[string]string{
"name": "ErrRepoNotFound",
"digest": "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621",
},
&mocks.MockedImageStore{
DeleteBlobFn: func(repo, digest string) error {
return zerr.ErrRepoNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
})
// Check Blob
Convey("CheckBlob", func() {
testCheckBlob := func(urlVars map[string]string, ism *mocks.MockedImageStore) int {
ctlr.StoreController.DefaultStore = ism
request, _ := http.NewRequestWithContext(context.TODO(), "HEAD", baseURL, nil)
request = mux.SetURLVars(request, urlVars)
response := httptest.NewRecorder()
rthdlr.CheckBlob(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
// ErrBadBlobDigest
statusCode := testCheckBlob(
map[string]string{
"name": "ErrBadBlobDigest",
"digest": "1234",
},
&mocks.MockedImageStore{
CheckBlobFn: func(repo, digest string) (bool, int64, error) {
return true, 0, zerr.ErrBadBlobDigest
},
})
So(statusCode, ShouldEqual, http.StatusBadRequest)
// ErrRepoNotFound
statusCode = testCheckBlob(
map[string]string{
"name": "ErrRepoNotFound",
"digest": "1234",
},
&mocks.MockedImageStore{
CheckBlobFn: func(repo, digest string) (bool, int64, error) {
return true, 0, zerr.ErrRepoNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
// ErrBlobNotFound
statusCode = testCheckBlob(
map[string]string{
"name": "ErrBlobNotFound",
"digest": "1234",
},
&mocks.MockedImageStore{
CheckBlobFn: func(repo, digest string) (bool, int64, error) {
return true, 0, zerr.ErrBlobNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
// ErrUnexpectedError
statusCode = testCheckBlob(
map[string]string{
"name": "ErrUnexpectedError",
"digest": "1234",
},
&mocks.MockedImageStore{
CheckBlobFn: func(repo, digest string) (bool, int64, error) {
return true, 0, ErrUnexpectedError
},
})
So(statusCode, ShouldEqual, http.StatusInternalServerError)
// Error Check Blob is not ok
statusCode = testCheckBlob(
map[string]string{
"name": "Check Blob Not Ok",
"digest": "1234",
},
&mocks.MockedImageStore{
CheckBlobFn: func(repo, digest string) (bool, int64, error) {
return false, 0, nil
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
})
Convey("GetBlob", func() {
testGetBlob := func(urlVars map[string]string, ism *mocks.MockedImageStore) int {
ctlr.StoreController.DefaultStore = ism
request, _ := http.NewRequestWithContext(context.TODO(), "GET", baseURL, nil)
request = mux.SetURLVars(request, urlVars)
response := httptest.NewRecorder()
rthdlr.GetBlob(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
// ErrRepoNotFound
statusCode := testGetBlob(
map[string]string{
"name": "ErrRepoNotFound",
"digest": "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621",
},
&mocks.MockedImageStore{
GetBlobFn: func(repo, digest, mediaType string) (io.ReadCloser, int64, error) {
return io.NopCloser(bytes.NewBuffer([]byte(""))), 0, zerr.ErrRepoNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
// ErrRepoNotFound
statusCode = testGetBlob(
map[string]string{
"name": "ErrRepoNotFound",
"digest": "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621",
},
&mocks.MockedImageStore{
GetBlobFn: func(repo, digest, mediaType string) (io.ReadCloser, int64, error) {
return io.NopCloser(bytes.NewBuffer([]byte(""))), 0, zerr.ErrBadBlobDigest
},
})
So(statusCode, ShouldEqual, http.StatusBadRequest)
})
Convey("CreateBlobUpload", func() {
testCreateBlobUpload := func(
query []struct{ k, v string },
headers map[string]string,
ism *mocks.MockedImageStore,
) int {
ctlr.StoreController.DefaultStore = ism
request, _ := http.NewRequestWithContext(context.TODO(), "POST", baseURL, nil)
request = mux.SetURLVars(request,
map[string]string{
"name": "test",
"mount": "1234",
})
q := request.URL.Query()
for _, qe := range query {
q.Add(qe.k, qe.v)
}
request.URL.RawQuery = q.Encode()
for k, v := range headers {
request.Header.Add(k, v)
}
response := httptest.NewRecorder()
rthdlr.CreateBlobUpload(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
// ErrRepoNotFound
statusCode := testCreateBlobUpload(
[]struct{ k, v string }{
{"mount", "1234"},
},
map[string]string{},
&mocks.MockedImageStore{
NewBlobUploadFn: func(repo string) (string, error) {
return "", zerr.ErrRepoNotFound
},
CheckBlobFn: func(repo, digest string) (bool, int64, error) {
return true, 0, zerr.ErrRepoNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
// a full blob upload if multiple digests are present
statusCode = testCreateBlobUpload(
[]struct{ k, v string }{
{"digest", "1234"},
{"digest", "5234"},
},
map[string]string{},
&mocks.MockedImageStore{
NewBlobUploadFn: func(repo string) (string, error) {
return "", zerr.ErrRepoNotFound
},
CheckBlobFn: func(repo, digest string) (bool, int64, error) {
return true, 0, zerr.ErrRepoNotFound
},
})
So(statusCode, ShouldEqual, http.StatusBadRequest)
// a full blob upload if content type is wrong
statusCode = testCreateBlobUpload(
[]struct{ k, v string }{
{"digest", "1234"},
},
map[string]string{
"Content-Type": "badContentType",
},
&mocks.MockedImageStore{
NewBlobUploadFn: func(repo string) (string, error) {
return "", zerr.ErrRepoNotFound
},
CheckBlobFn: func(repo, digest string) (bool, int64, error) {
return true, 0, zerr.ErrRepoNotFound
},
})
So(statusCode, ShouldEqual, http.StatusUnsupportedMediaType)
// digest prezent imgStore err
statusCode = testCreateBlobUpload(
[]struct{ k, v string }{
{"digest", "1234"},
},
map[string]string{
"Content-Type": constants.BinaryMediaType,
"Content-Length": "100",
},
&mocks.MockedImageStore{
FullBlobUploadFn: func(repo string, body io.Reader, digest string) (string, int64, error) {
return "session", 0, zerr.ErrBadBlobDigest
},
})
So(statusCode, ShouldEqual, http.StatusInternalServerError)
// digest prezent bad length
statusCode = testCreateBlobUpload(
[]struct{ k, v string }{
{"digest", "1234"},
},
map[string]string{
"Content-Type": constants.BinaryMediaType,
"Content-Length": "100",
},
&mocks.MockedImageStore{
FullBlobUploadFn: func(repo string, body io.Reader, digest string) (string, int64, error) {
return "session", 20, nil
},
})
So(statusCode, ShouldEqual, http.StatusInternalServerError)
// newBlobUpload not found
statusCode = testCreateBlobUpload(
[]struct{ k, v string }{},
map[string]string{
"Content-Type": constants.BinaryMediaType,
"Content-Length": "100",
},
&mocks.MockedImageStore{
NewBlobUploadFn: func(repo string) (string, error) {
return "", zerr.ErrRepoNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
// newBlobUpload unexpected error
statusCode = testCreateBlobUpload(
[]struct{ k, v string }{},
map[string]string{
"Content-Type": constants.BinaryMediaType,
"Content-Length": "100",
},
&mocks.MockedImageStore{
NewBlobUploadFn: func(repo string) (string, error) {
return "", ErrUnexpectedError
},
})
So(statusCode, ShouldEqual, http.StatusInternalServerError)
})
Convey("GetBlobUpload", func() {
testGetBlobUpload := func(
query []struct{ k, v string },
headers map[string]string,
vars map[string]string,
ism *mocks.MockedImageStore,
) int {
ctlr.StoreController.DefaultStore = ism
request, _ := http.NewRequestWithContext(context.TODO(), "GET", baseURL, nil)
request = mux.SetURLVars(request, vars)
q := request.URL.Query()
for _, qe := range query {
q.Add(qe.k, qe.v)
}
request.URL.RawQuery = q.Encode()
for k, v := range headers {
request.Header.Add(k, v)
}
response := httptest.NewRecorder()
rthdlr.GetBlobUpload(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
// ErrBadUploadRange
statusCode := testGetBlobUpload(
[]struct{ k, v string }{},
map[string]string{},
map[string]string{
"name": "test",
"session_id": "1234",
},
&mocks.MockedImageStore{
GetBlobUploadFn: func(repo, uuid string) (int64, error) {
return 0, zerr.ErrBadUploadRange
},
})
So(statusCode, ShouldEqual, http.StatusBadRequest)
// ErrBadBlobDigest
statusCode = testGetBlobUpload(
[]struct{ k, v string }{
{"mount", "1234"},
},
map[string]string{},
map[string]string{
"name": "test",
"session_id": "1234",
},
&mocks.MockedImageStore{
GetBlobUploadFn: func(repo, uuid string) (int64, error) {
return 0, zerr.ErrBadBlobDigest
},
})
So(statusCode, ShouldEqual, http.StatusBadRequest)
// ErrRepoNotFound
statusCode = testGetBlobUpload(
[]struct{ k, v string }{
{"mount", "1234"},
},
map[string]string{},
map[string]string{
"name": "test",
"session_id": "1234",
},
&mocks.MockedImageStore{
GetBlobUploadFn: func(repo, uuid string) (int64, error) {
return 0, zerr.ErrRepoNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
// ErrUploadNotFound
statusCode = testGetBlobUpload(
[]struct{ k, v string }{
{"mount", "1234"},
},
map[string]string{},
map[string]string{
"name": "test",
"session_id": "1234",
},
&mocks.MockedImageStore{
GetBlobUploadFn: func(repo, uuid string) (int64, error) {
return 0, zerr.ErrUploadNotFound
},
})
So(statusCode, ShouldEqual, http.StatusNotFound)
// ErrUploadNotFound
statusCode = testGetBlobUpload(
[]struct{ k, v string }{
{"mount", "1234"},
},
map[string]string{},
map[string]string{
"name": "test",
"session_id": "1234",
},
&mocks.MockedImageStore{
GetBlobUploadFn: func(repo, uuid string) (int64, error) {
return 0, ErrUnexpectedError
},
})
So(statusCode, ShouldEqual, http.StatusInternalServerError)
})
Convey("PatchBlobUpload", func() {
testPatchBlobUpload := func(
query []struct{ k, v string },
headers map[string]string,
vars map[string]string,
ism *mocks.MockedImageStore,
) int {
ctlr.StoreController.DefaultStore = ism
request, _ := http.NewRequestWithContext(context.TODO(), "PATCH", baseURL, nil)
request = mux.SetURLVars(request, vars)
q := request.URL.Query()
for _, qe := range query {
q.Add(qe.k, qe.v)
}
request.URL.RawQuery = q.Encode()
for k, v := range headers {
request.Header.Add(k, v)
}
response := httptest.NewRecorder()
rthdlr.PatchBlobUpload(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
status := testPatchBlobUpload(
[]struct{ k, v string }{},
map[string]string{
"Content-Length": "abc",
"Content-Range": "abc",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{},
)
So(status, ShouldEqual, http.StatusBadRequest)
status = testPatchBlobUpload(
[]struct{ k, v string }{},
map[string]string{
"Content-Length": "100",
"Content-Range": "1-50",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{},
)
So(status, ShouldEqual, http.StatusRequestedRangeNotSatisfiable)
status = testPatchBlobUpload(
[]struct{ k, v string }{},
map[string]string{
"Content-Length": "100",
"Content-Range": "1-100",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) {
return 100, zerr.ErrRepoNotFound
},
},
)
So(status, ShouldEqual, http.StatusNotFound)
status = testPatchBlobUpload(
[]struct{ k, v string }{},
map[string]string{
"Content-Length": "100",
"Content-Range": "1-100",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) {
return 100, zerr.ErrUploadNotFound
},
},
)
So(status, ShouldEqual, http.StatusNotFound)
status = testPatchBlobUpload(
[]struct{ k, v string }{},
map[string]string{
"Content-Length": "100",
"Content-Range": "1-100",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) {
return 100, ErrUnexpectedError
},
},
)
So(status, ShouldEqual, http.StatusInternalServerError)
})
Convey("UpdateBlobUpload", func() {
testUpdateBlobUpload := func(
query []struct{ k, v string },
headers map[string]string,
vars map[string]string,
ism *mocks.MockedImageStore,
) int {
ctlr.StoreController.DefaultStore = ism
request, _ := http.NewRequestWithContext(context.TODO(), "PATCH", baseURL, nil)
request = mux.SetURLVars(request, vars)
q := request.URL.Query()
for _, qe := range query {
q.Add(qe.k, qe.v)
}
request.URL.RawQuery = q.Encode()
for k, v := range headers {
request.Header.Add(k, v)
}
response := httptest.NewRecorder()
rthdlr.UpdateBlobUpload(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
status := testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "",
"Content-Range": "",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{},
)
So(status, ShouldEqual, http.StatusBadRequest)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "100",
"Content-Range": "badRange",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{},
)
So(status, ShouldEqual, http.StatusRequestedRangeNotSatisfiable)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "100",
"Content-Range": "1-100",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) {
return 0, zerr.ErrBadUploadRange
},
},
)
So(status, ShouldEqual, http.StatusBadRequest)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "100",
"Content-Range": "1-100",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) {
return 0, zerr.ErrRepoNotFound
},
},
)
So(status, ShouldEqual, http.StatusNotFound)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "100",
"Content-Range": "1-100",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) {
return 0, zerr.ErrUploadNotFound
},
},
)
So(status, ShouldEqual, http.StatusNotFound)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "100",
"Content-Range": "1-100",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) {
return 0, ErrUnexpectedError
},
},
)
So(status, ShouldEqual, http.StatusInternalServerError)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "0",
"Content-Range": "",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error {
return zerr.ErrBadBlobDigest
},
},
)
So(status, ShouldEqual, http.StatusBadRequest)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "0",
"Content-Range": "",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error {
return zerr.ErrBadUploadRange
},
},
)
So(status, ShouldEqual, http.StatusBadRequest)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "0",
"Content-Range": "",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error {
return zerr.ErrRepoNotFound
},
},
)
So(status, ShouldEqual, http.StatusNotFound)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "0",
"Content-Range": "",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error {
return zerr.ErrUploadNotFound
},
},
)
So(status, ShouldEqual, http.StatusNotFound)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "0",
"Content-Range": "",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error {
return ErrUnexpectedError
},
},
)
So(status, ShouldEqual, http.StatusInternalServerError)
})
Convey("DeleteBlobUpload", func() {
testDeleteBlobUpload := func(
query []struct{ k, v string },
headers map[string]string,
vars map[string]string,
ism *mocks.MockedImageStore,
) int {
ctlr.StoreController.DefaultStore = ism
request, _ := http.NewRequestWithContext(context.TODO(), "PATCH", baseURL, nil)
request = mux.SetURLVars(request, vars)
q := request.URL.Query()
for _, qe := range query {
q.Add(qe.k, qe.v)
}
request.URL.RawQuery = q.Encode()
for k, v := range headers {
request.Header.Add(k, v)
}
response := httptest.NewRecorder()
rthdlr.DeleteBlobUpload(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
status := testDeleteBlobUpload(
[]struct{ k, v string }{},
map[string]string{},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
DeleteBlobUploadFn: func(repo, uuid string) error {
return zerr.ErrRepoNotFound
},
},
)
So(status, ShouldEqual, http.StatusNotFound)
status = testDeleteBlobUpload(
[]struct{ k, v string }{},
map[string]string{},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
DeleteBlobUploadFn: func(repo, uuid string) error {
return zerr.ErrUploadNotFound
},
},
)
So(status, ShouldEqual, http.StatusNotFound)
status = testDeleteBlobUpload(
[]struct{ k, v string }{},
map[string]string{},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
DeleteBlobUploadFn: func(repo, uuid string) error {
return ErrUnexpectedError
},
},
)
So(status, ShouldEqual, http.StatusInternalServerError)
})
Convey("ListRepositories", func() {
testListRepositoriesWithSubstores := func(
query []struct{ k, v string },
headers map[string]string,
vars map[string]string,
ism *mocks.MockedImageStore,
) int {
ctlr.StoreController.DefaultStore = ism
ctlr.StoreController.SubStore = map[string]storage.ImageStore{
"test": &mocks.MockedImageStore{
GetRepositoriesFn: func() ([]string, error) {
return []string{}, ErrUnexpectedError
},
},
}
request, _ := http.NewRequestWithContext(context.TODO(), "GET", baseURL, nil)
request = mux.SetURLVars(request, vars)
q := request.URL.Query()
for _, qe := range query {
q.Add(qe.k, qe.v)
}
request.URL.RawQuery = q.Encode()
for k, v := range headers {
request.Header.Add(k, v)
}
response := httptest.NewRecorder()
rthdlr.ListRepositories(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
testListRepositories := func(
query []struct{ k, v string },
headers map[string]string,
vars map[string]string,
ism *mocks.MockedImageStore,
) int {
ctlr.StoreController.DefaultStore = ism
ctlr.StoreController.SubStore = map[string]storage.ImageStore{}
request, _ := http.NewRequestWithContext(context.TODO(), "PATCH", baseURL, nil)
request = mux.SetURLVars(request, vars)
q := request.URL.Query()
for _, qe := range query {
q.Add(qe.k, qe.v)
}
request.URL.RawQuery = q.Encode()
for k, v := range headers {
request.Header.Add(k, v)
}
response := httptest.NewRecorder()
rthdlr.ListRepositories(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
// with substores
status := testListRepositoriesWithSubstores(
[]struct{ k, v string }{},
map[string]string{},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
GetRepositoriesFn: func() ([]string, error) {
return []string{}, ErrUnexpectedError
},
},
)
So(status, ShouldEqual, http.StatusInternalServerError)
status = testListRepositories(
[]struct{ k, v string }{},
map[string]string{},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
GetRepositoriesFn: func() ([]string, error) {
return []string{}, ErrUnexpectedError
},
},
)
So(status, ShouldEqual, http.StatusInternalServerError)
})
Convey("ListRepositories with Authz", func() {
ctlr.StoreController.DefaultStore = &mocks.MockedImageStore{
GetRepositoriesFn: func() ([]string, error) {
return []string{"repo"}, nil
},
}
ctlr.StoreController.SubStore = map[string]storage.ImageStore{
"test1": &mocks.MockedImageStore{
GetRepositoriesFn: func() ([]string, error) {
return []string{"repo1"}, nil
},
},
"test2": &mocks.MockedImageStore{
GetRepositoriesFn: func() ([]string, error) {
return []string{"repo2"}, nil
},
},
}
// make the user an admin
// acCtx := api.NewAccessControlContext(map[string]bool{}, true)
// ctx := context.WithValue(context.Background(), "ctx", acCtx)
ctx := context.Background()
request, _ := http.NewRequestWithContext(ctx, "GET", baseURL, nil)
request = mux.SetURLVars(request, map[string]string{
"name": "repo",
"session_id": "test",
})
response := httptest.NewRecorder()
rthdlr.ListRepositories(response, request)
resp := response.Result()
defer resp.Body.Close()
So(resp.StatusCode, ShouldEqual, http.StatusOK)
})
Convey("Helper functions", func() {
testUpdateBlobUpload := func(
query []struct{ k, v string },
headers map[string]string,
vars map[string]string,
ism *mocks.MockedImageStore,
) int {
ctlr.StoreController.DefaultStore = ism
request, _ := http.NewRequestWithContext(context.TODO(), "PATCH", baseURL, nil)
request = mux.SetURLVars(request, vars)
q := request.URL.Query()
for _, qe := range query {
q.Add(qe.k, qe.v)
}
request.URL.RawQuery = q.Encode()
for k, v := range headers {
request.Header.Add(k, v)
}
response := httptest.NewRecorder()
rthdlr.UpdateBlobUpload(response, request)
resp := response.Result()
defer resp.Body.Close()
return resp.StatusCode
}
status := testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "0",
"Content-Range": "a-100",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error {
return zerr.ErrUploadNotFound
},
},
)
So(status, ShouldEqual, http.StatusRequestedRangeNotSatisfiable)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "0",
"Content-Range": "20-a",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error {
return zerr.ErrUploadNotFound
},
},
)
So(status, ShouldEqual, http.StatusRequestedRangeNotSatisfiable)
status = testUpdateBlobUpload(
[]struct{ k, v string }{
{"digest", "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"},
},
map[string]string{
"Content-Length": "0",
"Content-Range": "20-1",
},
map[string]string{
"name": "repo",
"session_id": "test",
},
&mocks.MockedImageStore{
FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error {
return zerr.ErrUploadNotFound
},
},
)
So(status, ShouldEqual, http.StatusRequestedRangeNotSatisfiable)
})
})
}