From 76929fb63f39b5d760bd50e9febc93dab8b06c15 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Wed, 10 Dec 2014 22:33:36 -0800 Subject: [PATCH] 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. --- api_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ app.go | 15 +++++++++++++++ routes.go | 6 ++++++ routes_test.go | 5 +++++ urls.go | 14 ++++++++++++++ 5 files changed, 85 insertions(+) diff --git a/api_test.go b/api_test.go index 2c832a17..d6cf34dd 100644 --- a/api_test.go +++ b/api_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "net/http" "net/http/httptest" "net/http/httputil" @@ -22,6 +23,50 @@ import ( "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. func TestLayerAPI(t *testing.T) { // TODO(stevvooe): This test code is complete junk but it should cover the diff --git a/app.go b/app.go index 324cad29..76605f1b 100644 --- a/app.go +++ b/app.go @@ -1,6 +1,7 @@ package registry import ( + "fmt" "net/http" "github.com/docker/docker-registry/storagedriver" @@ -38,6 +39,9 @@ func NewApp(configuration configuration.Configuration) *App { } // 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(routeNameTags, tagsDispatcher) 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) +} diff --git a/routes.go b/routes.go index 440473e9..b291ee4b 100644 --- a/routes.go +++ b/routes.go @@ -6,6 +6,7 @@ import ( ) const ( + routeNameBase = "base" routeNameImageManifest = "image-manifest" routeNameTags = "tags" routeNameBlob = "blob" @@ -27,6 +28,11 @@ func v2APIRouter() *mux.Router { router := mux.NewRouter(). StrictSlash(true) + // GET /v2/ Check Check that the registry implements API version 2(.1) + router. + Path("/v2/"). + Name(routeNameBase) + // 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. diff --git a/routes_test.go b/routes_test.go index 8c514943..6d684a61 100644 --- a/routes_test.go +++ b/routes_test.go @@ -46,6 +46,11 @@ func TestRouter(t *testing.T) { server := httptest.NewServer(router) for _, testcase := range []routeTestCase{ + { + RouteName: routeNameBase, + RequestURI: "/v2/", + Vars: map[string]string{}, + }, { RouteName: routeNameImageManifest, RequestURI: "/v2/foo/bar/manifests/tag", diff --git a/urls.go b/urls.go index 8f34a5b1..92233da4 100644 --- a/urls.go +++ b/urls.go @@ -39,6 +39,20 @@ func newURLBuilderFromString(root string) (*urlBuilder, error) { 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) { route := clonedRoute(ub.router, routeNameTags)