From ff4a1700cc6a55af71867b53321ce3afcdc32473 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Fri, 16 Jan 2015 18:32:27 -0800 Subject: [PATCH] Update the registry app to use the new storage interfaces Signed-off-by: Stephen J Day --- registry/app.go | 29 ++++++++++++++++++----------- registry/app_test.go | 13 +++++++++---- registry/context.go | 7 ++++--- registry/images.go | 12 ++++++------ registry/layer.go | 5 ++--- registry/layerupload.go | 18 +++++++++++++----- registry/tags.go | 8 ++++---- 7 files changed, 56 insertions(+), 36 deletions(-) diff --git a/registry/app.go b/registry/app.go index 6a79cdfab..078e33032 100644 --- a/registry/app.go +++ b/registry/app.go @@ -26,8 +26,8 @@ type App struct { // driver maintains the app global storage driver instance. driver storagedriver.StorageDriver - // services contains the main services instance for the application. - services *storage.Services + // registry is the primary registry backend for the app instance. + registry storage.Registry layerHandler storage.LayerHandler @@ -63,7 +63,7 @@ func NewApp(configuration configuration.Configuration) *App { } app.driver = driver - app.services = storage.NewServices(app.driver) + app.registry = storage.NewRegistryWithDriver(app.driver) authType := configuration.Auth.Type() if authType != "" { @@ -136,11 +136,11 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { context := app.context(r) - if err := app.authorized(w, r, context); err != nil { + if err := app.authorized(w, r, context, context.vars["name"]); err != nil { return } - context.log = log.WithField("name", context.Name) + context.log = log.WithField("name", context.Repository.Name()) handler := dispatch(context, r) ssrw := &singleStatusResponseWriter{ResponseWriter: w} @@ -165,7 +165,6 @@ func (app *App) context(r *http.Request) *Context { vars := mux.Vars(r) context := &Context{ App: app, - Name: vars["name"], urlBuilder: v2.NewURLBuilderFromRequest(r), } @@ -175,19 +174,23 @@ func (app *App) context(r *http.Request) *Context { return context } -// authorized checks if the request can proceed with with request access- -// level. If it cannot, the method will return an error. -func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Context) error { +// authorized checks if the request can proceed with access to the requested +// repository. If it succeeds, the repository will be available on the +// context. An error will be if access is not available. +func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Context, repo string) error { if app.accessController == nil { + // No access controller, so we simply provide access. + context.Repository = app.registry.Repository(repo) + return nil // access controller is not enabled. } var accessRecords []auth.Access - if context.Name != "" { + if repo != "" { resource := auth.Resource{ Type: "repository", - Name: context.Name, + Name: repo, } switch r.Method { @@ -256,6 +259,10 @@ func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Cont return err } + // At this point, the request should have access to the repository under + // the requested operation. Make is available on the context. + context.Repository = app.registry.Repository(repo) + return nil } diff --git a/registry/app_test.go b/registry/app_test.go index 4d9535f74..d49c7bbd4 100644 --- a/registry/app_test.go +++ b/registry/app_test.go @@ -10,6 +10,8 @@ import ( "github.com/docker/distribution/api/v2" _ "github.com/docker/distribution/auth/silly" "github.com/docker/distribution/configuration" + "github.com/docker/distribution/storage" + "github.com/docker/distribution/storagedriver/inmemory" ) // TestAppDispatcher builds an application with a test dispatcher and ensures @@ -17,9 +19,12 @@ import ( // This only tests the dispatch mechanism. The underlying dispatchers must be // tested individually. func TestAppDispatcher(t *testing.T) { + driver := inmemory.New() app := &App{ - Config: configuration.Configuration{}, - router: v2.Router(), + Config: configuration.Configuration{}, + router: v2.Router(), + driver: driver, + registry: storage.NewRegistryWithDriver(driver), } server := httptest.NewServer(app) router := v2.Router() @@ -32,8 +37,8 @@ func TestAppDispatcher(t *testing.T) { varCheckingDispatcher := func(expectedVars map[string]string) dispatchFunc { return func(ctx *Context, r *http.Request) http.Handler { // Always checks the same name context - if ctx.Name != ctx.vars["name"] { - t.Fatalf("unexpected name: %q != %q", ctx.Name, "foo/bar") + if ctx.Repository.Name() != ctx.vars["name"] { + t.Fatalf("unexpected name: %q != %q", ctx.Repository.Name(), "foo/bar") } // Check that we have all that is expected diff --git a/registry/context.go b/registry/context.go index 88193cda0..8e8d0fedf 100644 --- a/registry/context.go +++ b/registry/context.go @@ -3,6 +3,7 @@ package registry import ( "github.com/Sirupsen/logrus" "github.com/docker/distribution/api/v2" + "github.com/docker/distribution/storage" ) // Context should contain the request specific context for use in across @@ -12,9 +13,9 @@ type Context struct { // App points to the application structure that created this context. *App - // Name is the prefix for the current request. Corresponds to the - // namespace/repository associated with the image. - Name string + // Repository is the repository for the current request. All requests + // should be scoped to a single repository. This field may be nil. + Repository storage.Repository // Errors is a collection of errors encountered during the request to be // returned to the client API. If errors are added to the collection, the diff --git a/registry/images.go b/registry/images.go index a6b558598..3d6feeed9 100644 --- a/registry/images.go +++ b/registry/images.go @@ -38,8 +38,8 @@ type imageManifestHandler struct { // GetImageManifest fetches the image manifest from the storage backend, if it exists. func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http.Request) { - manifests := imh.services.Manifests() - manifest, err := manifests.Get(imh.Name, imh.Tag) + manifests := imh.Repository.Manifests() + manifest, err := manifests.Get(imh.Tag) if err != nil { imh.Errors.Push(v2.ErrorCodeManifestUnknown, err) @@ -54,7 +54,7 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http // PutImageManifest validates and stores and image in the registry. func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http.Request) { - manifests := imh.services.Manifests() + manifests := imh.Repository.Manifests() dec := json.NewDecoder(r.Body) var manifest manifest.SignedManifest @@ -64,7 +64,7 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http return } - if err := manifests.Put(imh.Name, imh.Tag, &manifest); err != nil { + if err := manifests.Put(imh.Tag, &manifest); err != nil { // TODO(stevvooe): These error handling switches really need to be // handled by an app global mapper. switch err := err.(type) { @@ -96,8 +96,8 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http // DeleteImageManifest removes the image with the given tag from the registry. func (imh *imageManifestHandler) DeleteImageManifest(w http.ResponseWriter, r *http.Request) { - manifests := imh.services.Manifests() - if err := manifests.Delete(imh.Name, imh.Tag); err != nil { + manifests := imh.Repository.Manifests() + if err := manifests.Delete(imh.Tag); err != nil { switch err := err.(type) { case storage.ErrUnknownManifest: imh.Errors.Push(v2.ErrorCodeManifestUnknown, err) diff --git a/registry/layer.go b/registry/layer.go index 836df3b72..bea1cc8b9 100644 --- a/registry/layer.go +++ b/registry/layer.go @@ -42,9 +42,8 @@ type layerHandler struct { // GetLayer fetches the binary data from backend storage returns it in the // response. func (lh *layerHandler) GetLayer(w http.ResponseWriter, r *http.Request) { - layers := lh.services.Layers() - - layer, err := layers.Fetch(lh.Name, lh.Digest) + layers := lh.Repository.Layers() + layer, err := layers.Fetch(lh.Digest) if err != nil { switch err := err.(type) { diff --git a/registry/layerupload.go b/registry/layerupload.go index d597afa69..5cd445a59 100644 --- a/registry/layerupload.go +++ b/registry/layerupload.go @@ -43,6 +43,14 @@ func layerUploadDispatcher(ctx *Context, r *http.Request) http.Handler { } luh.State = state + if state.Name != ctx.Repository.Name() { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx.log.Infof("mismatched repository name in upload state: %q != %q", state.Name, luh.Repository.Name()) + w.WriteHeader(http.StatusBadRequest) + luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err) + }) + } + if state.UUID != luh.UUID { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx.log.Infof("mismatched uuid in upload state: %q != %q", state.UUID, luh.UUID) @@ -51,8 +59,8 @@ func layerUploadDispatcher(ctx *Context, r *http.Request) http.Handler { }) } - layers := ctx.services.Layers() - upload, err := layers.Resume(luh.Name, luh.UUID) + layers := ctx.Repository.Layers() + upload, err := layers.Resume(luh.UUID) if err != nil { ctx.log.Errorf("error resolving upload: %v", err) if err == storage.ErrLayerUploadUnknown { @@ -114,8 +122,8 @@ type layerUploadHandler struct { // StartLayerUpload begins the layer upload process and allocates a server- // side upload session. func (luh *layerUploadHandler) StartLayerUpload(w http.ResponseWriter, r *http.Request) { - layers := luh.services.Layers() - upload, err := layers.Upload(luh.Name) + layers := luh.Repository.Layers() + upload, err := layers.Upload() if err != nil { w.WriteHeader(http.StatusInternalServerError) // Error conditions here? luh.Errors.Push(v2.ErrorCodeUnknown, err) @@ -222,7 +230,7 @@ func (luh *layerUploadHandler) layerUploadResponse(w http.ResponseWriter, r *htt } // TODO(stevvooe): Need a better way to manage the upload state automatically. - luh.State.Name = luh.Name + luh.State.Name = luh.Repository.Name() luh.State.UUID = luh.Upload.UUID() luh.State.Offset = offset luh.State.StartedAt = luh.Upload.StartedAt() diff --git a/registry/tags.go b/registry/tags.go index 18f6add21..1f745c6a9 100644 --- a/registry/tags.go +++ b/registry/tags.go @@ -33,14 +33,14 @@ type tagsAPIResponse struct { // GetTags returns a json list of tags for a specific image name. func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() - manifests := th.services.Manifests() + manifests := th.Repository.Manifests() - tags, err := manifests.Tags(th.Name) + tags, err := manifests.Tags() if err != nil { switch err := err.(type) { case storage.ErrUnknownRepository: w.WriteHeader(404) - th.Errors.Push(v2.ErrorCodeNameUnknown, map[string]string{"name": th.Name}) + th.Errors.Push(v2.ErrorCodeNameUnknown, map[string]string{"name": th.Repository.Name()}) default: th.Errors.PushErr(err) } @@ -51,7 +51,7 @@ func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) { enc := json.NewEncoder(w) if err := enc.Encode(tagsAPIResponse{ - Name: th.Name, + Name: th.Repository.Name(), Tags: tags, }); err != nil { th.Errors.PushErr(err)