0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00

api: use blob cache path while making hard link

previously mount blob will look for blob that is provided in http request and try to hard link that path
but ideally we should look for path from our cache and do the hard link of that particular path.
this commit does the same.
This commit is contained in:
Shivam Mishra 2021-06-29 11:45:01 -07:00 committed by Ramkumar Chinchani
parent 3a59b9f487
commit af30c06aff
4 changed files with 65 additions and 44 deletions

View file

@ -1801,6 +1801,7 @@ func TestCrossRepoMount(t *testing.T) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
c.Config.Storage.RootDirectory = dir c.Config.Storage.RootDirectory = dir
@ -1825,6 +1826,9 @@ func TestCrossRepoMount(t *testing.T) {
params := make(map[string]string) params := make(map[string]string)
digest := "sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29" digest := "sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29"
d := godigest.Digest(digest)
name := "zot-cve-test" name := "zot-cve-test"
params["mount"] = digest params["mount"] = digest
@ -1836,6 +1840,7 @@ func TestCrossRepoMount(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(headResponse.StatusCode(), ShouldEqual, 200) So(headResponse.StatusCode(), ShouldEqual, 200)
// All invalid request of mount should return 202.
params["mount"] = "sha:" params["mount"] = "sha:"
postResponse, err := client.R(). postResponse, err := client.R().
@ -1855,12 +1860,13 @@ func TestCrossRepoMount(t *testing.T) {
So(postResponse.StatusCode(), ShouldEqual, 202) So(postResponse.StatusCode(), ShouldEqual, 202)
// Use correct request // Use correct request
// This is correct request but it will return 202 because blob is not present in cache.
params["mount"] = digest params["mount"] = digest
postResponse, err = client.R(). postResponse, err = client.R().
SetBasicAuth(username, passphrase).SetQueryParams(params). SetBasicAuth(username, passphrase).SetQueryParams(params).
Post(baseURL + "/v2/zot-c-test/blobs/uploads/") Post(baseURL + "/v2/zot-c-test/blobs/uploads/")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(postResponse.StatusCode(), ShouldEqual, 201) So(postResponse.StatusCode(), ShouldEqual, 202)
// Send same request again // Send same request again
postResponse, err = client.R(). postResponse, err = client.R().
@ -1874,7 +1880,7 @@ func TestCrossRepoMount(t *testing.T) {
SetBasicAuth(username, passphrase).SetQueryParams(params). SetBasicAuth(username, passphrase).SetQueryParams(params).
Post(baseURL + "/v2/zot-d-test/blobs/uploads/") Post(baseURL + "/v2/zot-d-test/blobs/uploads/")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(postResponse.StatusCode(), ShouldEqual, 201) So(postResponse.StatusCode(), ShouldEqual, 202)
headResponse, err = client.R().SetBasicAuth(username, passphrase). headResponse, err = client.R().SetBasicAuth(username, passphrase).
Head(fmt.Sprintf("%s/v2/zot-cv-test/blobs/%s", baseURL, digest)) Head(fmt.Sprintf("%s/v2/zot-cv-test/blobs/%s", baseURL, digest))
@ -1907,6 +1913,47 @@ func TestCrossRepoMount(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(postResponse.StatusCode(), ShouldEqual, 201) So(postResponse.StatusCode(), ShouldEqual, 201)
// We have uploaded a blob and since we have provided digest it should be full blob upload and there should be entry
// in cache, now try mount blob request status and it should be 201 because now blob is present in cache
// and it should do hard link.
params["mount"] = digest
postResponse, err = client.R().
SetBasicAuth(username, passphrase).SetQueryParams(params).
Post(baseURL + "/v2/zot-mount-test/blobs/uploads/")
So(err, ShouldBeNil)
So(postResponse.StatusCode(), ShouldEqual, 201)
// Check os.SameFile here
cachePath := path.Join(c.Config.Storage.RootDirectory, "zot-d-test", "blobs/sha256", d.Hex())
cacheFi, err := os.Stat(cachePath)
So(err, ShouldBeNil)
linkPath := path.Join(c.Config.Storage.RootDirectory, "zot-mount-test", "blobs/sha256", d.Hex())
linkFi, err := os.Stat(linkPath)
So(err, ShouldBeNil)
So(os.SameFile(cacheFi, linkFi), ShouldEqual, true)
// Now try another mount request and this time it should be from above uploaded repo i.e zot-mount-test
// mount request should pass and should return 201.
params["mount"] = digest
params["from"] = "zot-mount-test"
postResponse, err = client.R().
SetBasicAuth(username, passphrase).SetQueryParams(params).
Post(baseURL + "/v2/zot-mount1-test/blobs/uploads/")
So(err, ShouldBeNil)
So(postResponse.StatusCode(), ShouldEqual, 201)
linkPath = path.Join(c.Config.Storage.RootDirectory, "zot-mount1-test", "blobs/sha256", d.Hex())
linkFi, err = os.Stat(linkPath)
So(err, ShouldBeNil)
So(os.SameFile(cacheFi, linkFi), ShouldEqual, true)
headResponse, err = client.R().SetBasicAuth(username, passphrase). headResponse, err = client.R().SetBasicAuth(username, passphrase).
Head(fmt.Sprintf("%s/v2/zot-cv-test/blobs/%s", baseURL, digest)) Head(fmt.Sprintf("%s/v2/zot-cv-test/blobs/%s", baseURL, digest))
So(err, ShouldBeNil) So(err, ShouldBeNil)

View file

@ -497,9 +497,7 @@ func (rh *RouteHandler) CheckBlob(w http.ResponseWriter, r *http.Request) {
return return
} }
mediaType := r.Header.Get("Accept") ok, blen, err := is.CheckBlob(name, digest)
ok, blen, err := is.CheckBlob(name, digest, mediaType)
if err != nil { if err != nil {
switch err { switch err {
case errors.ErrBadBlobDigest: case errors.ErrBadBlobDigest:
@ -662,8 +660,10 @@ func (rh *RouteHandler) CreateBlobUpload(w http.ResponseWriter, r *http.Request)
return return
} }
// zot does not support cross mounting directly and do a workaround by copying blob using hard link // zot does not support cross mounting directly and do a workaround creating using hard link.
err := is.MountBlob(name, from[0], mountDigests[0]) // check blob looks for actual path (name+mountDigests[0]) first then look for cache and
// if found in cache, will do hard link and if fails we will start new upload.
_, _, err := is.CheckBlob(name, mountDigests[0])
if err != nil { if err != nil {
u, err := is.NewBlobUpload(name) u, err := is.NewBlobUpload(name)
if err != nil { if err != nil {

View file

@ -1024,40 +1024,8 @@ func (is *ImageStore) BlobPath(repo string, digest godigest.Digest) string {
return path.Join(is.rootDir, repo, "blobs", digest.Algorithm().String(), digest.Encoded()) return path.Join(is.rootDir, repo, "blobs", digest.Algorithm().String(), digest.Encoded())
} }
func (is *ImageStore) MountBlob(repo string, mountRepo string, digest string) error {
d, err := godigest.Parse(digest)
if err != nil {
is.log.Error().Err(err).Str("digest", digest).Msg("failed to parse digest")
return errors.ErrBadBlobDigest
}
mountBlobPath := is.BlobPath(mountRepo, d)
is.log.Debug().Str("mount path", mountBlobPath)
blobPath := is.BlobPath(repo, d)
is.log.Debug().Str("repo path", blobPath)
_, err = os.Stat(mountBlobPath)
if err != nil {
is.log.Error().Err(err).Msg("mount: blob path not found")
return errors.ErrBlobNotFound
}
_, err = is.copyBlob(repo, blobPath, mountBlobPath)
if err != nil {
is.log.Error().Err(err).Msg("cache: error copying blobs from cache location")
return err
}
return nil
}
// CheckBlob verifies a blob and returns true if the blob is correct. // CheckBlob verifies a blob and returns true if the blob is correct.
func (is *ImageStore) CheckBlob(repo string, digest string, func (is *ImageStore) CheckBlob(repo string, digest string) (bool, int64, error) {
mediaType string) (bool, int64, error) {
d, err := godigest.Parse(digest) d, err := godigest.Parse(digest)
if err != nil { if err != nil {
is.log.Error().Err(err).Str("digest", digest).Msg("failed to parse digest") is.log.Error().Err(err).Str("digest", digest).Msg("failed to parse digest")
@ -1097,6 +1065,12 @@ func (is *ImageStore) CheckBlob(repo string, digest string,
return false, -1, errors.ErrBlobNotFound return false, -1, errors.ErrBlobNotFound
} }
if err := is.cache.PutBlob(digest, blobPath); err != nil {
is.log.Error().Err(err).Str("blobPath", blobPath).Msg("dedupe: unable to insert blob record")
return false, -1, err
}
return true, blobSize, nil return true, blobSize, nil
} }

View file

@ -102,7 +102,7 @@ func TestAPIs(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(b, ShouldEqual, l) So(b, ShouldEqual, l)
_, _, err = il.CheckBlob("test", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip") _, _, err = il.CheckBlob("test", d.String())
So(err, ShouldBeNil) So(err, ShouldBeNil)
_, _, err = il.GetBlob("test", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip") _, _, err = il.GetBlob("test", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip")
@ -201,7 +201,7 @@ func TestAPIs(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(b, ShouldEqual, l) So(b, ShouldEqual, l)
_, _, err = il.CheckBlob("test", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip") _, _, err = il.CheckBlob("test", d.String())
So(err, ShouldBeNil) So(err, ShouldBeNil)
_, _, err = il.GetBlob("test", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip") _, _, err = il.GetBlob("test", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip")
@ -363,7 +363,7 @@ func TestAPIs(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(b, ShouldEqual, l) So(b, ShouldEqual, l)
_, _, err = il.CheckBlob("dedupe1", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip") _, _, err = il.CheckBlob("dedupe1", d.String())
So(err, ShouldBeNil) So(err, ShouldBeNil)
_, _, err = il.GetBlob("dedupe1", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip") _, _, err = il.GetBlob("dedupe1", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip")
@ -412,7 +412,7 @@ func TestAPIs(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(b, ShouldEqual, l) So(b, ShouldEqual, l)
_, _, err = il.CheckBlob("dedupe2", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip") _, _, err = il.CheckBlob("dedupe2", d.String())
So(err, ShouldBeNil) So(err, ShouldBeNil)
_, _, err = il.GetBlob("dedupe2", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip") _, _, err = il.GetBlob("dedupe2", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip")