Add an "enabled" parameter under "readonly", and make it as if the mutable handlers don't exist when read-only mode is enabled

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2015-08-06 18:02:43 -07:00
parent df9758ba39
commit cbf83ecd31
7 changed files with 40 additions and 45 deletions

View file

@ -133,14 +133,4 @@ var (
longer proceed.`,
HTTPStatusCode: http.StatusNotFound,
})
// ErrorCodeMaintenanceMode is returned when an upload can't be
// accepted because the registry is in maintenance mode.
ErrorCodeMaintenanceMode = errcode.Register(errGroup, errcode.ErrorDescriptor{
Value: "MAINTENANCE_MODE",
Message: "registry in maintenance mode",
Description: `The upload cannot be accepted because the registry
is running read-only in maintenance mode.`,
HTTPStatusCode: http.StatusServiceUnavailable,
})
)

View file

@ -658,7 +658,7 @@ func TestDeleteReadOnly(t *testing.T) {
t.Fatalf("unexpected error deleting layer: %v", err)
}
checkResponse(t, "deleting layer in read-only mode", resp, http.StatusServiceUnavailable)
checkResponse(t, "deleting layer in read-only mode", resp, http.StatusMethodNotAllowed)
}
func TestStartPushReadOnly(t *testing.T) {
@ -678,7 +678,7 @@ func TestStartPushReadOnly(t *testing.T) {
}
defer resp.Body.Close()
checkResponse(t, "starting push in read-only mode", resp, http.StatusServiceUnavailable)
checkResponse(t, "starting push in read-only mode", resp, http.StatusMethodNotAllowed)
}
func httpDelete(url string) (*http.Response, error) {

View file

@ -109,9 +109,15 @@ func NewApp(ctx context.Context, configuration *configuration.Configuration) *Ap
}
}
if v, ok := mc["readonly"]; ok {
app.readOnly, ok = v.(bool)
readOnly, ok := v.(map[interface{}]interface{})
if !ok {
panic("readonly config key must have a boolean value")
panic("readonly config key must contain additional keys")
}
if readOnlyEnabled, ok := readOnly["enabled"]; ok {
app.readOnly, ok = readOnlyEnabled.(bool)
if !ok {
panic("readonly's enabled config key must have a boolean value")
}
}
}
}

View file

@ -32,11 +32,16 @@ func blobDispatcher(ctx *Context, r *http.Request) http.Handler {
Digest: dgst,
}
return handlers.MethodHandler{
mhandler := handlers.MethodHandler{
"GET": http.HandlerFunc(blobHandler.GetBlob),
"HEAD": http.HandlerFunc(blobHandler.GetBlob),
"DELETE": mutableHandler(blobHandler.DeleteBlob, ctx),
}
if !ctx.readOnly {
mhandler["DELETE"] = http.HandlerFunc(blobHandler.DeleteBlob)
}
return mhandler
}
// blobHandler serves http blob requests.

View file

@ -22,14 +22,17 @@ func blobUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
UUID: getUploadUUID(ctx),
}
handler := http.Handler(handlers.MethodHandler{
"POST": mutableHandler(buh.StartBlobUpload, ctx),
handler := handlers.MethodHandler{
"GET": http.HandlerFunc(buh.GetUploadStatus),
"HEAD": http.HandlerFunc(buh.GetUploadStatus),
"PATCH": mutableHandler(buh.PatchBlobData, ctx),
"PUT": mutableHandler(buh.PutBlobUploadComplete, ctx),
"DELETE": mutableHandler(buh.CancelBlobUpload, ctx),
})
}
if !ctx.readOnly {
handler["POST"] = http.HandlerFunc(buh.StartBlobUpload)
handler["PATCH"] = http.HandlerFunc(buh.PatchBlobData)
handler["PUT"] = http.HandlerFunc(buh.PutBlobUploadComplete)
handler["DELETE"] = http.HandlerFunc(buh.CancelBlobUpload)
}
if buh.UUID != "" {
state, err := hmacKey(ctx.Config.HTTP.Secret).unpackUploadState(r.FormValue("_state"))
@ -93,7 +96,7 @@ func blobUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
}
}
handler = closeResources(handler, buh.Upload)
return closeResources(handler, buh.Upload)
}
return handler

View file

@ -7,7 +7,6 @@ import (
ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/registry/api/errcode"
"github.com/docker/distribution/registry/api/v2"
)
// closeResources closes all the provided resources after running the target
@ -61,16 +60,3 @@ func copyFullPayload(responseWriter http.ResponseWriter, r *http.Request, destWr
return nil
}
// mutableHandler wraps a http.HandlerFunc with a check that the registry is
// not in read-only mode. If it is in read-only mode, the wrapper returns
// v2.ErrorCodeMaintenanceMode to the client.
func mutableHandler(handler http.HandlerFunc, ctx *Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if ctx.App.readOnly {
ctx.Errors = append(ctx.Errors, v2.ErrorCodeMaintenanceMode)
return
}
handler(w, r)
}
}

View file

@ -32,11 +32,16 @@ func imageManifestDispatcher(ctx *Context, r *http.Request) http.Handler {
imageManifestHandler.Digest = dgst
}
return handlers.MethodHandler{
mhandler := handlers.MethodHandler{
"GET": http.HandlerFunc(imageManifestHandler.GetImageManifest),
"PUT": mutableHandler(imageManifestHandler.PutImageManifest, ctx),
"DELETE": mutableHandler(imageManifestHandler.DeleteImageManifest, ctx),
}
if !ctx.readOnly {
mhandler["PUT"] = http.HandlerFunc(imageManifestHandler.PutImageManifest)
mhandler["DELETE"] = http.HandlerFunc(imageManifestHandler.DeleteImageManifest)
}
return mhandler
}
// imageManifestHandler handles http operations on image manifests.