Pluralize route API paths

During the specification period, it was suggested that pluralized object names
are more idiomatic in APIs than singular. This changeset simply adopts that
preference for the API going forward. The client has been updated to remain
compatible.
This commit is contained in:
Stephen J Day 2014-12-10 22:29:58 -08:00
parent 500d11564b
commit 2a16a2ff6a
4 changed files with 28 additions and 28 deletions

View File

@ -222,7 +222,7 @@ func (r *clientImpl) ListImageTags(name string) ([]string, error) {
} }
func (r *clientImpl) BlobLength(name string, dgst digest.Digest) (int, error) { func (r *clientImpl) BlobLength(name string, dgst digest.Digest) (int, error) {
response, err := http.Head(fmt.Sprintf("%s/v2/%s/blob/%s", r.Endpoint, name, dgst)) response, err := http.Head(fmt.Sprintf("%s/v2/%s/blobs/%s", r.Endpoint, name, dgst))
if err != nil { if err != nil {
return -1, err return -1, err
} }
@ -255,7 +255,7 @@ func (r *clientImpl) BlobLength(name string, dgst digest.Digest) (int, error) {
func (r *clientImpl) GetBlob(name string, dgst digest.Digest, byteOffset int) (io.ReadCloser, int, error) { func (r *clientImpl) GetBlob(name string, dgst digest.Digest, byteOffset int) (io.ReadCloser, int, error) {
getRequest, err := http.NewRequest("GET", getRequest, err := http.NewRequest("GET",
fmt.Sprintf("%s/v2/%s/blob/%s", r.Endpoint, name, dgst), nil) fmt.Sprintf("%s/v2/%s/blobs/%s", r.Endpoint, name, dgst), nil)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -294,7 +294,7 @@ func (r *clientImpl) GetBlob(name string, dgst digest.Digest, byteOffset int) (i
func (r *clientImpl) InitiateBlobUpload(name string) (string, error) { func (r *clientImpl) InitiateBlobUpload(name string) (string, error) {
postRequest, err := http.NewRequest("POST", postRequest, err := http.NewRequest("POST",
fmt.Sprintf("%s/v2/%s/blob/upload/", r.Endpoint, name), nil) fmt.Sprintf("%s/v2/%s/blobs/uploads/", r.Endpoint, name), nil)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -519,7 +519,7 @@ func (r *clientImpl) CancelBlobUpload(location string) error {
// imageManifestURL is a helper method for returning the full url to an image // imageManifestURL is a helper method for returning the full url to an image
// manifest // manifest
func (r *clientImpl) imageManifestURL(name, tag string) string { func (r *clientImpl) imageManifestURL(name, tag string) string {
return fmt.Sprintf("%s/v2/%s/manifest/%s", r.Endpoint, name, tag) return fmt.Sprintf("%s/v2/%s/manifests/%s", r.Endpoint, name, tag)
} }
// parseRangeHeader parses out the offset and length from a returned Range // parseRangeHeader parses out the offset and length from a returned Range

View File

@ -41,7 +41,7 @@ func TestPush(t *testing.T) {
// because we can't know which blob will get which location. // because we can't know which blob will get which location.
// It's sort of okay because we're using unique digests, but this needs // It's sort of okay because we're using unique digests, but this needs
// to change at some point. // to change at some point.
uploadLocations[i] = fmt.Sprintf("/v2/%s/blob/test-uuid", name) uploadLocations[i] = fmt.Sprintf("/v2/%s/blobs/test-uuid", name)
blobs[i] = storage.FSLayer{BlobSum: blob.digest} blobs[i] = storage.FSLayer{BlobSum: blob.digest}
history[i] = storage.ManifestHistory{V1Compatibility: blob.digest.String()} history[i] = storage.ManifestHistory{V1Compatibility: blob.digest.String()}
} }
@ -66,7 +66,7 @@ func TestPush(t *testing.T) {
blobRequestResponseMappings[2*i] = testutil.RequestResponseMapping{ blobRequestResponseMappings[2*i] = testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "POST", Method: "POST",
Route: "/v2/" + name + "/blob/upload/", Route: "/v2/" + name + "/blobs/uploads/",
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusAccepted, StatusCode: http.StatusAccepted,
@ -94,7 +94,7 @@ func TestPush(t *testing.T) {
handler := testutil.NewHandler(append(blobRequestResponseMappings, testutil.RequestResponseMapping{ handler := testutil.NewHandler(append(blobRequestResponseMappings, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "PUT", Method: "PUT",
Route: "/v2/" + name + "/manifest/" + tag, Route: "/v2/" + name + "/manifests/" + tag,
Body: manifest.Raw, Body: manifest.Raw,
}, },
Response: testutil.Response{ Response: testutil.Response{
@ -185,7 +185,7 @@ func TestPull(t *testing.T) {
blobRequestResponseMappings[i] = testutil.RequestResponseMapping{ blobRequestResponseMappings[i] = testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "GET", Method: "GET",
Route: "/v2/" + name + "/blob/" + blob.digest.String(), Route: "/v2/" + name + "/blobs/" + blob.digest.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -197,7 +197,7 @@ func TestPull(t *testing.T) {
handler := testutil.NewHandler(append(blobRequestResponseMappings, testutil.RequestResponseMapping{ handler := testutil.NewHandler(append(blobRequestResponseMappings, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "GET", Method: "GET",
Route: "/v2/" + name + "/manifest/" + tag, Route: "/v2/" + name + "/manifests/" + tag,
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -292,7 +292,7 @@ func TestPullResume(t *testing.T) {
layerRequestResponseMappings[2*i] = testutil.RequestResponseMapping{ layerRequestResponseMappings[2*i] = testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "GET", Method: "GET",
Route: "/v2/" + name + "/blob/" + blob.digest.String(), Route: "/v2/" + name + "/blobs/" + blob.digest.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -305,7 +305,7 @@ func TestPullResume(t *testing.T) {
layerRequestResponseMappings[2*i+1] = testutil.RequestResponseMapping{ layerRequestResponseMappings[2*i+1] = testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "GET", Method: "GET",
Route: "/v2/" + name + "/blob/" + blob.digest.String(), Route: "/v2/" + name + "/blobs/" + blob.digest.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -318,7 +318,7 @@ func TestPullResume(t *testing.T) {
layerRequestResponseMappings = append(layerRequestResponseMappings, testutil.RequestResponseMapping{ layerRequestResponseMappings = append(layerRequestResponseMappings, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "GET", Method: "GET",
Route: "/v2/" + name + "/manifest/" + tag, Route: "/v2/" + name + "/manifests/" + tag,
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,

View File

@ -31,7 +31,7 @@ func v2APIRouter() *mux.Router {
// PUT /v2/<name>/manifest/<tag> Image Manifest Upload 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. // DELETE /v2/<name>/manifest/<tag> Image Manifest Delete the image identified by name and tag.
router. router.
Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/manifest/{tag:" + common.TagNameRegexp.String() + "}"). Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/manifests/{tag:" + common.TagNameRegexp.String() + "}").
Name(routeNameImageManifest) Name(routeNameImageManifest)
// GET /v2/<name>/tags/list Tags Fetch the tags under the repository identified by name. // GET /v2/<name>/tags/list Tags Fetch the tags under the repository identified by name.
@ -41,19 +41,19 @@ func v2APIRouter() *mux.Router {
// GET /v2/<name>/blob/<digest> Layer Fetch the blob identified by digest. // GET /v2/<name>/blob/<digest> Layer Fetch the blob identified by digest.
router. router.
Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/blob/{digest:[a-zA-Z0-9-_+.]+:[a-zA-Z0-9-_+.=]+}"). Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/blobs/{digest:[a-zA-Z0-9-_+.]+:[a-zA-Z0-9-_+.=]+}").
Name(routeNameBlob) Name(routeNameBlob)
// POST /v2/<name>/blob/upload/ Layer Upload Initiate an upload of the layer identified by tarsum. // POST /v2/<name>/blob/upload/ Layer Upload Initiate an upload of the layer identified by tarsum.
router. router.
Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/blob/upload/"). Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/blobs/uploads/").
Name(routeNameBlobUpload) Name(routeNameBlobUpload)
// GET /v2/<name>/blob/upload/<uuid> Layer Upload Get the status of the upload identified by tarsum 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. // 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 // DELETE /v2/<name>/blob/upload/<uuid> Layer Upload Cancel the upload identified by layer and uuid
router. router.
Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/blob/upload/{uuid}"). Path("/v2/{name:" + common.RepositoryNameRegexp.String() + "}/blobs/uploads/{uuid}").
Name(routeNameBlobUploadResume) Name(routeNameBlobUploadResume)
return router return router

View File

@ -48,7 +48,7 @@ func TestRouter(t *testing.T) {
for _, testcase := range []routeTestCase{ for _, testcase := range []routeTestCase{
{ {
RouteName: routeNameImageManifest, RouteName: routeNameImageManifest,
RequestURI: "/v2/foo/bar/manifest/tag", RequestURI: "/v2/foo/bar/manifests/tag",
Vars: map[string]string{ Vars: map[string]string{
"name": "foo/bar", "name": "foo/bar",
"tag": "tag", "tag": "tag",
@ -63,7 +63,7 @@ func TestRouter(t *testing.T) {
}, },
{ {
RouteName: routeNameBlob, RouteName: routeNameBlob,
RequestURI: "/v2/foo/bar/blob/tarsum.dev+foo:abcdef0919234", RequestURI: "/v2/foo/bar/blobs/tarsum.dev+foo:abcdef0919234",
Vars: map[string]string{ Vars: map[string]string{
"name": "foo/bar", "name": "foo/bar",
"digest": "tarsum.dev+foo:abcdef0919234", "digest": "tarsum.dev+foo:abcdef0919234",
@ -71,7 +71,7 @@ func TestRouter(t *testing.T) {
}, },
{ {
RouteName: routeNameBlob, RouteName: routeNameBlob,
RequestURI: "/v2/foo/bar/blob/sha256:abcdef0919234", RequestURI: "/v2/foo/bar/blobs/sha256:abcdef0919234",
Vars: map[string]string{ Vars: map[string]string{
"name": "foo/bar", "name": "foo/bar",
"digest": "sha256:abcdef0919234", "digest": "sha256:abcdef0919234",
@ -79,14 +79,14 @@ func TestRouter(t *testing.T) {
}, },
{ {
RouteName: routeNameBlobUpload, RouteName: routeNameBlobUpload,
RequestURI: "/v2/foo/bar/blob/upload/", RequestURI: "/v2/foo/bar/blobs/uploads/",
Vars: map[string]string{ Vars: map[string]string{
"name": "foo/bar", "name": "foo/bar",
}, },
}, },
{ {
RouteName: routeNameBlobUploadResume, RouteName: routeNameBlobUploadResume,
RequestURI: "/v2/foo/bar/blob/upload/uuid", RequestURI: "/v2/foo/bar/blobs/uploads/uuid",
Vars: map[string]string{ Vars: map[string]string{
"name": "foo/bar", "name": "foo/bar",
"uuid": "uuid", "uuid": "uuid",
@ -94,7 +94,7 @@ func TestRouter(t *testing.T) {
}, },
{ {
RouteName: routeNameBlobUploadResume, RouteName: routeNameBlobUploadResume,
RequestURI: "/v2/foo/bar/blob/upload/D95306FA-FAD3-4E36-8D41-CF1C93EF8286", RequestURI: "/v2/foo/bar/blobs/uploads/D95306FA-FAD3-4E36-8D41-CF1C93EF8286",
Vars: map[string]string{ Vars: map[string]string{
"name": "foo/bar", "name": "foo/bar",
"uuid": "D95306FA-FAD3-4E36-8D41-CF1C93EF8286", "uuid": "D95306FA-FAD3-4E36-8D41-CF1C93EF8286",
@ -102,7 +102,7 @@ func TestRouter(t *testing.T) {
}, },
{ {
RouteName: routeNameBlobUploadResume, RouteName: routeNameBlobUploadResume,
RequestURI: "/v2/foo/bar/blob/upload/RDk1MzA2RkEtRkFEMy00RTM2LThENDEtQ0YxQzkzRUY4Mjg2IA==", RequestURI: "/v2/foo/bar/blobs/uploads/RDk1MzA2RkEtRkFEMy00RTM2LThENDEtQ0YxQzkzRUY4Mjg2IA==",
Vars: map[string]string{ Vars: map[string]string{
"name": "foo/bar", "name": "foo/bar",
"uuid": "RDk1MzA2RkEtRkFEMy00RTM2LThENDEtQ0YxQzkzRUY4Mjg2IA==", "uuid": "RDk1MzA2RkEtRkFEMy00RTM2LThENDEtQ0YxQzkzRUY4Mjg2IA==",
@ -113,9 +113,9 @@ func TestRouter(t *testing.T) {
// "foo/bar/image/image" and image for "foo/bar/image" with tag // "foo/bar/image/image" and image for "foo/bar/image" with tag
// "tags" // "tags"
RouteName: routeNameImageManifest, RouteName: routeNameImageManifest,
RequestURI: "/v2/foo/bar/manifest/manifest/tags", RequestURI: "/v2/foo/bar/manifests/manifests/tags",
Vars: map[string]string{ Vars: map[string]string{
"name": "foo/bar/manifest", "name": "foo/bar/manifests",
"tag": "tags", "tag": "tags",
}, },
}, },
@ -123,14 +123,14 @@ func TestRouter(t *testing.T) {
// This case presents an ambiguity between foo/bar with tag="tags" // This case presents an ambiguity between foo/bar with tag="tags"
// and list tags for "foo/bar/manifest" // and list tags for "foo/bar/manifest"
RouteName: routeNameTags, RouteName: routeNameTags,
RequestURI: "/v2/foo/bar/manifest/tags/list", RequestURI: "/v2/foo/bar/manifests/tags/list",
Vars: map[string]string{ Vars: map[string]string{
"name": "foo/bar/manifest", "name": "foo/bar/manifests",
}, },
}, },
{ {
RouteName: routeNameBlobUploadResume, RouteName: routeNameBlobUploadResume,
RequestURI: "/v2/foo/../../layer/upload/D95306FA-FAD3-4E36-8D41-CF1C93EF8286", RequestURI: "/v2/foo/../../blob/uploads/D95306FA-FAD3-4E36-8D41-CF1C93EF8286",
StatusCode: http.StatusNotFound, StatusCode: http.StatusNotFound,
}, },
} { } {