forked from TrueCloudLab/distribution
Implement V2 API base endpoint
This implements a base endpoint that will respond with a 200 OK and an empty json response. Such an endpoint can be used as to ping the v2 service or as an endpoint to check authorization status.
This commit is contained in:
parent
2a16a2ff6a
commit
76929fb63f
5 changed files with 85 additions and 0 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)
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
routeNameBase = "base"
|
||||||
routeNameImageManifest = "image-manifest"
|
routeNameImageManifest = "image-manifest"
|
||||||
routeNameTags = "tags"
|
routeNameTags = "tags"
|
||||||
routeNameBlob = "blob"
|
routeNameBlob = "blob"
|
||||||
|
@ -27,6 +28,11 @@ 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.
|
||||||
|
|
|
@ -46,6 +46,11 @@ 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/manifests/tag",
|
RequestURI: "/v2/foo/bar/manifests/tag",
|
||||||
|
|
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