forked from TrueCloudLab/distribution
Merge pull request #842 from stevvooe/next-generation
Pluralize API methods and add the base endpoint to support API checks
This commit is contained in:
commit
18ace1f454
7 changed files with 113 additions and 28 deletions
45
api_test.go
45
api_test.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
@ -22,6 +23,50 @@ import (
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestCheckAPI hits the base endpoint (/v2/) ensures we return the specified
|
||||||
|
// 200 OK response.
|
||||||
|
func TestCheckAPI(t *testing.T) {
|
||||||
|
config := configuration.Configuration{
|
||||||
|
Storage: configuration.Storage{
|
||||||
|
"inmemory": configuration.Parameters{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
app := NewApp(config)
|
||||||
|
server := httptest.NewServer(handlers.CombinedLoggingHandler(os.Stderr, app))
|
||||||
|
builder, err := newURLBuilderFromString(server.URL)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creating url builder: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
baseURL, err := builder.buildBaseURL()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error building base url: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Get(baseURL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error issuing request: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
checkResponse(t, "issuing api base check", resp, http.StatusOK)
|
||||||
|
checkHeaders(t, resp, http.Header{
|
||||||
|
"Content-Type": []string{"application/json"},
|
||||||
|
"Content-Length": []string{"2"},
|
||||||
|
})
|
||||||
|
|
||||||
|
p, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error reading response body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(p) != "{}" {
|
||||||
|
t.Fatalf("unexpected response body: %v", string(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestLayerAPI conducts a full of the of the layer api.
|
// TestLayerAPI conducts a full of the of the layer api.
|
||||||
func TestLayerAPI(t *testing.T) {
|
func TestLayerAPI(t *testing.T) {
|
||||||
// TODO(stevvooe): This test code is complete junk but it should cover the
|
// TODO(stevvooe): This test code is complete junk but it should cover the
|
||||||
|
|
15
app.go
15
app.go
|
@ -1,6 +1,7 @@
|
||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/docker/docker-registry/storagedriver"
|
"github.com/docker/docker-registry/storagedriver"
|
||||||
|
@ -38,6 +39,9 @@ func NewApp(configuration configuration.Configuration) *App {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the handler dispatchers.
|
// Register the handler dispatchers.
|
||||||
|
app.register(routeNameBase, func(ctx *Context, r *http.Request) http.Handler {
|
||||||
|
return http.HandlerFunc(apiBase)
|
||||||
|
})
|
||||||
app.register(routeNameImageManifest, imageManifestDispatcher)
|
app.register(routeNameImageManifest, imageManifestDispatcher)
|
||||||
app.register(routeNameTags, tagsDispatcher)
|
app.register(routeNameTags, tagsDispatcher)
|
||||||
app.register(routeNameBlob, layerDispatcher)
|
app.register(routeNameBlob, layerDispatcher)
|
||||||
|
@ -134,3 +138,14 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apiBase implements a simple yes-man for doing overall checks against the
|
||||||
|
// api. This can support auth roundtrips to support docker login.
|
||||||
|
func apiBase(w http.ResponseWriter, r *http.Request) {
|
||||||
|
const emptyJSON = "{}"
|
||||||
|
// Provide a simple /v2/ 200 OK response with empty json response.
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Header().Set("Content-Length", fmt.Sprint(len(emptyJSON)))
|
||||||
|
|
||||||
|
fmt.Fprint(w, emptyJSON)
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
14
routes.go
14
routes.go
|
@ -6,6 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
routeNameBase = "base"
|
||||||
routeNameImageManifest = "image-manifest"
|
routeNameImageManifest = "image-manifest"
|
||||||
routeNameTags = "tags"
|
routeNameTags = "tags"
|
||||||
routeNameBlob = "blob"
|
routeNameBlob = "blob"
|
||||||
|
@ -27,11 +28,16 @@ func v2APIRouter() *mux.Router {
|
||||||
router := mux.NewRouter().
|
router := mux.NewRouter().
|
||||||
StrictSlash(true)
|
StrictSlash(true)
|
||||||
|
|
||||||
|
// GET /v2/ Check Check that the registry implements API version 2(.1)
|
||||||
|
router.
|
||||||
|
Path("/v2/").
|
||||||
|
Name(routeNameBase)
|
||||||
|
|
||||||
// GET /v2/<name>/manifest/<tag> Image Manifest Fetch the image manifest 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.
|
// 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 +47,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
|
||||||
|
|
|
@ -46,9 +46,14 @@ func TestRouter(t *testing.T) {
|
||||||
server := httptest.NewServer(router)
|
server := httptest.NewServer(router)
|
||||||
|
|
||||||
for _, testcase := range []routeTestCase{
|
for _, testcase := range []routeTestCase{
|
||||||
|
{
|
||||||
|
RouteName: routeNameBase,
|
||||||
|
RequestURI: "/v2/",
|
||||||
|
Vars: map[string]string{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
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 +68,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 +76,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 +84,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 +99,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 +107,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 +118,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 +128,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,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
|
14
urls.go
14
urls.go
|
@ -39,6 +39,20 @@ func newURLBuilderFromString(root string) (*urlBuilder, error) {
|
||||||
return newURLBuilder(u), nil
|
return newURLBuilder(u), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ub *urlBuilder) buildBaseURL() (string, error) {
|
||||||
|
route := clonedRoute(ub.router, routeNameBase)
|
||||||
|
|
||||||
|
baseURL, err := route.
|
||||||
|
Schemes(ub.url.Scheme).
|
||||||
|
Host(ub.url.Host).
|
||||||
|
URL()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseURL.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ub *urlBuilder) buildTagsURL(name string) (string, error) {
|
func (ub *urlBuilder) buildTagsURL(name string) (string, error) {
|
||||||
route := clonedRoute(ub.router, routeNameTags)
|
route := clonedRoute(ub.router, routeNameTags)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue