Update the registry app to use the new storage interfaces

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day 2015-01-16 18:32:27 -08:00
parent ea5b999fc0
commit ff4a1700cc
7 changed files with 56 additions and 36 deletions

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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) {

View file

@ -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()

View file

@ -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)