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:
Stephen J Day 2014-12-10 22:33:36 -08:00
parent 2a16a2ff6a
commit 76929fb63f
5 changed files with 85 additions and 0 deletions

View file

@ -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
View file

@ -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)
}

View file

@ -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.

View file

@ -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
View file

@ -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)