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:
peusebiu
2023-07-10 12:24:45 +03:00
committed by GitHub
parent b22989cfe0
commit cda6916b45
11 changed files with 560 additions and 38 deletions
+69
View File
@@ -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()
+4
View File
@@ -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)