mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 04:17:55 +08:00
fix: don't allow blobs to be deleted if in use (#1559)
dist-spec APIs independently allow deletion of blobs and manifests. Doing the former when in use by an image manifest or index is simply error-prone. So disallow it. Fixes issue #1509 Signed-off-by: Petu Eusebiu <peusebiu@cisco.com> Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com> Co-authored-by: Ramkumar Chinchani <rchincha@cisco.com>
This commit is contained in:
@@ -901,6 +901,75 @@ func TestBasicAuth(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestBlobReferenced(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 := makeController(conf, t.TempDir(), "")
|
||||
|
||||
cm := test.NewControllerManager(ctlr)
|
||||
cm.StartAndWait(port)
|
||||
defer cm.StopServer()
|
||||
|
||||
// without creds, should get access error
|
||||
resp, err := resty.R().Get(baseURL + "/v2/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
|
||||
repoName := "repo"
|
||||
|
||||
cfg, layers, manifest, err := test.GetImageComponents(2)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.UploadImage(
|
||||
test.Image{
|
||||
Config: cfg,
|
||||
Layers: layers,
|
||||
Manifest: manifest,
|
||||
Reference: "1.0",
|
||||
}, baseURL, repoName)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifestContent, err := json.Marshal(manifest)
|
||||
So(err, ShouldBeNil)
|
||||
manifestDigest := godigest.FromBytes(manifestContent)
|
||||
So(manifestDigest, ShouldNotBeNil)
|
||||
|
||||
configContent, err := json.Marshal(cfg)
|
||||
So(err, ShouldBeNil)
|
||||
configDigest := godigest.FromBytes(configContent)
|
||||
So(configDigest, ShouldNotBeNil)
|
||||
|
||||
// delete manifest blob
|
||||
resp, err = resty.R().Delete(baseURL + "/v2/" + repoName + "/blobs/" + manifestDigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusMethodNotAllowed)
|
||||
|
||||
// delete config blob
|
||||
resp, err = resty.R().Delete(baseURL + "/v2/" + repoName + "/blobs/" + configDigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusMethodNotAllowed)
|
||||
|
||||
// delete manifest with manifest api method
|
||||
resp, err = resty.R().Delete(baseURL + "/v2/" + repoName + "/manifests/" + manifestDigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusAccepted)
|
||||
|
||||
// delete blob should work after manifest is deleted
|
||||
resp, err = resty.R().Delete(baseURL + "/v2/" + repoName + "/blobs/" + configDigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusAccepted)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInterruptedBlobUpload(t *testing.T) {
|
||||
Convey("Successfully cleaning interrupted blob uploads", t, func() {
|
||||
port := test.GetFreePort()
|
||||
|
||||
@@ -1091,6 +1091,10 @@ func (rh *RouteHandler) DeleteBlob(response http.ResponseWriter, request *http.R
|
||||
zcommon.WriteJSON(response,
|
||||
http.StatusNotFound,
|
||||
apiErr.NewErrorList(apiErr.NewError(apiErr.BLOB_UNKNOWN, map[string]string{".String()": digest.String()})))
|
||||
} else if errors.Is(err, zerr.ErrBlobReferenced) {
|
||||
zcommon.WriteJSON(response,
|
||||
http.StatusMethodNotAllowed,
|
||||
apiErr.NewErrorList(apiErr.NewError(apiErr.DENIED, map[string]string{".String()": digest.String()})))
|
||||
} else {
|
||||
rh.c.Log.Error().Err(err).Msg("unexpected error")
|
||||
response.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
Reference in New Issue
Block a user