From 5789ca7629cd147f04ecbe922b5abc602a121d98 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Tue, 18 Nov 2014 19:38:14 -0800 Subject: [PATCH] Update routes to reflect specification changes To bring the implementation inline with the specification, the names and structure of the API routes have been updated. The overloaded term "image" has been replaced with the term "manifest", which may also be known as "image manifest". The desire for the layer storage to be more of a general blob storage is reflected in moving from "layer" api prefixes to "blob". The "tarsum" path parameter has been replaced by a more general "digest" parameter and is no longer required to start uploads. Another set of changes will come along to support this change at the storage service layer. --- app.go | 6 ++--- app_test.go | 10 +++----- layer.go | 3 ++- layerupload.go | 1 + routes.go | 46 +++++++++++++++++----------------- routes_test.go | 67 ++++++++++++++++++++++++++++---------------------- 6 files changed, 70 insertions(+), 63 deletions(-) diff --git a/app.go b/app.go index 63635250..bc7df554 100644 --- a/app.go +++ b/app.go @@ -29,10 +29,10 @@ func NewApp(configuration configuration.Configuration) *App { // Register the handler dispatchers. app.register(routeNameImageManifest, imageManifestDispatcher) - app.register(routeNameLayer, layerDispatcher) + app.register(routeNameBlob, layerDispatcher) app.register(routeNameTags, tagsDispatcher) - app.register(routeNameLayerUpload, layerUploadDispatcher) - app.register(routeNameLayerUploadResume, layerUploadDispatcher) + app.register(routeNameBlobUpload, layerUploadDispatcher) + app.register(routeNameBlobUploadResume, layerUploadDispatcher) return app } diff --git a/app_test.go b/app_test.go index e0fa727f..bb78044a 100644 --- a/app_test.go +++ b/app_test.go @@ -84,24 +84,22 @@ func TestAppDispatcher(t *testing.T) { }, }, { - endpoint: routeNameLayer, + endpoint: routeNameBlob, vars: []string{ "name", "foo/bar", - "tarsum", "tarsum.v1+bogus:abcdef0123456789", + "digest", "tarsum.v1+bogus:abcdef0123456789", }, }, { - endpoint: routeNameLayerUpload, + endpoint: routeNameBlobUpload, vars: []string{ "name", "foo/bar", - "tarsum", "tarsum.v1+bogus:abcdef0123456789", }, }, { - endpoint: routeNameLayerUploadResume, + endpoint: routeNameBlobUploadResume, vars: []string{ "name", "foo/bar", - "tarsum", "tarsum.v1+bogus:abcdef0123456789", "uuid", "theuuid", }, }, diff --git a/layer.go b/layer.go index 96920a8e..82a1e6d9 100644 --- a/layer.go +++ b/layer.go @@ -16,7 +16,8 @@ func layerDispatcher(ctx *Context, r *http.Request) http.Handler { layerHandler.log = layerHandler.log.WithField("tarsum", layerHandler.TarSum) return handlers.MethodHandler{ - "GET": http.HandlerFunc(layerHandler.GetLayer), + "GET": http.HandlerFunc(layerHandler.GetLayer), + "HEAD": http.HandlerFunc(layerHandler.GetLayer), } } diff --git a/layerupload.go b/layerupload.go index 3eb2ff9a..8916b552 100644 --- a/layerupload.go +++ b/layerupload.go @@ -24,6 +24,7 @@ func layerUploadDispatcher(ctx *Context, r *http.Request) http.Handler { return handlers.MethodHandler{ "POST": http.HandlerFunc(layerUploadHandler.StartLayerUpload), "GET": http.HandlerFunc(layerUploadHandler.GetUploadStatus), + "HEAD": http.HandlerFunc(layerUploadHandler.GetUploadStatus), "PUT": http.HandlerFunc(layerUploadHandler.PutLayerChunk), "DELETE": http.HandlerFunc(layerUploadHandler.CancelLayerUpload), } diff --git a/routes.go b/routes.go index 8da7c3e2..4aa0097f 100644 --- a/routes.go +++ b/routes.go @@ -6,19 +6,19 @@ import ( ) const ( - routeNameImageManifest = "image-manifest" - routeNameTags = "tags" - routeNameLayer = "layer" - routeNameLayerUpload = "layer-upload" - routeNameLayerUploadResume = "layer-upload-resume" + routeNameImageManifest = "image-manifest" + routeNameTags = "tags" + routeNameBlob = "blob" + routeNameBlobUpload = "blob-upload" + routeNameBlobUploadResume = "blob-upload-resume" ) var allEndpoints = []string{ routeNameImageManifest, routeNameTags, - routeNameLayer, - routeNameLayerUpload, - routeNameLayerUploadResume, + routeNameBlob, + routeNameBlobUpload, + routeNameBlobUploadResume, } // v2APIRouter builds a gorilla router with named routes for the various API @@ -27,11 +27,11 @@ func v2APIRouter() *mux.Router { router := mux.NewRouter(). StrictSlash(true) - // GET /v2//image/ Image Manifest Fetch the image manifest identified by name and tag. - // PUT /v2//image/ Image Manifest Upload the image manifest identified by name and tag. - // DELETE /v2//image/ Image Manifest Delete the image identified by name and tag. + // GET /v2//manifest/ Image Manifest Fetch the image manifest identified by name and tag. + // PUT /v2//manifest/ Image Manifest Upload the image manifest identified by name and tag. + // DELETE /v2//manifest/ Image Manifest Delete the image identified by name and tag. router. - Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/image/{tag:" + common.TagNameRegexp.String() + "}"). + Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/manifest/{tag:" + common.TagNameRegexp.String() + "}"). Name(routeNameImageManifest) // GET /v2//tags/list Tags Fetch the tags under the repository identified by name. @@ -39,22 +39,22 @@ func v2APIRouter() *mux.Router { Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/tags/list"). Name(routeNameTags) - // GET /v2//layer/ Layer Fetch the layer identified by tarsum. + // GET /v2//blob/ Layer Fetch the blob identified by digest. router. - Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/layer/{tarsum:" + common.TarsumRegexp.String() + "}"). - Name(routeNameLayer) + Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/blob/{digest:[a-zA-Z0-9-_+.]+:[a-zA-Z0-9-_+.=]+}"). + Name(routeNameBlob) - // POST /v2//layer//upload/ Layer Upload Initiate an upload of the layer identified by tarsum. Requires length and a checksum parameter. + // POST /v2//blob/upload/ Layer Upload Initiate an upload of the layer identified by tarsum. router. - Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/layer/{tarsum:" + common.TarsumRegexp.String() + "}/upload/"). - Name(routeNameLayerUpload) + Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/blob/upload/"). + Name(routeNameBlobUpload) - // GET /v2//layer//upload/ Layer Upload Get the status of the upload identified by tarsum and uuid. - // PUT /v2//layer//upload/ Layer Upload Upload all or a chunk of the upload identified by tarsum and uuid. - // DELETE /v2//layer//upload/ Layer Upload Cancel the upload identified by layer and uuid + // GET /v2//blob/upload/ Layer Upload Get the status of the upload identified by tarsum and uuid. + // PUT /v2//blob/upload/ Layer Upload Upload all or a chunk of the upload identified by tarsum and uuid. + // DELETE /v2//blob/upload/ Layer Upload Cancel the upload identified by layer and uuid router. - Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/layer/{tarsum:" + common.TarsumRegexp.String() + "}/upload/{uuid}"). - Name(routeNameLayerUploadResume) + Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/blob/upload/{uuid}"). + Name(routeNameBlobUploadResume) return router } diff --git a/routes_test.go b/routes_test.go index 7ca51ae5..9085d302 100644 --- a/routes_test.go +++ b/routes_test.go @@ -48,7 +48,7 @@ func TestRouter(t *testing.T) { for _, testcase := range []routeTestCase{ { RouteName: routeNameImageManifest, - RequestURI: "/v2/foo/bar/image/tag", + RequestURI: "/v2/foo/bar/manifest/tag", Vars: map[string]string{ "name": "foo/bar", "tag": "tag", @@ -62,68 +62,75 @@ func TestRouter(t *testing.T) { }, }, { - RouteName: routeNameLayer, - RequestURI: "/v2/foo/bar/layer/tarsum.dev+foo:abcdef0919234", + RouteName: routeNameBlob, + RequestURI: "/v2/foo/bar/blob/tarsum.dev+foo:abcdef0919234", Vars: map[string]string{ "name": "foo/bar", - "tarsum": "tarsum.dev+foo:abcdef0919234", + "digest": "tarsum.dev+foo:abcdef0919234", }, }, { - RouteName: routeNameLayerUpload, - RequestURI: "/v2/foo/bar/layer/tarsum.dev+foo:abcdef0919234/upload/", + RouteName: routeNameBlob, + RequestURI: "/v2/foo/bar/blob/sha256:abcdef0919234", Vars: map[string]string{ "name": "foo/bar", - "tarsum": "tarsum.dev+foo:abcdef0919234", + "digest": "sha256:abcdef0919234", }, }, { - RouteName: routeNameLayerUploadResume, - RequestURI: "/v2/foo/bar/layer/tarsum.dev+foo:abcdef0919234/upload/uuid", + RouteName: routeNameBlobUpload, + RequestURI: "/v2/foo/bar/blob/upload/", Vars: map[string]string{ - "name": "foo/bar", - "tarsum": "tarsum.dev+foo:abcdef0919234", - "uuid": "uuid", + "name": "foo/bar", }, }, { - RouteName: routeNameLayerUploadResume, - RequestURI: "/v2/foo/bar/layer/tarsum.dev+foo:abcdef0919234/upload/D95306FA-FAD3-4E36-8D41-CF1C93EF8286", + RouteName: routeNameBlobUploadResume, + RequestURI: "/v2/foo/bar/blob/upload/uuid", Vars: map[string]string{ - "name": "foo/bar", - "tarsum": "tarsum.dev+foo:abcdef0919234", - "uuid": "D95306FA-FAD3-4E36-8D41-CF1C93EF8286", + "name": "foo/bar", + "uuid": "uuid", + }, + }, + { + RouteName: routeNameBlobUploadResume, + RequestURI: "/v2/foo/bar/blob/upload/D95306FA-FAD3-4E36-8D41-CF1C93EF8286", + Vars: map[string]string{ + "name": "foo/bar", + "uuid": "D95306FA-FAD3-4E36-8D41-CF1C93EF8286", + }, + }, + { + RouteName: routeNameBlobUploadResume, + RequestURI: "/v2/foo/bar/blob/upload/RDk1MzA2RkEtRkFEMy00RTM2LThENDEtQ0YxQzkzRUY4Mjg2IA==", + Vars: map[string]string{ + "name": "foo/bar", + "uuid": "RDk1MzA2RkEtRkFEMy00RTM2LThENDEtQ0YxQzkzRUY4Mjg2IA==", }, }, - { // Check ambiguity: ensure we can distinguish between tags for // "foo/bar/image/image" and image for "foo/bar/image" with tag // "tags" RouteName: routeNameImageManifest, - RequestURI: "/v2/foo/bar/image/image/tags", + RequestURI: "/v2/foo/bar/manifest/manifest/tags", Vars: map[string]string{ - "name": "foo/bar/image", + "name": "foo/bar/manifest", "tag": "tags", }, }, { // This case presents an ambiguity between foo/bar with tag="tags" - // and list tags for "foo/bar/image" + // and list tags for "foo/bar/manifest" RouteName: routeNameTags, - RequestURI: "/v2/foo/bar/image/tags/list", + RequestURI: "/v2/foo/bar/manifest/tags/list", Vars: map[string]string{ - "name": "foo/bar/image", + "name": "foo/bar/manifest", }, }, { - RouteName: routeNameLayerUploadResume, - RequestURI: "/v2/foo/../../layer/tarsum.dev+foo:abcdef0919234/upload/D95306FA-FAD3-4E36-8D41-CF1C93EF8286", - Vars: map[string]string{ - "name": "foo/bar", - "tarsum": "tarsum.dev+foo:abcdef0919234", - "uuid": "D95306FA-FAD3-4E36-8D41-CF1C93EF8286", - }, + RouteName: routeNameBlobUploadResume, + RequestURI: "/v2/foo/../../layer/upload/D95306FA-FAD3-4E36-8D41-CF1C93EF8286", StatusCode: http.StatusNotFound, }, } {