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:
parent
594263a3f5
commit
825da388a4
7 changed files with 56 additions and 36 deletions
29
docs/app.go
29
docs/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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue