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 636352505..bc7df554e 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 e0fa727fe..bb78044a9 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 96920a8e5..82a1e6d97 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 3eb2ff9ab..8916b5524 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 8da7c3e2c..4aa0097f6 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 7ca51ae5f..9085d3029 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, }, } {