Update the registry app to use the new storage interfaces
Signed-off-by: Stephen J Day <stephen.day@docker.com>master
							parent
							
								
									ea5b999fc0
								
							
						
					
					
						commit
						ff4a1700cc
					
				|  | @ -26,8 +26,8 @@ type App struct { | ||||||
| 	// driver maintains the app global storage driver instance.
 | 	// driver maintains the app global storage driver instance.
 | ||||||
| 	driver storagedriver.StorageDriver | 	driver storagedriver.StorageDriver | ||||||
| 
 | 
 | ||||||
| 	// services contains the main services instance for the application.
 | 	// registry is the primary registry backend for the app instance.
 | ||||||
| 	services *storage.Services | 	registry storage.Registry | ||||||
| 
 | 
 | ||||||
| 	layerHandler storage.LayerHandler | 	layerHandler storage.LayerHandler | ||||||
| 
 | 
 | ||||||
|  | @ -63,7 +63,7 @@ func NewApp(configuration configuration.Configuration) *App { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	app.driver = driver | 	app.driver = driver | ||||||
| 	app.services = storage.NewServices(app.driver) | 	app.registry = storage.NewRegistryWithDriver(app.driver) | ||||||
| 	authType := configuration.Auth.Type() | 	authType := configuration.Auth.Type() | ||||||
| 
 | 
 | ||||||
| 	if authType != "" { | 	if authType != "" { | ||||||
|  | @ -136,11 +136,11 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler { | ||||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
| 		context := app.context(r) | 		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 | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		context.log = log.WithField("name", context.Name) | 		context.log = log.WithField("name", context.Repository.Name()) | ||||||
| 		handler := dispatch(context, r) | 		handler := dispatch(context, r) | ||||||
| 
 | 
 | ||||||
| 		ssrw := &singleStatusResponseWriter{ResponseWriter: w} | 		ssrw := &singleStatusResponseWriter{ResponseWriter: w} | ||||||
|  | @ -165,7 +165,6 @@ func (app *App) context(r *http.Request) *Context { | ||||||
| 	vars := mux.Vars(r) | 	vars := mux.Vars(r) | ||||||
| 	context := &Context{ | 	context := &Context{ | ||||||
| 		App:        app, | 		App:        app, | ||||||
| 		Name:       vars["name"], |  | ||||||
| 		urlBuilder: v2.NewURLBuilderFromRequest(r), | 		urlBuilder: v2.NewURLBuilderFromRequest(r), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -175,19 +174,23 @@ func (app *App) context(r *http.Request) *Context { | ||||||
| 	return context | 	return context | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // authorized checks if the request can proceed with with request access-
 | // authorized checks if the request can proceed with access to the requested
 | ||||||
| // level. If it cannot, the method will return an error.
 | // repository. If it succeeds, the repository will be available on the
 | ||||||
| func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Context) error { | // 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 { | 	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.
 | 		return nil // access controller is not enabled.
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var accessRecords []auth.Access | 	var accessRecords []auth.Access | ||||||
| 
 | 
 | ||||||
| 	if context.Name != "" { | 	if repo != "" { | ||||||
| 		resource := auth.Resource{ | 		resource := auth.Resource{ | ||||||
| 			Type: "repository", | 			Type: "repository", | ||||||
| 			Name: context.Name, | 			Name: repo, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		switch r.Method { | 		switch r.Method { | ||||||
|  | @ -256,6 +259,10 @@ func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Cont | ||||||
| 		return err | 		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 | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,6 +10,8 @@ import ( | ||||||
| 	"github.com/docker/distribution/api/v2" | 	"github.com/docker/distribution/api/v2" | ||||||
| 	_ "github.com/docker/distribution/auth/silly" | 	_ "github.com/docker/distribution/auth/silly" | ||||||
| 	"github.com/docker/distribution/configuration" | 	"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
 | // 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
 | // This only tests the dispatch mechanism. The underlying dispatchers must be
 | ||||||
| // tested individually.
 | // tested individually.
 | ||||||
| func TestAppDispatcher(t *testing.T) { | func TestAppDispatcher(t *testing.T) { | ||||||
|  | 	driver := inmemory.New() | ||||||
| 	app := &App{ | 	app := &App{ | ||||||
| 		Config: configuration.Configuration{}, | 		Config:   configuration.Configuration{}, | ||||||
| 		router: v2.Router(), | 		router:   v2.Router(), | ||||||
|  | 		driver:   driver, | ||||||
|  | 		registry: storage.NewRegistryWithDriver(driver), | ||||||
| 	} | 	} | ||||||
| 	server := httptest.NewServer(app) | 	server := httptest.NewServer(app) | ||||||
| 	router := v2.Router() | 	router := v2.Router() | ||||||
|  | @ -32,8 +37,8 @@ func TestAppDispatcher(t *testing.T) { | ||||||
| 	varCheckingDispatcher := func(expectedVars map[string]string) dispatchFunc { | 	varCheckingDispatcher := func(expectedVars map[string]string) dispatchFunc { | ||||||
| 		return func(ctx *Context, r *http.Request) http.Handler { | 		return func(ctx *Context, r *http.Request) http.Handler { | ||||||
| 			// Always checks the same name context
 | 			// Always checks the same name context
 | ||||||
| 			if ctx.Name != ctx.vars["name"] { | 			if ctx.Repository.Name() != ctx.vars["name"] { | ||||||
| 				t.Fatalf("unexpected name: %q != %q", ctx.Name, "foo/bar") | 				t.Fatalf("unexpected name: %q != %q", ctx.Repository.Name(), "foo/bar") | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// Check that we have all that is expected
 | 			// Check that we have all that is expected
 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ package registry | ||||||
| import ( | import ( | ||||||
| 	"github.com/Sirupsen/logrus" | 	"github.com/Sirupsen/logrus" | ||||||
| 	"github.com/docker/distribution/api/v2" | 	"github.com/docker/distribution/api/v2" | ||||||
|  | 	"github.com/docker/distribution/storage" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Context should contain the request specific context for use in across
 | // 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 points to the application structure that created this context.
 | ||||||
| 	*App | 	*App | ||||||
| 
 | 
 | ||||||
| 	// Name is the prefix for the current request. Corresponds to the
 | 	// Repository is the repository for the current request. All requests
 | ||||||
| 	// namespace/repository associated with the image.
 | 	// should be scoped to a single repository. This field may be nil.
 | ||||||
| 	Name string | 	Repository storage.Repository | ||||||
| 
 | 
 | ||||||
| 	// Errors is a collection of errors encountered during the request to be
 | 	// 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
 | 	// 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.
 | // GetImageManifest fetches the image manifest from the storage backend, if it exists.
 | ||||||
| func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http.Request) { | func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http.Request) { | ||||||
| 	manifests := imh.services.Manifests() | 	manifests := imh.Repository.Manifests() | ||||||
| 	manifest, err := manifests.Get(imh.Name, imh.Tag) | 	manifest, err := manifests.Get(imh.Tag) | ||||||
| 
 | 
 | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		imh.Errors.Push(v2.ErrorCodeManifestUnknown, err) | 		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.
 | // PutImageManifest validates and stores and image in the registry.
 | ||||||
| func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http.Request) { | func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http.Request) { | ||||||
| 	manifests := imh.services.Manifests() | 	manifests := imh.Repository.Manifests() | ||||||
| 	dec := json.NewDecoder(r.Body) | 	dec := json.NewDecoder(r.Body) | ||||||
| 
 | 
 | ||||||
| 	var manifest manifest.SignedManifest | 	var manifest manifest.SignedManifest | ||||||
|  | @ -64,7 +64,7 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http | ||||||
| 		return | 		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
 | 		// TODO(stevvooe): These error handling switches really need to be
 | ||||||
| 		// handled by an app global mapper.
 | 		// handled by an app global mapper.
 | ||||||
| 		switch err := err.(type) { | 		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.
 | // DeleteImageManifest removes the image with the given tag from the registry.
 | ||||||
| func (imh *imageManifestHandler) DeleteImageManifest(w http.ResponseWriter, r *http.Request) { | func (imh *imageManifestHandler) DeleteImageManifest(w http.ResponseWriter, r *http.Request) { | ||||||
| 	manifests := imh.services.Manifests() | 	manifests := imh.Repository.Manifests() | ||||||
| 	if err := manifests.Delete(imh.Name, imh.Tag); err != nil { | 	if err := manifests.Delete(imh.Tag); err != nil { | ||||||
| 		switch err := err.(type) { | 		switch err := err.(type) { | ||||||
| 		case storage.ErrUnknownManifest: | 		case storage.ErrUnknownManifest: | ||||||
| 			imh.Errors.Push(v2.ErrorCodeManifestUnknown, err) | 			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
 | // GetLayer fetches the binary data from backend storage returns it in the
 | ||||||
| // response.
 | // response.
 | ||||||
| func (lh *layerHandler) GetLayer(w http.ResponseWriter, r *http.Request) { | func (lh *layerHandler) GetLayer(w http.ResponseWriter, r *http.Request) { | ||||||
| 	layers := lh.services.Layers() | 	layers := lh.Repository.Layers() | ||||||
| 
 | 	layer, err := layers.Fetch(lh.Digest) | ||||||
| 	layer, err := layers.Fetch(lh.Name, lh.Digest) |  | ||||||
| 
 | 
 | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		switch err := err.(type) { | 		switch err := err.(type) { | ||||||
|  |  | ||||||
|  | @ -43,6 +43,14 @@ func layerUploadDispatcher(ctx *Context, r *http.Request) http.Handler { | ||||||
| 		} | 		} | ||||||
| 		luh.State = state | 		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 { | 		if state.UUID != luh.UUID { | ||||||
| 			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
| 				ctx.log.Infof("mismatched uuid in upload state: %q != %q", state.UUID, luh.UUID) | 				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() | 		layers := ctx.Repository.Layers() | ||||||
| 		upload, err := layers.Resume(luh.Name, luh.UUID) | 		upload, err := layers.Resume(luh.UUID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.log.Errorf("error resolving upload: %v", err) | 			ctx.log.Errorf("error resolving upload: %v", err) | ||||||
| 			if err == storage.ErrLayerUploadUnknown { | 			if err == storage.ErrLayerUploadUnknown { | ||||||
|  | @ -114,8 +122,8 @@ type layerUploadHandler struct { | ||||||
| // StartLayerUpload begins the layer upload process and allocates a server-
 | // StartLayerUpload begins the layer upload process and allocates a server-
 | ||||||
| // side upload session.
 | // side upload session.
 | ||||||
| func (luh *layerUploadHandler) StartLayerUpload(w http.ResponseWriter, r *http.Request) { | func (luh *layerUploadHandler) StartLayerUpload(w http.ResponseWriter, r *http.Request) { | ||||||
| 	layers := luh.services.Layers() | 	layers := luh.Repository.Layers() | ||||||
| 	upload, err := layers.Upload(luh.Name) | 	upload, err := layers.Upload() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
 | 		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
 | ||||||
| 		luh.Errors.Push(v2.ErrorCodeUnknown, err) | 		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.
 | 	// 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.UUID = luh.Upload.UUID() | ||||||
| 	luh.State.Offset = offset | 	luh.State.Offset = offset | ||||||
| 	luh.State.StartedAt = luh.Upload.StartedAt() | 	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.
 | // GetTags returns a json list of tags for a specific image name.
 | ||||||
| func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) { | func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) { | ||||||
| 	defer r.Body.Close() | 	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 { | 	if err != nil { | ||||||
| 		switch err := err.(type) { | 		switch err := err.(type) { | ||||||
| 		case storage.ErrUnknownRepository: | 		case storage.ErrUnknownRepository: | ||||||
| 			w.WriteHeader(404) | 			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: | 		default: | ||||||
| 			th.Errors.PushErr(err) | 			th.Errors.PushErr(err) | ||||||
| 		} | 		} | ||||||
|  | @ -51,7 +51,7 @@ func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) { | ||||||
| 
 | 
 | ||||||
| 	enc := json.NewEncoder(w) | 	enc := json.NewEncoder(w) | ||||||
| 	if err := enc.Encode(tagsAPIResponse{ | 	if err := enc.Encode(tagsAPIResponse{ | ||||||
| 		Name: th.Name, | 		Name: th.Repository.Name(), | ||||||
| 		Tags: tags, | 		Tags: tags, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		th.Errors.PushErr(err) | 		th.Errors.PushErr(err) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue