From 94afc30661744ecf24e49b25984e3b00208cb471 Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Thu, 5 Mar 2020 17:46:19 -0700 Subject: [PATCH] routes: lock everything that modifies blobs Now that we're GCing blobs on delete/update manifest, we should lock the blob queries so that they don't race with each other. This is a pretty coarse grained lock, there's probably a better way to do this. Signed-off-by: Tycho Andersen --- pkg/api/routes.go | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/pkg/api/routes.go b/pkg/api/routes.go index 25b23588..02e2722c 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -20,6 +20,7 @@ import ( "sort" "strconv" "strings" + "sync" _ "github.com/anuvu/zot/docs" // nolint (golint) - as required by swaggo "github.com/anuvu/zot/errors" @@ -40,16 +41,25 @@ const ( ) type RouteHandler struct { - c *Controller + c *Controller + blobLock sync.Mutex } func NewRouteHandler(c *Controller) *RouteHandler { - rh := &RouteHandler{c: c} + rh := &RouteHandler{c: c, blobLock: sync.Mutex{}} rh.SetupRoutes() return rh } +func (rh *RouteHandler) blobLockWrapper(f func (w http.ResponseWriter, r *http.Request)) func (w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + rh.blobLock.Lock() + f(w, r) + rh.blobLock.Unlock() + } +} + func (rh *RouteHandler) SetupRoutes() { rh.c.Router.Use(AuthHandler(rh.c)) g := rh.c.Router.PathPrefix(RoutePrefix).Subrouter() @@ -61,25 +71,25 @@ func (rh *RouteHandler) SetupRoutes() { g.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", NameRegexp.String()), rh.GetManifest).Methods("GET") g.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", NameRegexp.String()), - rh.UpdateManifest).Methods("PUT") + rh.blobLockWrapper(rh.UpdateManifest)).Methods("PUT") g.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", NameRegexp.String()), - rh.DeleteManifest).Methods("DELETE") + rh.blobLockWrapper(rh.DeleteManifest)).Methods("DELETE") g.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", NameRegexp.String()), rh.CheckBlob).Methods("HEAD") g.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", NameRegexp.String()), rh.GetBlob).Methods("GET") g.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", NameRegexp.String()), - rh.DeleteBlob).Methods("DELETE") + rh.blobLockWrapper(rh.DeleteBlob)).Methods("DELETE") g.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/", NameRegexp.String()), - rh.CreateBlobUpload).Methods("POST") + rh.blobLockWrapper(rh.CreateBlobUpload)).Methods("POST") g.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", NameRegexp.String()), rh.GetBlobUpload).Methods("GET") g.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", NameRegexp.String()), - rh.PatchBlobUpload).Methods("PATCH") + rh.blobLockWrapper(rh.PatchBlobUpload)).Methods("PATCH") g.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", NameRegexp.String()), - rh.UpdateBlobUpload).Methods("PUT") + rh.blobLockWrapper(rh.UpdateBlobUpload)).Methods("PUT") g.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", NameRegexp.String()), - rh.DeleteBlobUpload).Methods("DELETE") + rh.blobLockWrapper(rh.DeleteBlobUpload)).Methods("DELETE") g.HandleFunc("/_catalog", rh.ListRepositories).Methods("GET") g.HandleFunc("/",