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.master
							parent
							
								
									9dc6fa3765
								
							
						
					
					
						commit
						5789ca7629
					
				
							
								
								
									
										6
									
								
								app.go
								
								
								
								
							
							
						
						
									
										6
									
								
								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 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										10
									
								
								app_test.go
								
								
								
								
							
							
						
						
									
										10
									
								
								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", | ||||
| 			}, | ||||
| 		}, | ||||
|  |  | |||
							
								
								
									
										3
									
								
								layer.go
								
								
								
								
							
							
						
						
									
										3
									
								
								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), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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), | ||||
| 	} | ||||
|  |  | |||
							
								
								
									
										46
									
								
								routes.go
								
								
								
								
							
							
						
						
									
										46
									
								
								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/<name>/image/<tag>	Image Manifest	Fetch the image manifest identified by name and tag.
 | ||||
| 	// PUT      /v2/<name>/image/<tag>	Image Manifest	Upload the image manifest identified by name and tag.
 | ||||
| 	// DELETE   /v2/<name>/image/<tag>	Image Manifest	Delete the image identified by name and tag.
 | ||||
| 	// GET      /v2/<name>/manifest/<tag>	Image Manifest	Fetch the image manifest identified by name and tag.
 | ||||
| 	// PUT      /v2/<name>/manifest/<tag>	Image Manifest	Upload the image manifest identified by name and tag.
 | ||||
| 	// DELETE   /v2/<name>/manifest/<tag>	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/<name>/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/<name>/layer/<tarsum>	Layer	Fetch the layer identified by tarsum.
 | ||||
| 	// GET	/v2/<name>/blob/<digest>	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/<name>/layer/<tarsum>/upload/	Layer Upload	Initiate an upload of the layer identified by tarsum. Requires length and a checksum parameter.
 | ||||
| 	// POST	/v2/<name>/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/<name>/layer/<tarsum>/upload/<uuid>	Layer Upload	Get the status of the upload identified by tarsum and uuid.
 | ||||
| 	// PUT	/v2/<name>/layer/<tarsum>/upload/<uuid>	Layer Upload	Upload all or a chunk of the upload identified by tarsum and uuid.
 | ||||
| 	// DELETE	/v2/<name>/layer/<tarsum>/upload/<uuid>	Layer Upload	Cancel the upload identified by layer and uuid
 | ||||
| 	// GET	/v2/<name>/blob/upload/<uuid>	Layer Upload	Get the status of the upload identified by tarsum and uuid.
 | ||||
| 	// PUT	/v2/<name>/blob/upload/<uuid>	Layer Upload	Upload all or a chunk of the upload identified by tarsum and uuid.
 | ||||
| 	// DELETE	/v2/<name>/blob/upload/<uuid>	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 | ||||
| } | ||||
|  |  | |||
|  | @ -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, | ||||
| 		}, | ||||
| 	} { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue